4 * The windows message processing methods.
6 * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7 * Written by Pawel W. Olszta, <olszta@sourceforge.net>
8 * Creation date: Fri Dec 3 1999
10 * Permission is hereby granted, free of charge, to any person obtaining a
11 * copy of this software and associated documentation files (the "Software"),
12 * to deal in the Software without restriction, including without limitation
13 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 * and/or sell copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following conditions:
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
24 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 #include <GL/freeglut.h>
29 #include "fg_internal.h"
35 # define VFPRINTF(s,f,a) vfprintf((s),(f),(a))
36 #elif defined(HAVE__DOPRNT)
37 # define VFPRINTF(s,f,a) _doprnt((f),(a),(s))
39 # define VFPRINTF(s,f,a)
43 * Try to get the maximum value allowed for ints, falling back to the minimum
44 * guaranteed by ISO C99 if there is no suitable header.
50 # define INT_MAX 32767
54 # define MIN(a,b) (((a)<(b)) ? (a) : (b))
57 extern void fgProcessWork ( SFG_Window *window );
58 extern fg_time_t fgPlatformSystemTime ( void );
59 extern void fgPlatformSleepForEvents( fg_time_t msec );
60 extern void fgPlatformProcessSingleEvent ( void );
61 extern void fgPlatformMainLoopPreliminaryWork ( void );
63 extern void fgPlatformInitWork(SFG_Window* window);
64 extern void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask);
65 extern void fgPlatformVisibilityWork(SFG_Window* window);
68 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
70 void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify)
72 GLboolean notify = GL_FALSE;
74 if( width != window->State.Width ||
75 height != window->State.Height )
77 window->State.Width = width;
78 window->State.Height = height;
83 if (notify || forceNotify)
85 SFG_Window *saved_window = fgStructure.CurrentWindow;
87 INVOKE_WCB( *window, Reshape, ( width, height ) );
90 * Force a window redraw. In Windows at least this is only a partial
91 * solution: if the window is increasing in size in either dimension,
92 * the already-drawn part does not get drawn again and things look funny.
93 * But without this we get this bad behaviour whenever we resize the
95 * DN: Hmm.. the above sounds like a concern only in single buffered mode...
97 window->State.WorkMask |= GLUT_DISPLAY_WORK;
99 fgSetWindow( saved_window );
103 void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify)
105 GLboolean notify = GL_FALSE;
107 if( x != window->State.Xpos ||
108 y != window->State.Ypos )
110 window->State.Xpos = x;
111 window->State.Ypos = y;
116 if (notify || forceNotify)
118 SFG_Window *saved_window = fgStructure.CurrentWindow;
119 INVOKE_WCB( *window, Position, ( x, y ) );
120 fgSetWindow( saved_window );
125 * Calls a window's redraw method. This is used when
126 * a redraw is forced by the incoming window messages,
127 * or if a redisplay is otherwise pending.
128 * this is lean and mean without checks as it is
129 * currently only called from fghcbDisplayWindow which
130 * only calls this if the window is visible and needs
132 * Note that the fgSetWindow call on Windows makes the
133 * right device context current on windows, allowing
134 * direct drawing without BeginPaint/EndPaint in the
137 void fghRedrawWindow ( SFG_Window *window )
139 SFG_Window *current_window = fgStructure.CurrentWindow;
141 fgSetWindow( window );
142 INVOKE_WCB( *window, Display, ( ) );
144 fgSetWindow( current_window );
147 void fghRedrawWindowAndChildren ( SFG_Window *window )
151 fghRedrawWindow(window);
153 for( child = ( SFG_Window * )window->Children.First;
155 child = ( SFG_Window * )child->Node.Next )
157 fghRedrawWindowAndChildren(child);
162 static void fghcbProcessWork( SFG_Window *window,
163 SFG_Enumerator *enumerator )
165 if( window->State.WorkMask )
166 fgProcessWork ( window );
168 fgEnumSubWindows( window, fghcbProcessWork, enumerator );
172 * Make all windows process their work list
174 static void fghProcessWork( void )
176 SFG_Enumerator enumerator;
178 enumerator.found = GL_FALSE;
179 enumerator.data = NULL;
181 fgEnumWindows( fghcbProcessWork, &enumerator );
185 * Window enumerator callback to check for the joystick polling code
187 static void fghcbCheckJoystickPolls( SFG_Window *window,
188 SFG_Enumerator *enumerator )
192 if (window->State.JoystickPollRate > 0 && FETCH_WCB( *window, Joystick ))
194 /* This window has a joystick to be polled (if pollrate <= 0, user needs to poll manually with glutForceJoystickFunc */
195 checkTime= fgElapsedTime( );
197 if( window->State.JoystickLastPoll + window->State.JoystickPollRate <=
200 #if !defined(_WIN32_WCE)
201 fgJoystickPollWindow( window );
202 #endif /* !defined(_WIN32_WCE) */
203 window->State.JoystickLastPoll = checkTime;
207 fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );
211 * Check all windows for joystick polling
213 * The real way to do this is to make use of the glutTimer() API
214 * to more cleanly re-implement the joystick API. Then, this code
215 * and all other "joystick timer" code can be yanked.
217 static void fghCheckJoystickPolls( void )
219 SFG_Enumerator enumerator;
221 enumerator.found = GL_FALSE;
222 enumerator.data = NULL;
224 fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );
228 * Check the global timers
230 static void fghCheckTimers( void )
232 fg_time_t checkTime = fgElapsedTime( );
234 while( fgState.Timers.First )
236 SFG_Timer *timer = fgState.Timers.First;
238 if( timer->TriggerTime > checkTime )
239 /* Timers are sorted by triggerTime */
242 fgListRemove( &fgState.Timers, &timer->Node );
243 fgListAppend( &fgState.FreeTimers, &timer->Node );
245 timer->Callback( timer->ID );
250 /* Platform-dependent time in milliseconds, as an unsigned 64-bit integer.
251 * This doesn't overflow in any reasonable time, so no need to worry about
252 * that. The GLUT API return value will however overflow after 49.7 days,
253 * which means you will still get in trouble when running the
254 * application for more than 49.7 days.
256 fg_time_t fgSystemTime(void)
258 return fgPlatformSystemTime();
264 fg_time_t fgElapsedTime( void )
266 return fgSystemTime() - fgState.Time;
272 void fgError( const char *fmt, ... )
276 if (fgState.ErrorFunc) {
280 /* call user set error handler here */
281 fgState.ErrorFunc(fmt, ap);
286 #ifdef FREEGLUT_PRINT_ERRORS
289 fprintf( stderr, "freeglut ");
290 if( fgState.ProgramName )
291 fprintf( stderr, "(%s): ", fgState.ProgramName );
292 VFPRINTF( stderr, fmt, ap );
293 fprintf( stderr, "\n" );
298 if ( fgState.Initialised )
305 void fgWarning( const char *fmt, ... )
309 if (fgState.WarningFunc) {
313 /* call user set warning handler here */
314 fgState.WarningFunc(fmt, ap);
319 #ifdef FREEGLUT_PRINT_WARNINGS
322 fprintf( stderr, "freeglut ");
323 if( fgState.ProgramName )
324 fprintf( stderr, "(%s): ", fgState.ProgramName );
325 VFPRINTF( stderr, fmt, ap );
326 fprintf( stderr, "\n" );
335 * Indicates whether work is pending for ANY window.
337 * The current mechanism is to walk all of the windows and ask if
338 * work is pending. We have a short-circuit early return if we find any.
340 static void fghHavePendingWorkCallback( SFG_Window* w, SFG_Enumerator* e)
342 if( w->State.WorkMask )
348 fgEnumSubWindows( w, fghHavePendingWorkCallback, e );
350 static int fghHavePendingWork (void)
352 SFG_Enumerator enumerator;
354 enumerator.found = GL_FALSE;
355 enumerator.data = NULL;
356 fgEnumWindows( fghHavePendingWorkCallback, &enumerator );
357 return !!enumerator.data;
361 * Returns the number of GLUT ticks (milliseconds) till the next timer event.
363 static fg_time_t fghNextTimer( void )
365 fg_time_t currentTime;
366 SFG_Timer *timer = fgState.Timers.First; /* timers are sorted by trigger time, so only have to check the first */
371 currentTime = fgElapsedTime();
372 if( timer->TriggerTime < currentTime )
375 return timer->TriggerTime - currentTime;
378 static void fghSleepForEvents( void )
382 if( fghHavePendingWork( ) )
385 msec = fghNextTimer( );
386 /* XXX Should use GLUT timers for joysticks... */
387 /* XXX Dumb; forces granularity to .01sec */
388 if( fgState.NumActiveJoysticks>0 && ( msec > 10 ) )
391 fgPlatformSleepForEvents ( msec );
395 /* Step through the work list */
396 void fgProcessWork(SFG_Window *window)
398 unsigned int workMask = window->State.WorkMask;
399 /* Now clear it so that any callback generated by the actions below can set work again */
400 window->State.WorkMask = 0;
402 if (workMask&~GLUT_DISPLAY_WORK) /* Display work is the common case, skip all the below at once */
404 if (workMask & GLUT_INIT_WORK)
406 /* This is before the first display callback: if needed for the platform,
407 * call a few callbacks to inform user of window size, position, etc
409 fgPlatformInitWork(window);
411 /* Call init context callback */
412 INVOKE_WCB( *window, InitContext, ());
414 /* Lastly, check if we have a display callback, error out if not
415 * This is the right place to do it, as the redisplay will be
416 * next right after we exit this function, so there is no more
417 * opportunity for the user to register a callback for this window.
419 if (!FETCH_WCB(*window, Display))
420 fgError ( "ERROR: No display callback registered for window %d\n", window->ID );
423 /* On windows we can position, resize and change z order at the same time */
424 if (workMask & (GLUT_POSITION_WORK|GLUT_SIZE_WORK|GLUT_ZORDER_WORK|GLUT_FULL_SCREEN_WORK))
426 fgPlatformPosResZordWork(window,workMask);
429 if (workMask & GLUT_VISIBILITY_WORK)
431 fgPlatformVisibilityWork(window);
435 /* check window state's workmask as well as some of the above callbacks might have generated redisplay requests. We can deal with those right now instead of wait for the next mainloop iteration. */
436 if (workMask & GLUT_DISPLAY_WORK || window->State.WorkMask & GLUT_DISPLAY_WORK)
438 if( window->State.Visible )
440 /* Strip out display work from the work list */
441 /* NB: do this before the display callback is called as user might call postredisplay in his display callback */
442 window->State.WorkMask &= ~GLUT_DISPLAY_WORK;
444 fghRedrawWindow ( window );
450 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
453 * Executes a single iteration in the freeglut processing loop.
455 void FGAPIENTRY glutMainLoopEvent( void )
458 fgPlatformProcessSingleEvent ();
460 if( fgState.Timers.First )
462 if (fgState.NumActiveJoysticks>0) /* If zero, don't poll joysticks */
463 fghCheckJoystickPolls( );
465 /* Perform work on the window (position, reshape, display, etc) */
468 /* Check OpenGL error state if requested.
469 * Don't call if no more open windows (can happen if user closes window from
470 * title bar), would lead to infinite error loop in glutReportErrors
472 if (fgState.GLDebugSwitch && fgStructure.CurrentWindow)
479 * Enters the freeglut processing loop.
480 * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
482 void FGAPIENTRY glutMainLoop( void )
486 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoop" );
488 if (!fgStructure.Windows.First)
489 fgError(" ERROR: glutMainLoop called with no windows created.");
491 fgPlatformMainLoopPreliminaryWork ();
493 fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
494 while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
498 glutMainLoopEvent( );
500 * Step through the list of windows, seeing if there are any
503 for( window = ( SFG_Window * )fgStructure.Windows.First;
505 window = ( SFG_Window * )window->Node.Next )
506 if ( ! ( window->IsMenu ) )
510 fgState.ExecState = GLUT_EXEC_STATE_STOP;
513 if( fgState.IdleCallback )
515 if( fgStructure.CurrentWindow &&
516 fgStructure.CurrentWindow->IsMenu )
518 fgSetWindow( window );
519 fgState.IdleCallback( );
522 fghSleepForEvents( );
527 * When this loop terminates, destroy the display, state and structure
528 * of a freeglut session, so that another glutInit() call can happen
530 * Save the "ActionOnWindowClose" because "fgDeinitialize" resets it.
532 action = fgState.ActionOnWindowClose;
534 if( action == GLUT_ACTION_EXIT )
539 * Leaves the freeglut processing loop.
541 void FGAPIENTRY glutLeaveMainLoop( void )
543 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveMainLoop" );
544 fgState.ExecState = GLUT_EXEC_STATE_STOP ;
549 /*** END OF FILE ***/