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
91 * DN: Hmm.. the above sounds like a concern only in single buffered mode...
93 window->State.Redisplay = GL_TRUE;
96 fgSetWindow( current_window );
100 * Calls a window's redraw method. This is used when
101 * a redraw is forced by the incoming window messages.
103 void fghRedrawWindow ( SFG_Window *window )
105 SFG_Window *current_window = fgStructure.CurrentWindow;
106 printf("fghRedrawWindow\n");
108 freeglut_return_if_fail( window );
109 printf("got window\n");
111 if( window->State.NeedToInitContext ) {
112 INVOKE_WCB( *window, InitContext, ());
113 window->State.NeedToInitContext = GL_FALSE;
116 freeglut_return_if_fail( FETCH_WCB ( *window, Display ) );
117 printf("got displayCB\n");
119 window->State.Redisplay = GL_FALSE;
121 freeglut_return_if_fail( window->State.Visible );
122 printf("we're visible\n");
124 fgSetWindow( window );
126 if( window->State.NeedToResize )
128 /* Set need to resize to false before calling fghReshapeWindow, otherwise
129 in the case the user's reshape callback calls glutReshapeWindow,
130 his request would get canceled after fghReshapeWindow gets called.
132 window->State.NeedToResize = GL_FALSE;
141 printf("invoking displayCB\n");
142 INVOKE_WCB( *window, Display, ( ) );
144 fgSetWindow( current_window );
148 static void fghcbDisplayWindow( SFG_Window *window,
149 SFG_Enumerator *enumerator )
151 printf("window %p, %i\n",window,window->State.Redisplay);
152 if( window->State.Redisplay &&
153 window->State.Visible )
155 window->State.Redisplay = GL_FALSE;
156 printf("redisplaying...\n");
157 fgPlatformDisplayWindow ( window );
160 fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );
164 * Make all windows perform a display call
166 static void fghDisplayAll( void )
168 SFG_Enumerator enumerator;
170 enumerator.found = GL_FALSE;
171 enumerator.data = NULL;
173 fgEnumWindows( fghcbDisplayWindow, &enumerator );
177 * Window enumerator callback to check for the joystick polling code
179 static void fghcbCheckJoystickPolls( SFG_Window *window,
180 SFG_Enumerator *enumerator )
182 fg_time_t checkTime = fgElapsedTime( );
184 if( window->State.JoystickLastPoll + window->State.JoystickPollRate <=
187 #if !defined(_WIN32_WCE)
188 fgJoystickPollWindow( window );
189 #endif /* !defined(_WIN32_WCE) */
190 window->State.JoystickLastPoll = checkTime;
193 fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );
197 * Check all windows for joystick polling
199 static void fghCheckJoystickPolls( void )
201 SFG_Enumerator enumerator;
203 enumerator.found = GL_FALSE;
204 enumerator.data = NULL;
206 fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );
210 * Check the global timers
212 static void fghCheckTimers( void )
214 fg_time_t checkTime = fgElapsedTime( );
216 while( fgState.Timers.First )
218 SFG_Timer *timer = fgState.Timers.First;
220 if( timer->TriggerTime > checkTime )
223 fgListRemove( &fgState.Timers, &timer->Node );
224 fgListAppend( &fgState.FreeTimers, &timer->Node );
226 timer->Callback( timer->ID );
231 /* Platform-dependent time in milliseconds, as an unsigned 64-bit integer.
232 * This doesn't overflow in any reasonable time, so no need to worry about
233 * that. The GLUT API return value will however overflow after 49.7 days,
234 * and on Windows we (currently) do not have access to a 64-bit timestamp,
235 * which means internal time will still get in trouble when running the
236 * application for more than 49.7 days.
237 * This value wraps every 49.7 days, but integer overflows cancel
238 * when subtracting an initial start time, unless the total time exceeds
239 * 32-bit, where the GLUT API return value is also overflowed.
241 fg_time_t fgSystemTime(void)
243 return fgPlatformSystemTime();
249 fg_time_t fgElapsedTime( void )
251 return fgSystemTime() - fgState.Time;
257 void fgError( const char *fmt, ... )
261 if (fgState.ErrorFunc) {
265 /* call user set error handler here */
266 fgState.ErrorFunc(fmt, ap);
274 fprintf( stderr, "freeglut ");
275 if( fgState.ProgramName )
276 fprintf( stderr, "(%s): ", fgState.ProgramName );
277 VFPRINTF( stderr, fmt, ap );
278 fprintf( stderr, "\n" );
282 if ( fgState.Initialised )
289 void fgWarning( const char *fmt, ... )
293 if (fgState.WarningFunc) {
297 /* call user set warning handler here */
298 fgState.WarningFunc(fmt, ap);
306 fprintf( stderr, "freeglut ");
307 if( fgState.ProgramName )
308 fprintf( stderr, "(%s): ", fgState.ProgramName );
309 VFPRINTF( stderr, fmt, ap );
310 fprintf( stderr, "\n" );
318 * Indicates whether Joystick events are being used by ANY window.
320 * The current mechanism is to walk all of the windows and ask if
321 * there is a joystick callback. We have a short-circuit early
322 * return if we find any joystick handler registered.
324 * The real way to do this is to make use of the glutTimer() API
325 * to more cleanly re-implement the joystick API. Then, this code
326 * and all other "joystick timer" code can be yanked.
329 static void fghCheckJoystickCallback( SFG_Window* w, SFG_Enumerator* e)
331 if( FETCH_WCB( *w, Joystick ) )
336 fgEnumSubWindows( w, fghCheckJoystickCallback, e );
338 static int fghHaveJoystick( void )
340 SFG_Enumerator enumerator;
342 enumerator.found = GL_FALSE;
343 enumerator.data = NULL;
344 fgEnumWindows( fghCheckJoystickCallback, &enumerator );
345 return !!enumerator.data;
347 static void fghHavePendingRedisplaysCallback( SFG_Window* w, SFG_Enumerator* e)
349 if( w->State.Redisplay && w->State.Visible )
354 fgEnumSubWindows( w, fghHavePendingRedisplaysCallback, e );
356 static int fghHavePendingRedisplays (void)
358 SFG_Enumerator enumerator;
360 enumerator.found = GL_FALSE;
361 enumerator.data = NULL;
362 fgEnumWindows( fghHavePendingRedisplaysCallback, &enumerator );
363 return !!enumerator.data;
366 * Returns the number of GLUT ticks (milliseconds) till the next timer event.
368 static fg_time_t fghNextTimer( void )
370 fg_time_t currentTime = fgElapsedTime();
371 SFG_Timer *timer = fgState.Timers.First;
376 if( timer->TriggerTime < currentTime )
379 return timer->TriggerTime - currentTime;
382 static void fghSleepForEvents( void )
386 if( fgState.IdleCallback || fghHavePendingRedisplays( ) )
389 msec = fghNextTimer( );
390 /* XXX Use GLUT timers for joysticks... */
391 /* XXX Dumb; forces granularity to .01sec */
392 if( fghHaveJoystick( ) && ( msec > 10 ) )
395 fgPlatformSleepForEvents ( msec );
399 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
402 * Executes a single iteration in the freeglut processing loop.
404 void FGAPIENTRY glutMainLoopEvent( void )
406 fgPlatformProcessSingleEvent ();
408 if( fgState.Timers.First )
410 fghCheckJoystickPolls( );
417 * Enters the freeglut processing loop.
418 * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
420 void FGAPIENTRY glutMainLoop( void )
424 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoop" );
426 fgPlatformMainLoopPreliminaryWork ();
428 fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
429 while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
433 glutMainLoopEvent( );
435 * Step through the list of windows, seeing if there are any
438 for( window = ( SFG_Window * )fgStructure.Windows.First;
440 window = ( SFG_Window * )window->Node.Next )
441 if ( ! ( window->IsMenu ) )
445 fgState.ExecState = GLUT_EXEC_STATE_STOP;
448 if( fgState.IdleCallback )
450 if( fgStructure.CurrentWindow &&
451 fgStructure.CurrentWindow->IsMenu )
453 fgSetWindow( window );
454 fgState.IdleCallback( );
457 fghSleepForEvents( );
462 * When this loop terminates, destroy the display, state and structure
463 * of a freeglut session, so that another glutInit() call can happen
465 * Save the "ActionOnWindowClose" because "fgDeinitialize" resets it.
467 action = fgState.ActionOnWindowClose;
469 if( action == GLUT_ACTION_EXIT )
474 * Leaves the freeglut processing loop.
476 void FGAPIENTRY glutLeaveMainLoop( void )
478 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveMainLoop" );
479 fgState.ExecState = GLUT_EXEC_STATE_STOP ;
484 /*** END OF FILE ***/