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 fgPlatformReshapeWindow ( SFG_Window *window, int width, int height );
58 extern void fgPlatformDisplayWindow ( SFG_Window *window );
59 extern fg_time_t fgPlatformSystemTime ( void );
60 extern void fgPlatformSleepForEvents( fg_time_t msec );
61 extern void fgPlatformProcessSingleEvent ( void );
62 extern void fgPlatformMainLoopPreliminaryWork ( void );
67 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
69 static void fghReshapeWindow ( SFG_Window *window, int width, int height )
71 SFG_Window *current_window = fgStructure.CurrentWindow;
73 freeglut_return_if_fail( window != NULL );
75 fgPlatformReshapeWindow ( window, width, height );
77 if( FETCH_WCB( *window, Reshape ) )
78 INVOKE_WCB( *window, Reshape, ( width, height ) );
81 fgSetWindow( window );
82 glViewport( 0, 0, width, height );
86 * Force a window redraw. In Windows at least this is only a partial
87 * solution: if the window is increasing in size in either dimension,
88 * the already-drawn part does not get drawn again and things look funny.
89 * But without this we get this bad behaviour whenever we resize the
92 window->State.Redisplay = GL_TRUE;
95 fgSetWindow( current_window );
99 * Calls a window's redraw method. This is used when
100 * a redraw is forced by the incoming window messages.
102 void fghRedrawWindow ( SFG_Window *window )
104 SFG_Window *current_window = fgStructure.CurrentWindow;
106 freeglut_return_if_fail( window );
108 if( window->State.NeedToFixMyNameInitContext ) {
109 INVOKE_WCB( *window, FixMyNameInitContext, ());
110 window->State.NeedToFixMyNameInitContext = GL_FALSE;
113 freeglut_return_if_fail( FETCH_WCB ( *window, Display ) );
115 window->State.Redisplay = GL_FALSE;
117 freeglut_return_if_fail( window->State.Visible );
119 fgSetWindow( window );
121 if( window->State.NeedToResize )
129 window->State.NeedToResize = GL_FALSE;
132 INVOKE_WCB( *window, Display, ( ) );
134 fgSetWindow( current_window );
138 static void fghcbDisplayWindow( SFG_Window *window,
139 SFG_Enumerator *enumerator )
141 if( window->State.Redisplay &&
142 window->State.Visible )
144 window->State.Redisplay = GL_FALSE;
145 fgPlatformDisplayWindow ( window );
148 fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );
152 * Make all windows perform a display call
154 static void fghDisplayAll( void )
156 SFG_Enumerator enumerator;
158 enumerator.found = GL_FALSE;
159 enumerator.data = NULL;
161 fgEnumWindows( fghcbDisplayWindow, &enumerator );
165 * Window enumerator callback to check for the joystick polling code
167 static void fghcbCheckJoystickPolls( SFG_Window *window,
168 SFG_Enumerator *enumerator )
170 fg_time_t checkTime = fgElapsedTime( );
172 if( window->State.JoystickLastPoll + window->State.JoystickPollRate <=
175 #if !defined(_WIN32_WCE)
176 fgJoystickPollWindow( window );
177 #endif /* !defined(_WIN32_WCE) */
178 window->State.JoystickLastPoll = checkTime;
181 fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );
185 * Check all windows for joystick polling
187 static void fghCheckJoystickPolls( void )
189 SFG_Enumerator enumerator;
191 enumerator.found = GL_FALSE;
192 enumerator.data = NULL;
194 fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );
198 * Check the global timers
200 static void fghCheckTimers( void )
202 fg_time_t checkTime = fgElapsedTime( );
204 while( fgState.Timers.First )
206 SFG_Timer *timer = fgState.Timers.First;
208 if( timer->TriggerTime > checkTime )
211 fgListRemove( &fgState.Timers, &timer->Node );
212 fgListAppend( &fgState.FreeTimers, &timer->Node );
214 timer->Callback( timer->ID );
219 /* Platform-dependent time in milliseconds, as an unsigned 64-bit integer.
220 * This doesn't overflow in any reasonable time, so no need to worry about
221 * that. The GLUT API return value will however overflow after 49.7 days,
222 * and on Windows we (currently) do not have access to a 64-bit timestamp,
223 * which means internal time will still get in trouble when running the
224 * application for more than 49.7 days.
225 * This value wraps every 49.7 days, but integer overflows cancel
226 * when subtracting an initial start time, unless the total time exceeds
227 * 32-bit, where the GLUT API return value is also overflowed.
229 fg_time_t fgSystemTime(void)
231 return fgPlatformSystemTime();
237 fg_time_t fgElapsedTime( void )
239 return fgSystemTime() - fgState.Time;
245 void fgError( const char *fmt, ... )
249 if (fgState.ErrorFunc) {
253 /* call user set error handler here */
254 fgState.ErrorFunc(fmt, ap);
262 fprintf( stderr, "freeglut ");
263 if( fgState.ProgramName )
264 fprintf( stderr, "(%s): ", fgState.ProgramName );
265 VFPRINTF( stderr, fmt, ap );
266 fprintf( stderr, "\n" );
270 if ( fgState.Initialised )
277 void fgWarning( const char *fmt, ... )
281 if (fgState.WarningFunc) {
285 /* call user set warning handler here */
286 fgState.WarningFunc(fmt, ap);
294 fprintf( stderr, "freeglut ");
295 if( fgState.ProgramName )
296 fprintf( stderr, "(%s): ", fgState.ProgramName );
297 VFPRINTF( stderr, fmt, ap );
298 fprintf( stderr, "\n" );
306 * Indicates whether Joystick events are being used by ANY window.
308 * The current mechanism is to walk all of the windows and ask if
309 * there is a joystick callback. We have a short-circuit early
310 * return if we find any joystick handler registered.
312 * The real way to do this is to make use of the glutTimer() API
313 * to more cleanly re-implement the joystick API. Then, this code
314 * and all other "joystick timer" code can be yanked.
317 static void fghCheckJoystickCallback( SFG_Window* w, SFG_Enumerator* e)
319 if( FETCH_WCB( *w, Joystick ) )
324 fgEnumSubWindows( w, fghCheckJoystickCallback, e );
326 static int fghHaveJoystick( void )
328 SFG_Enumerator enumerator;
330 enumerator.found = GL_FALSE;
331 enumerator.data = NULL;
332 fgEnumWindows( fghCheckJoystickCallback, &enumerator );
333 return !!enumerator.data;
335 static void fghHavePendingRedisplaysCallback( SFG_Window* w, SFG_Enumerator* e)
337 if( w->State.Redisplay && w->State.Visible )
342 fgEnumSubWindows( w, fghHavePendingRedisplaysCallback, e );
344 static int fghHavePendingRedisplays (void)
346 SFG_Enumerator enumerator;
348 enumerator.found = GL_FALSE;
349 enumerator.data = NULL;
350 fgEnumWindows( fghHavePendingRedisplaysCallback, &enumerator );
351 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 = fgElapsedTime();
359 SFG_Timer *timer = fgState.Timers.First;
364 if( timer->TriggerTime < currentTime )
367 return timer->TriggerTime - currentTime;
370 static void fghSleepForEvents( void )
374 if( fgState.IdleCallback || fghHavePendingRedisplays( ) )
377 msec = fghNextTimer( );
378 /* XXX Use GLUT timers for joysticks... */
379 /* XXX Dumb; forces granularity to .01sec */
380 if( fghHaveJoystick( ) && ( msec > 10 ) )
383 fgPlatformSleepForEvents ( msec );
387 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
390 * Executes a single iteration in the freeglut processing loop.
392 void FGAPIENTRY glutMainLoopEvent( void )
394 fgPlatformProcessSingleEvent ();
396 if( fgState.Timers.First )
398 fghCheckJoystickPolls( );
405 * Enters the freeglut processing loop.
406 * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
408 void FGAPIENTRY glutMainLoop( void )
412 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoop" );
414 fgPlatformMainLoopPreliminaryWork ();
416 fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
417 while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
421 glutMainLoopEvent( );
423 * Step through the list of windows, seeing if there are any
426 for( window = ( SFG_Window * )fgStructure.Windows.First;
428 window = ( SFG_Window * )window->Node.Next )
429 if ( ! ( window->IsMenu ) )
433 fgState.ExecState = GLUT_EXEC_STATE_STOP;
436 if( fgState.IdleCallback )
438 if( fgStructure.CurrentWindow &&
439 fgStructure.CurrentWindow->IsMenu )
441 fgSetWindow( window );
442 fgState.IdleCallback( );
445 fghSleepForEvents( );
450 * When this loop terminates, destroy the display, state and structure
451 * of a freeglut session, so that another glutInit() call can happen
453 * Save the "ActionOnWindowClose" because "fgDeinitialize" resets it.
455 action = fgState.ActionOnWindowClose;
457 if( action == GLUT_ACTION_EXIT )
462 * Leaves the freeglut processing loop.
464 void FGAPIENTRY glutLeaveMainLoop( void )
466 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveMainLoop" );
467 fgState.ExecState = GLUT_EXEC_STATE_STOP ;
472 /*** END OF FILE ***/