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 INVOKE_WCB( *window, Reshape, ( width, height ) );
80 * Force a window redraw. In Windows at least this is only a partial
81 * solution: if the window is increasing in size in either dimension,
82 * the already-drawn part does not get drawn again and things look funny.
83 * But without this we get this bad behaviour whenever we resize the
85 * DN: Hmm.. the above sounds like a concern only in single buffered mode...
87 window->State.Redisplay = GL_TRUE;
90 fgSetWindow( current_window );
94 * Calls a window's redraw method. This is used when
95 * a redraw is forced by the incoming window messages.
97 void fghRedrawWindow ( SFG_Window *window )
99 SFG_Window *current_window = fgStructure.CurrentWindow;
101 freeglut_return_if_fail( window );
103 if( window->State.NeedToInitContext ) {
104 INVOKE_WCB( *window, InitContext, ());
105 window->State.NeedToInitContext = GL_FALSE;
108 freeglut_return_if_fail( FETCH_WCB ( *window, Display ) );
110 window->State.Redisplay = GL_FALSE;
112 freeglut_return_if_fail( window->State.Visible );
114 fgSetWindow( window );
116 if( window->State.NeedToResize )
118 /* Set need to resize to false before calling fghReshapeWindow, otherwise
119 in the case the user's reshape callback calls glutReshapeWindow,
120 his request would get canceled after fghReshapeWindow gets called.
122 window->State.NeedToResize = GL_FALSE;
131 INVOKE_WCB( *window, Display, ( ) );
133 fgSetWindow( current_window );
137 static void fghcbDisplayWindow( SFG_Window *window,
138 SFG_Enumerator *enumerator )
140 if( window->State.Redisplay &&
141 window->State.Visible )
143 window->State.Redisplay = GL_FALSE;
144 fgPlatformDisplayWindow ( window );
147 fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );
151 * Make all windows perform a display call
153 static void fghDisplayAll( void )
155 SFG_Enumerator enumerator;
157 enumerator.found = GL_FALSE;
158 enumerator.data = NULL;
160 fgEnumWindows( fghcbDisplayWindow, &enumerator );
164 * Window enumerator callback to check for the joystick polling code
166 static void fghcbCheckJoystickPolls( SFG_Window *window,
167 SFG_Enumerator *enumerator )
171 if (window->State.JoystickPollRate > 0 && FETCH_WCB( *window, Joystick ))
173 /* This window has a joystick to be polled (if pollrate <= 0, user needs to poll manually with glutForceJoystickFunc */
174 checkTime= fgElapsedTime( );
176 if( window->State.JoystickLastPoll + window->State.JoystickPollRate <=
179 #if !defined(_WIN32_WCE)
180 fgJoystickPollWindow( window );
181 #endif /* !defined(_WIN32_WCE) */
182 window->State.JoystickLastPoll = checkTime;
186 fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );
190 * Check all windows for joystick polling
192 * The real way to do this is to make use of the glutTimer() API
193 * to more cleanly re-implement the joystick API. Then, this code
194 * and all other "joystick timer" code can be yanked.
196 static void fghCheckJoystickPolls( void )
198 SFG_Enumerator enumerator;
200 enumerator.found = GL_FALSE;
201 enumerator.data = NULL;
203 fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );
207 * Check the global timers
209 static void fghCheckTimers( void )
211 fg_time_t checkTime = fgElapsedTime( );
213 while( fgState.Timers.First )
215 SFG_Timer *timer = fgState.Timers.First;
217 if( timer->TriggerTime > checkTime )
218 /* XXX: are timers always sorted by triggerTime? If not, this and fghNextTimer are wrong */
221 fgListRemove( &fgState.Timers, &timer->Node );
222 fgListAppend( &fgState.FreeTimers, &timer->Node );
224 timer->Callback( timer->ID );
229 /* Platform-dependent time in milliseconds, as an unsigned 64-bit integer.
230 * This doesn't overflow in any reasonable time, so no need to worry about
231 * that. The GLUT API return value will however overflow after 49.7 days,
232 * which means you will still get in trouble when running the
233 * application for more than 49.7 days.
235 fg_time_t fgSystemTime(void)
237 return fgPlatformSystemTime();
243 fg_time_t fgElapsedTime( void )
245 return fgSystemTime() - fgState.Time;
251 void fgError( const char *fmt, ... )
255 if (fgState.ErrorFunc) {
259 /* call user set error handler here */
260 fgState.ErrorFunc(fmt, ap);
268 fprintf( stderr, "freeglut ");
269 if( fgState.ProgramName )
270 fprintf( stderr, "(%s): ", fgState.ProgramName );
271 VFPRINTF( stderr, fmt, ap );
272 fprintf( stderr, "\n" );
277 if ( fgState.Initialised )
284 void fgWarning( const char *fmt, ... )
288 if (fgState.WarningFunc) {
292 /* call user set warning handler here */
293 fgState.WarningFunc(fmt, ap);
298 #if FREEGLUT_WARNINGS
301 fprintf( stderr, "freeglut ");
302 if( fgState.ProgramName )
303 fprintf( stderr, "(%s): ", fgState.ProgramName );
304 VFPRINTF( stderr, fmt, ap );
305 fprintf( stderr, "\n" );
314 * Indicates whether a redisplay is pending for ANY window.
316 * The current mechanism is to walk all of the windows and ask if
317 * a redisplay is pending. We have a short-circuit early
318 * return if we find any.
320 static void fghHavePendingRedisplaysCallback( SFG_Window* w, SFG_Enumerator* e)
322 if( w->State.Redisplay && w->State.Visible )
328 fgEnumSubWindows( w, fghHavePendingRedisplaysCallback, e );
330 static int fghHavePendingRedisplays (void)
332 SFG_Enumerator enumerator;
334 enumerator.found = GL_FALSE;
335 enumerator.data = NULL;
336 fgEnumWindows( fghHavePendingRedisplaysCallback, &enumerator );
337 return !!enumerator.data;
341 * Returns the number of GLUT ticks (milliseconds) till the next timer event.
343 static fg_time_t fghNextTimer( void )
345 fg_time_t currentTime = fgElapsedTime();
346 SFG_Timer *timer = fgState.Timers.First;
351 if( timer->TriggerTime < currentTime )
354 return timer->TriggerTime - currentTime;
357 static void fghSleepForEvents( void )
361 if( fghHavePendingRedisplays( ) )
364 msec = fghNextTimer( );
365 /* XXX Should use GLUT timers for joysticks... */
366 /* XXX Dumb; forces granularity to .01sec */
367 if( fgState.NumActiveJoysticks>0 && ( msec > 10 ) )
370 fgPlatformSleepForEvents ( msec );
374 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
377 * Executes a single iteration in the freeglut processing loop.
379 void FGAPIENTRY glutMainLoopEvent( void )
381 fgPlatformProcessSingleEvent ();
383 if( fgState.Timers.First )
385 if (fgState.NumActiveJoysticks>0) /* If zero, don't poll joysticks */
386 fghCheckJoystickPolls( );
393 * Enters the freeglut processing loop.
394 * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
396 void FGAPIENTRY glutMainLoop( void )
400 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoop" );
402 if (!fgStructure.Windows.First)
403 fgError(" ERROR: glutMainLoop called with no windows created.");
405 fgPlatformMainLoopPreliminaryWork ();
407 fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
408 while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
412 glutMainLoopEvent( );
414 * Step through the list of windows, seeing if there are any
417 for( window = ( SFG_Window * )fgStructure.Windows.First;
419 window = ( SFG_Window * )window->Node.Next )
420 if ( ! ( window->IsMenu ) )
424 fgState.ExecState = GLUT_EXEC_STATE_STOP;
427 if( fgState.IdleCallback )
429 if( fgStructure.CurrentWindow &&
430 fgStructure.CurrentWindow->IsMenu )
432 fgSetWindow( window );
433 fgState.IdleCallback( );
436 fghSleepForEvents( );
441 * When this loop terminates, destroy the display, state and structure
442 * of a freeglut session, so that another glutInit() call can happen
444 * Save the "ActionOnWindowClose" because "fgDeinitialize" resets it.
446 action = fgState.ActionOnWindowClose;
448 if( action == GLUT_ACTION_EXIT )
453 * Leaves the freeglut processing loop.
455 void FGAPIENTRY glutLeaveMainLoop( void )
457 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveMainLoop" );
458 fgState.ExecState = GLUT_EXEC_STATE_STOP ;
463 /*** END OF FILE ***/