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 fgPlatformProcessWork ( 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 fgPlatformProcessWork ( 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 fgPlatformProcessWork(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 if (workMask & GLUT_DISPLAY_WORK)
437 if( window->State.Visible )
438 fghRedrawWindow ( window );
440 /* Strip out display work that might have ended up on work list now as some of the above genereates callbacks */
441 window->State.WorkMask &= ~GLUT_DISPLAY_WORK;
446 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
449 * Executes a single iteration in the freeglut processing loop.
451 void FGAPIENTRY glutMainLoopEvent( void )
454 fgPlatformProcessSingleEvent ();
456 if( fgState.Timers.First )
458 if (fgState.NumActiveJoysticks>0) /* If zero, don't poll joysticks */
459 fghCheckJoystickPolls( );
461 /* Perform work on the window (position, reshape, display, etc) */
468 * Enters the freeglut processing loop.
469 * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
471 void FGAPIENTRY glutMainLoop( void )
475 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoop" );
477 if (!fgStructure.Windows.First)
478 fgError(" ERROR: glutMainLoop called with no windows created.");
480 fgPlatformMainLoopPreliminaryWork ();
482 fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
483 while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
487 glutMainLoopEvent( );
489 * Step through the list of windows, seeing if there are any
492 for( window = ( SFG_Window * )fgStructure.Windows.First;
494 window = ( SFG_Window * )window->Node.Next )
495 if ( ! ( window->IsMenu ) )
499 fgState.ExecState = GLUT_EXEC_STATE_STOP;
502 if( fgState.IdleCallback )
504 if( fgStructure.CurrentWindow &&
505 fgStructure.CurrentWindow->IsMenu )
507 fgSetWindow( window );
508 fgState.IdleCallback( );
511 fghSleepForEvents( );
516 * When this loop terminates, destroy the display, state and structure
517 * of a freeglut session, so that another glutInit() call can happen
519 * Save the "ActionOnWindowClose" because "fgDeinitialize" resets it.
521 action = fgState.ActionOnWindowClose;
523 if( action == GLUT_ACTION_EXIT )
528 * Leaves the freeglut processing loop.
530 void FGAPIENTRY glutLeaveMainLoop( void )
532 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveMainLoop" );
533 fgState.ExecState = GLUT_EXEC_STATE_STOP ;
538 /*** END OF FILE ***/