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"
36 * Try to get the maximum value allowed for ints, falling back to the minimum
37 * guaranteed by ISO C99 if there is no suitable header.
43 # define INT_MAX 32767
47 # define MIN(a,b) (((a)<(b)) ? (a) : (b))
50 extern void fgProcessWork ( SFG_Window *window );
51 extern fg_time_t fgPlatformSystemTime ( void );
52 extern void fgPlatformSleepForEvents( fg_time_t msec );
53 extern void fgPlatformProcessSingleEvent ( void );
54 extern void fgPlatformMainLoopPreliminaryWork ( void );
56 extern void fgPlatformInitWork(SFG_Window* window);
57 extern void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask);
58 extern void fgPlatformVisibilityWork(SFG_Window* window);
61 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
63 void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify)
65 GLboolean notify = GL_FALSE;
67 if( width != window->State.Width ||
68 height != window->State.Height )
70 window->State.Width = width;
71 window->State.Height = height;
76 if (notify || forceNotify)
78 SFG_Window *saved_window = fgStructure.CurrentWindow;
80 INVOKE_WCB( *window, Reshape, ( width, height ) );
83 * Force a window redraw. In Windows at least this is only a partial
84 * solution: if the window is increasing in size in either dimension,
85 * the already-drawn part does not get drawn again and things look funny.
86 * But without this we get this bad behaviour whenever we resize the
88 * DN: Hmm.. the above sounds like a concern only in single buffered mode...
90 window->State.WorkMask |= GLUT_DISPLAY_WORK;
92 fgSetWindow( saved_window );
96 void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify)
98 GLboolean notify = GL_FALSE;
100 if( x != window->State.Xpos ||
101 y != window->State.Ypos )
103 window->State.Xpos = x;
104 window->State.Ypos = y;
109 if (notify || forceNotify)
111 SFG_Window *saved_window = fgStructure.CurrentWindow;
112 INVOKE_WCB( *window, Position, ( x, y ) );
113 fgSetWindow( saved_window );
118 * Calls a window's redraw method. This is used when
119 * a redraw is forced by the incoming window messages,
120 * or if a redisplay is otherwise pending.
121 * this is lean and mean without checks as it is
122 * currently only called from fghcbDisplayWindow which
123 * only calls this if the window is visible and needs
125 * Note that the fgSetWindow call on Windows makes the
126 * right device context current on windows, allowing
127 * direct drawing without BeginPaint/EndPaint in the
130 void fghRedrawWindow ( SFG_Window *window )
132 SFG_Window *current_window = fgStructure.CurrentWindow;
134 fgSetWindow( window );
135 INVOKE_WCB( *window, Display, ( ) );
137 fgSetWindow( current_window );
140 void fghRedrawWindowAndChildren ( SFG_Window *window )
144 fghRedrawWindow(window);
146 for( child = ( SFG_Window * )window->Children.First;
148 child = ( SFG_Window * )child->Node.Next )
150 fghRedrawWindowAndChildren(child);
155 static void fghcbProcessWork( SFG_Window *window,
156 SFG_Enumerator *enumerator )
158 if( window->State.WorkMask )
159 fgProcessWork ( window );
161 fgEnumSubWindows( window, fghcbProcessWork, enumerator );
165 * Make all windows process their work list
167 static void fghProcessWork( void )
169 SFG_Enumerator enumerator;
171 enumerator.found = GL_FALSE;
172 enumerator.data = NULL;
174 fgEnumWindows( fghcbProcessWork, &enumerator );
178 * Window enumerator callback to check for the joystick polling code
180 static void fghcbCheckJoystickPolls( SFG_Window *window,
181 SFG_Enumerator *enumerator )
185 if (window->State.JoystickPollRate > 0 && FETCH_WCB( *window, Joystick ))
187 /* This window has a joystick to be polled (if pollrate <= 0, user needs to poll manually with glutForceJoystickFunc */
188 checkTime= fgElapsedTime( );
190 if( window->State.JoystickLastPoll + window->State.JoystickPollRate <=
193 #if !defined(_WIN32_WCE)
194 fgJoystickPollWindow( window );
195 #endif /* !defined(_WIN32_WCE) */
196 window->State.JoystickLastPoll = checkTime;
200 fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );
204 * Check all windows for joystick polling
206 * The real way to do this is to make use of the glutTimer() API
207 * to more cleanly re-implement the joystick API. Then, this code
208 * and all other "joystick timer" code can be yanked.
210 static void fghCheckJoystickPolls( void )
212 SFG_Enumerator enumerator;
214 enumerator.found = GL_FALSE;
215 enumerator.data = NULL;
217 fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );
221 * Check the global timers
223 static void fghCheckTimers( void )
225 fg_time_t checkTime = fgElapsedTime( );
227 while( fgState.Timers.First )
229 SFG_Timer *timer = fgState.Timers.First;
231 if( timer->TriggerTime > checkTime )
232 /* Timers are sorted by triggerTime */
235 fgListRemove( &fgState.Timers, &timer->Node );
236 fgListAppend( &fgState.FreeTimers, &timer->Node );
238 timer->Callback( timer->ID );
243 /* Platform-dependent time in milliseconds, as an unsigned 64-bit integer.
244 * This doesn't overflow in any reasonable time, so no need to worry about
245 * that. The GLUT API return value will however overflow after 49.7 days,
246 * which means you will still get in trouble when running the
247 * application for more than 49.7 days.
249 fg_time_t fgSystemTime(void)
251 return fgPlatformSystemTime();
257 fg_time_t fgElapsedTime( void )
259 return fgSystemTime() - fgState.Time;
265 void fgError( const char *fmt, ... )
269 if (fgState.ErrorFunc) {
273 /* call user set error handler here */
274 fgState.ErrorFunc(fmt, ap);
279 #ifdef FREEGLUT_PRINT_ERRORS
282 fprintf( stderr, "freeglut ");
283 if( fgState.ProgramName )
284 fprintf( stderr, "(%s): ", fgState.ProgramName );
285 vfprintf( stderr, fmt, ap );
286 fprintf( stderr, "\n" );
291 if ( fgState.Initialised )
298 void fgWarning( const char *fmt, ... )
302 if (fgState.WarningFunc) {
306 /* call user set warning handler here */
307 fgState.WarningFunc(fmt, ap);
312 #ifdef FREEGLUT_PRINT_WARNINGS
315 fprintf( stderr, "freeglut ");
316 if( fgState.ProgramName )
317 fprintf( stderr, "(%s): ", fgState.ProgramName );
318 vfprintf( stderr, fmt, ap );
319 fprintf( stderr, "\n" );
328 * Indicates whether work is pending for ANY window.
330 * The current mechanism is to walk all of the windows and ask if
331 * work is pending. We have a short-circuit early return if we find any.
333 static void fghHavePendingWorkCallback( SFG_Window* w, SFG_Enumerator* e)
335 if( w->State.WorkMask )
341 fgEnumSubWindows( w, fghHavePendingWorkCallback, e );
343 static int fghHavePendingWork (void)
345 SFG_Enumerator enumerator;
347 enumerator.found = GL_FALSE;
348 enumerator.data = NULL;
349 fgEnumWindows( fghHavePendingWorkCallback, &enumerator );
350 return !!enumerator.data;
354 * Returns the number of GLUT ticks (milliseconds) till the next timer event.
356 static fg_time_t fghNextTimer( void )
358 fg_time_t currentTime;
359 SFG_Timer *timer = fgState.Timers.First; /* timers are sorted by trigger time, so only have to check the first */
364 currentTime = fgElapsedTime();
365 if( timer->TriggerTime < currentTime )
368 return timer->TriggerTime - currentTime;
371 static void fghSleepForEvents( void )
375 if( fghHavePendingWork( ) )
378 msec = fghNextTimer( );
379 /* XXX Should use GLUT timers for joysticks... */
380 /* XXX Dumb; forces granularity to .01sec */
381 if( fgState.NumActiveJoysticks>0 && ( msec > 10 ) )
384 fgPlatformSleepForEvents ( msec );
388 /* Step through the work list */
389 void fgProcessWork(SFG_Window *window)
391 unsigned int workMask = window->State.WorkMask;
392 /* Now clear it so that any callback generated by the actions below can set work again */
393 window->State.WorkMask = 0;
395 if (workMask&~GLUT_DISPLAY_WORK) /* Display work is the common case, skip all the below at once */
397 if (workMask & GLUT_INIT_WORK)
399 /* This is before the first display callback: if needed for the platform,
400 * call a few callbacks to inform user of window size, position, etc
402 fgPlatformInitWork(window);
404 /* Call init context callback */
405 INVOKE_WCB( *window, InitContext, ());
407 /* Lastly, check if we have a display callback, error out if not
408 * This is the right place to do it, as the redisplay will be
409 * next right after we exit this function, so there is no more
410 * opportunity for the user to register a callback for this window.
412 if (!FETCH_WCB(*window, Display))
413 fgError ( "ERROR: No display callback registered for window %d\n", window->ID );
416 /* On windows we can position, resize and change z order at the same time */
417 if (workMask & (GLUT_POSITION_WORK|GLUT_SIZE_WORK|GLUT_ZORDER_WORK|GLUT_FULL_SCREEN_WORK))
419 fgPlatformPosResZordWork(window,workMask);
422 if (workMask & GLUT_VISIBILITY_WORK)
424 fgPlatformVisibilityWork(window);
428 /* 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. */
429 if (workMask & GLUT_DISPLAY_WORK || window->State.WorkMask & GLUT_DISPLAY_WORK)
431 if( window->State.Visible )
433 /* Strip out display work from the work list */
434 /* NB: do this before the display callback is called as user might call postredisplay in his display callback */
435 window->State.WorkMask &= ~GLUT_DISPLAY_WORK;
437 fghRedrawWindow ( window );
443 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
446 * Executes a single iteration in the freeglut processing loop.
448 void FGAPIENTRY glutMainLoopEvent( void )
451 fgPlatformProcessSingleEvent ();
453 if( fgState.Timers.First )
455 if (fgState.NumActiveJoysticks>0) /* If zero, don't poll joysticks */
456 fghCheckJoystickPolls( );
458 /* Perform work on the window (position, reshape, display, etc) */
461 /* Check OpenGL error state if requested.
462 * Don't call if no more open windows (can happen if user closes window from
463 * title bar), would lead to infinite error loop in glutReportErrors
465 if (fgState.GLDebugSwitch && fgStructure.CurrentWindow)
472 * Enters the freeglut processing loop.
473 * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
475 void FGAPIENTRY glutMainLoop( void )
479 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoop" );
481 if (!fgStructure.Windows.First)
482 fgError(" ERROR: glutMainLoop called with no windows created.");
484 fgPlatformMainLoopPreliminaryWork ();
486 fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
487 while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
491 glutMainLoopEvent( );
493 * Step through the list of windows, seeing if there are any
496 for( window = ( SFG_Window * )fgStructure.Windows.First;
498 window = ( SFG_Window * )window->Node.Next )
499 if ( ! ( window->IsMenu ) )
503 fgState.ExecState = GLUT_EXEC_STATE_STOP;
506 if( fgState.IdleCallback )
508 if( fgStructure.CurrentWindow &&
509 fgStructure.CurrentWindow->IsMenu )
511 fgSetWindow( window );
512 fgState.IdleCallback( );
515 fghSleepForEvents( );
520 * When this loop terminates, destroy the display, state and structure
521 * of a freeglut session, so that another glutInit() call can happen
523 * Save the "ActionOnWindowClose" because "fgDeinitialize" resets it.
525 action = fgState.ActionOnWindowClose;
527 if( action == GLUT_ACTION_EXIT )
532 * Leaves the freeglut processing loop.
534 void FGAPIENTRY glutLeaveMainLoop( void )
536 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveMainLoop" );
537 fgState.ExecState = GLUT_EXEC_STATE_STOP ;
542 /*** END OF FILE ***/