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 );
78 * Force a window redraw. In Windows at least this is only a partial
79 * solution: if the window is increasing in size in either dimension,
80 * the already-drawn part does not get drawn again and things look funny.
81 * But without this we get this bad behaviour whenever we resize the
83 * DN: Hmm.. the above sounds like a concern only in single buffered mode...
85 window->State.Redisplay = GL_TRUE;
88 fgSetWindow( current_window );
92 * Calls a window's redraw method. This is used when
93 * a redraw is forced by the incoming window messages.
95 void fghRedrawWindow ( SFG_Window *window )
97 SFG_Window *current_window = fgStructure.CurrentWindow;
99 freeglut_return_if_fail( window );
101 if( window->State.NeedToInitContext ) {
102 INVOKE_WCB( *window, InitContext, ());
103 window->State.NeedToInitContext = GL_FALSE;
106 freeglut_return_if_fail( FETCH_WCB ( *window, Display ) );
108 window->State.Redisplay = GL_FALSE;
110 freeglut_return_if_fail( window->State.Visible );
112 fgSetWindow( window );
114 if( window->State.NeedToResize )
116 /* Set need to resize to false before calling fghReshapeWindow, otherwise
117 in the case the user's reshape callback calls glutReshapeWindow,
118 his request would get canceled after fghReshapeWindow gets called.
120 window->State.NeedToResize = GL_FALSE;
129 INVOKE_WCB( *window, Display, ( ) );
131 fgSetWindow( current_window );
135 static void fghcbDisplayWindow( SFG_Window *window,
136 SFG_Enumerator *enumerator )
138 if( window->State.Redisplay &&
139 window->State.Visible )
141 window->State.Redisplay = GL_FALSE;
142 fgPlatformDisplayWindow ( window );
145 fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );
149 * Make all windows perform a display call
151 static void fghDisplayAll( void )
153 SFG_Enumerator enumerator;
155 enumerator.found = GL_FALSE;
156 enumerator.data = NULL;
158 fgEnumWindows( fghcbDisplayWindow, &enumerator );
162 * Window enumerator callback to check for the joystick polling code
164 static void fghcbCheckJoystickPolls( SFG_Window *window,
165 SFG_Enumerator *enumerator )
169 if (window->State.JoystickPollRate > 0 && FETCH_WCB( *window, Joystick ))
171 /* This window has a joystick to be polled (if pollrate <= 0, user needs to poll manually with glutForceJoystickFunc */
172 checkTime= fgElapsedTime( );
174 if( window->State.JoystickLastPoll + window->State.JoystickPollRate <=
177 #if !defined(_WIN32_WCE)
178 fgJoystickPollWindow( window );
179 #endif /* !defined(_WIN32_WCE) */
180 window->State.JoystickLastPoll = checkTime;
184 fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );
188 * Check all windows for joystick polling
190 * The real way to do this is to make use of the glutTimer() API
191 * to more cleanly re-implement the joystick API. Then, this code
192 * and all other "joystick timer" code can be yanked.
194 static void fghCheckJoystickPolls( void )
196 SFG_Enumerator enumerator;
198 enumerator.found = GL_FALSE;
199 enumerator.data = NULL;
201 fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );
205 * Check the global timers
207 static void fghCheckTimers( void )
209 fg_time_t checkTime = fgElapsedTime( );
211 while( fgState.Timers.First )
213 SFG_Timer *timer = fgState.Timers.First;
215 if( timer->TriggerTime > checkTime )
216 /* XXX: are timers always sorted by triggerTime? If not, this and fghNextTimer are wrong */
219 fgListRemove( &fgState.Timers, &timer->Node );
220 fgListAppend( &fgState.FreeTimers, &timer->Node );
222 timer->Callback( timer->ID );
227 /* Platform-dependent time in milliseconds, as an unsigned 64-bit integer.
228 * This doesn't overflow in any reasonable time, so no need to worry about
229 * that. The GLUT API return value will however overflow after 49.7 days,
230 * which means you will still get in trouble when running the
231 * application for more than 49.7 days.
233 fg_time_t fgSystemTime(void)
235 return fgPlatformSystemTime();
241 fg_time_t fgElapsedTime( void )
243 return fgSystemTime() - fgState.Time;
249 void fgError( const char *fmt, ... )
253 if (fgState.ErrorFunc) {
257 /* call user set error handler here */
258 fgState.ErrorFunc(fmt, ap);
266 fprintf( stderr, "freeglut ");
267 if( fgState.ProgramName )
268 fprintf( stderr, "(%s): ", fgState.ProgramName );
269 VFPRINTF( stderr, fmt, ap );
270 fprintf( stderr, "\n" );
275 if ( fgState.Initialised )
282 void fgWarning( const char *fmt, ... )
286 if (fgState.WarningFunc) {
290 /* call user set warning handler here */
291 fgState.WarningFunc(fmt, ap);
296 #if FREEGLUT_WARNINGS
299 fprintf( stderr, "freeglut ");
300 if( fgState.ProgramName )
301 fprintf( stderr, "(%s): ", fgState.ProgramName );
302 VFPRINTF( stderr, fmt, ap );
303 fprintf( stderr, "\n" );
312 * Indicates whether a redisplay is pending for ANY window.
314 * The current mechanism is to walk all of the windows and ask if
315 * a redisplay is pending. We have a short-circuit early
316 * return if we find any.
318 static void fghHavePendingRedisplaysCallback( SFG_Window* w, SFG_Enumerator* e)
320 if( w->State.Redisplay && w->State.Visible )
326 fgEnumSubWindows( w, fghHavePendingRedisplaysCallback, e );
328 static int fghHavePendingRedisplays (void)
330 SFG_Enumerator enumerator;
332 enumerator.found = GL_FALSE;
333 enumerator.data = NULL;
334 fgEnumWindows( fghHavePendingRedisplaysCallback, &enumerator );
335 return !!enumerator.data;
339 * Returns the number of GLUT ticks (milliseconds) till the next timer event.
341 static fg_time_t fghNextTimer( void )
343 fg_time_t currentTime = fgElapsedTime();
344 SFG_Timer *timer = fgState.Timers.First;
349 if( timer->TriggerTime < currentTime )
352 return timer->TriggerTime - currentTime;
355 static void fghSleepForEvents( void )
359 if( fghHavePendingRedisplays( ) )
362 msec = fghNextTimer( );
363 /* XXX Should use GLUT timers for joysticks... */
364 /* XXX Dumb; forces granularity to .01sec */
365 if( fgState.NumActiveJoysticks>0 && ( msec > 10 ) )
368 fgPlatformSleepForEvents ( msec );
372 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
375 * Executes a single iteration in the freeglut processing loop.
377 void FGAPIENTRY glutMainLoopEvent( void )
379 fgPlatformProcessSingleEvent ();
381 if( fgState.Timers.First )
383 if (fgState.NumActiveJoysticks>0) /* If zero, don't poll joysticks */
384 fghCheckJoystickPolls( );
391 * Enters the freeglut processing loop.
392 * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
394 void FGAPIENTRY glutMainLoop( void )
398 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoop" );
400 if (!fgStructure.Windows.First)
401 fgError(" ERROR: glutMainLoop called with no windows created.");
403 fgPlatformMainLoopPreliminaryWork ();
405 fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
406 while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
410 glutMainLoopEvent( );
412 * Step through the list of windows, seeing if there are any
415 for( window = ( SFG_Window * )fgStructure.Windows.First;
417 window = ( SFG_Window * )window->Node.Next )
418 if ( ! ( window->IsMenu ) )
422 fgState.ExecState = GLUT_EXEC_STATE_STOP;
425 if( fgState.IdleCallback )
427 if( fgStructure.CurrentWindow &&
428 fgStructure.CurrentWindow->IsMenu )
430 fgSetWindow( window );
431 fgState.IdleCallback( );
434 fghSleepForEvents( );
439 * When this loop terminates, destroy the display, state and structure
440 * of a freeglut session, so that another glutInit() call can happen
442 * Save the "ActionOnWindowClose" because "fgDeinitialize" resets it.
444 action = fgState.ActionOnWindowClose;
446 if( action == GLUT_ACTION_EXIT )
451 * Leaves the freeglut processing loop.
453 void FGAPIENTRY glutLeaveMainLoop( void )
455 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveMainLoop" );
456 fgState.ExecState = GLUT_EXEC_STATE_STOP ;
461 /*** END OF FILE ***/