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;
107 freeglut_return_if_fail( window );
109 if( window->State.NeedToInitContext ) {
110 INVOKE_WCB( *window, InitContext, ());
111 window->State.NeedToInitContext = GL_FALSE;
114 freeglut_return_if_fail( FETCH_WCB ( *window, Display ) );
116 window->State.Redisplay = GL_FALSE;
118 freeglut_return_if_fail( window->State.Visible );
120 fgSetWindow( window );
122 if( window->State.NeedToResize )
124 /* Set need to resize to false before calling fghReshapeWindow, otherwise
125 in the case the user's reshape callback calls glutReshapeWindow,
126 his request would get canceled after fghReshapeWindow gets called.
128 window->State.NeedToResize = GL_FALSE;
137 INVOKE_WCB( *window, Display, ( ) );
139 fgSetWindow( current_window );
143 static void fghcbDisplayWindow( SFG_Window *window,
144 SFG_Enumerator *enumerator )
146 if( window->State.Redisplay &&
147 window->State.Visible )
149 window->State.Redisplay = GL_FALSE;
150 fgPlatformDisplayWindow ( window );
153 fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );
157 * Make all windows perform a display call
159 static void fghDisplayAll( void )
161 SFG_Enumerator enumerator;
163 enumerator.found = GL_FALSE;
164 enumerator.data = NULL;
166 fgEnumWindows( fghcbDisplayWindow, &enumerator );
170 * Window enumerator callback to check for the joystick polling code
172 static void fghcbCheckJoystickPolls( SFG_Window *window,
173 SFG_Enumerator *enumerator )
175 fg_time_t checkTime = fgElapsedTime( );
177 if( window->State.JoystickLastPoll + window->State.JoystickPollRate <=
180 #if !defined(_WIN32_WCE)
181 fgJoystickPollWindow( window );
182 #endif /* !defined(_WIN32_WCE) */
183 window->State.JoystickLastPoll = checkTime;
186 fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );
190 * Check all windows for joystick polling
192 static void fghCheckJoystickPolls( void )
194 SFG_Enumerator enumerator;
196 enumerator.found = GL_FALSE;
197 enumerator.data = NULL;
199 fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );
203 * Check the global timers
205 static void fghCheckTimers( void )
207 fg_time_t checkTime = fgElapsedTime( );
209 while( fgState.Timers.First )
211 SFG_Timer *timer = fgState.Timers.First;
213 if( timer->TriggerTime > checkTime )
216 fgListRemove( &fgState.Timers, &timer->Node );
217 fgListAppend( &fgState.FreeTimers, &timer->Node );
219 timer->Callback( timer->ID );
224 /* Platform-dependent time in milliseconds, as an unsigned 64-bit integer.
225 * This doesn't overflow in any reasonable time, so no need to worry about
226 * that. The GLUT API return value will however overflow after 49.7 days,
227 * and on Windows we (currently) do not have access to a 64-bit timestamp,
228 * which means internal time will still get in trouble when running the
229 * application for more than 49.7 days.
230 * This value wraps every 49.7 days, but integer overflows cancel
231 * when subtracting an initial start time, unless the total time exceeds
232 * 32-bit, where the GLUT API return value is also overflowed.
234 fg_time_t fgSystemTime(void)
236 return fgPlatformSystemTime();
242 fg_time_t fgElapsedTime( void )
244 return fgSystemTime() - fgState.Time;
250 void fgError( const char *fmt, ... )
254 if (fgState.ErrorFunc) {
258 /* call user set error handler here */
259 fgState.ErrorFunc(fmt, ap);
267 fprintf( stderr, "freeglut ");
268 if( fgState.ProgramName )
269 fprintf( stderr, "(%s): ", fgState.ProgramName );
270 VFPRINTF( stderr, fmt, ap );
271 fprintf( stderr, "\n" );
276 if ( fgState.Initialised )
283 void fgWarning( const char *fmt, ... )
287 if (fgState.WarningFunc) {
291 /* call user set warning handler here */
292 fgState.WarningFunc(fmt, ap);
297 #if FREEGLUT_WARNINGS
300 fprintf( stderr, "freeglut ");
301 if( fgState.ProgramName )
302 fprintf( stderr, "(%s): ", fgState.ProgramName );
303 VFPRINTF( stderr, fmt, ap );
304 fprintf( stderr, "\n" );
313 * Indicates whether Joystick events are being used by ANY window.
315 * The current mechanism is to walk all of the windows and ask if
316 * there is a joystick callback. We have a short-circuit early
317 * return if we find any joystick handler registered.
319 * The real way to do this is to make use of the glutTimer() API
320 * to more cleanly re-implement the joystick API. Then, this code
321 * and all other "joystick timer" code can be yanked.
324 static void fghCheckJoystickCallback( SFG_Window* w, SFG_Enumerator* e)
326 if( FETCH_WCB( *w, Joystick ) )
331 fgEnumSubWindows( w, fghCheckJoystickCallback, e );
333 static int fghHaveJoystick( void )
335 SFG_Enumerator enumerator;
337 enumerator.found = GL_FALSE;
338 enumerator.data = NULL;
339 fgEnumWindows( fghCheckJoystickCallback, &enumerator );
340 return !!enumerator.data;
342 static void fghHavePendingRedisplaysCallback( SFG_Window* w, SFG_Enumerator* e)
344 if( w->State.Redisplay && w->State.Visible )
349 fgEnumSubWindows( w, fghHavePendingRedisplaysCallback, e );
351 static int fghHavePendingRedisplays (void)
353 SFG_Enumerator enumerator;
355 enumerator.found = GL_FALSE;
356 enumerator.data = NULL;
357 fgEnumWindows( fghHavePendingRedisplaysCallback, &enumerator );
358 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 = fgElapsedTime();
366 SFG_Timer *timer = fgState.Timers.First;
371 if( timer->TriggerTime < currentTime )
374 return timer->TriggerTime - currentTime;
377 static void fghSleepForEvents( void )
381 if( fgState.IdleCallback || fghHavePendingRedisplays( ) )
384 msec = fghNextTimer( );
385 /* XXX Use GLUT timers for joysticks... */
386 /* XXX Dumb; forces granularity to .01sec */
387 if( fghHaveJoystick( ) && ( msec > 10 ) )
390 fgPlatformSleepForEvents ( msec );
394 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
397 * Executes a single iteration in the freeglut processing loop.
399 void FGAPIENTRY glutMainLoopEvent( void )
401 fgPlatformProcessSingleEvent ();
403 if( fgState.Timers.First )
405 fghCheckJoystickPolls( );
412 * Enters the freeglut processing loop.
413 * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
415 void FGAPIENTRY glutMainLoop( void )
419 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoop" );
421 fgPlatformMainLoopPreliminaryWork ();
423 fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
424 while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
428 glutMainLoopEvent( );
430 * Step through the list of windows, seeing if there are any
433 for( window = ( SFG_Window * )fgStructure.Windows.First;
435 window = ( SFG_Window * )window->Node.Next )
436 if ( ! ( window->IsMenu ) )
440 fgState.ExecState = GLUT_EXEC_STATE_STOP;
443 if( fgState.IdleCallback )
445 if( fgStructure.CurrentWindow &&
446 fgStructure.CurrentWindow->IsMenu )
448 fgSetWindow( window );
449 fgState.IdleCallback( );
452 fghSleepForEvents( );
457 * When this loop terminates, destroy the display, state and structure
458 * of a freeglut session, so that another glutInit() call can happen
460 * Save the "ActionOnWindowClose" because "fgDeinitialize" resets it.
462 action = fgState.ActionOnWindowClose;
464 if( action == GLUT_ACTION_EXIT )
469 * Leaves the freeglut processing loop.
471 void FGAPIENTRY glutLeaveMainLoop( void )
473 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveMainLoop" );
474 fgState.ExecState = GLUT_EXEC_STATE_STOP ;
479 /*** END OF FILE ***/