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 fgPlatformProcessWork ( SFG_Window *window );
58 extern fg_time_t fgPlatformSystemTime ( void );
59 extern void fgPlatformSleepForEvents( fg_time_t msec );
60 extern void fgPlatformProcessSingleEvent ( void );
61 extern void fgPlatformMainLoopPreliminaryWork ( void );
66 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
68 void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify)
70 GLboolean notify = GL_FALSE;
72 if( width != window->State.Width ||
73 height != window->State.Height )
75 window->State.Width = width;
76 window->State.Height = height;
81 if (notify || forceNotify)
83 SFG_Window *saved_window = fgStructure.CurrentWindow;
85 INVOKE_WCB( *window, Reshape, ( width, height ) );
88 * Force a window redraw. In Windows at least this is only a partial
89 * solution: if the window is increasing in size in either dimension,
90 * the already-drawn part does not get drawn again and things look funny.
91 * But without this we get this bad behaviour whenever we resize the
93 * DN: Hmm.. the above sounds like a concern only in single buffered mode...
97 fgSetWindow( saved_window );
101 void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify)
103 GLboolean notify = GL_FALSE;
105 if( x != window->State.Xpos ||
106 y != window->State.Ypos )
108 window->State.Xpos = x;
109 window->State.Ypos = y;
114 if (notify || forceNotify)
116 SFG_Window *saved_window = fgStructure.CurrentWindow;
117 INVOKE_WCB( *window, Position, ( x, y ) );
118 fgSetWindow( saved_window );
123 * Calls a window's redraw method. This is used when
124 * a redraw is forced by the incoming window messages,
125 * or if a redisplay is otherwise pending.
126 * this is lean and mean without checks as it is
127 * currently only called from fghcbDisplayWindow which
128 * only calls this if the window is visible and needs
130 * Note that the fgSetWindow call on Windows makes the
131 * right device context current on windows, allowing
132 * direct drawing without BeginPaint/EndPaint in the
135 void fghRedrawWindow ( SFG_Window *window )
137 SFG_Window *current_window = fgStructure.CurrentWindow;
139 fgSetWindow( window );
140 INVOKE_WCB( *window, Display, ( ) );
142 fgSetWindow( current_window );
145 void fghRedrawWindowAndChildren ( SFG_Window *window )
149 fghRedrawWindow(window);
151 for( child = ( SFG_Window * )window->Children.First;
153 child = ( SFG_Window * )child->Node.Next )
155 fghRedrawWindowAndChildren(child);
160 static void fghcbProcessWork( SFG_Window *window,
161 SFG_Enumerator *enumerator )
163 if( window->State.WorkMask )
164 fgPlatformProcessWork ( window );
166 fgEnumSubWindows( window, fghcbProcessWork, enumerator );
170 * Make all windows process their work list
172 static void fghProcessWork( void )
174 SFG_Enumerator enumerator;
176 enumerator.found = GL_FALSE;
177 enumerator.data = NULL;
179 fgEnumWindows( fghcbProcessWork, &enumerator );
183 static void fghcbDisplayWindow( SFG_Window *window,
184 SFG_Enumerator *enumerator )
186 if( window->State.Redisplay &&
187 window->State.Visible )
189 window->State.Redisplay = GL_FALSE;
190 fghRedrawWindow ( window );
193 fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );
197 * Make all windows perform a display call
199 static void fghDisplayAll( void )
201 SFG_Enumerator enumerator;
203 enumerator.found = GL_FALSE;
204 enumerator.data = NULL;
206 fgEnumWindows( fghcbDisplayWindow, &enumerator );
210 * Window enumerator callback to check for the joystick polling code
212 static void fghcbCheckJoystickPolls( SFG_Window *window,
213 SFG_Enumerator *enumerator )
217 if (window->State.JoystickPollRate > 0 && FETCH_WCB( *window, Joystick ))
219 /* This window has a joystick to be polled (if pollrate <= 0, user needs to poll manually with glutForceJoystickFunc */
220 checkTime= fgElapsedTime( );
222 if( window->State.JoystickLastPoll + window->State.JoystickPollRate <=
225 #if !defined(_WIN32_WCE)
226 fgJoystickPollWindow( window );
227 #endif /* !defined(_WIN32_WCE) */
228 window->State.JoystickLastPoll = checkTime;
232 fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );
236 * Check all windows for joystick polling
238 * The real way to do this is to make use of the glutTimer() API
239 * to more cleanly re-implement the joystick API. Then, this code
240 * and all other "joystick timer" code can be yanked.
242 static void fghCheckJoystickPolls( void )
244 SFG_Enumerator enumerator;
246 enumerator.found = GL_FALSE;
247 enumerator.data = NULL;
249 fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );
253 * Check the global timers
255 static void fghCheckTimers( void )
257 fg_time_t checkTime = fgElapsedTime( );
259 while( fgState.Timers.First )
261 SFG_Timer *timer = fgState.Timers.First;
263 if( timer->TriggerTime > checkTime )
264 /* XXX: are timers always sorted by triggerTime? If not, this and fghNextTimer are wrong */
267 fgListRemove( &fgState.Timers, &timer->Node );
268 fgListAppend( &fgState.FreeTimers, &timer->Node );
270 timer->Callback( timer->ID );
275 /* Platform-dependent time in milliseconds, as an unsigned 64-bit integer.
276 * This doesn't overflow in any reasonable time, so no need to worry about
277 * that. The GLUT API return value will however overflow after 49.7 days,
278 * which means you will still get in trouble when running the
279 * application for more than 49.7 days.
281 fg_time_t fgSystemTime(void)
283 return fgPlatformSystemTime();
289 fg_time_t fgElapsedTime( void )
291 return fgSystemTime() - fgState.Time;
297 void fgError( const char *fmt, ... )
301 if (fgState.ErrorFunc) {
305 /* call user set error handler here */
306 fgState.ErrorFunc(fmt, ap);
314 fprintf( stderr, "freeglut ");
315 if( fgState.ProgramName )
316 fprintf( stderr, "(%s): ", fgState.ProgramName );
317 VFPRINTF( stderr, fmt, ap );
318 fprintf( stderr, "\n" );
323 if ( fgState.Initialised )
330 void fgWarning( const char *fmt, ... )
334 if (fgState.WarningFunc) {
338 /* call user set warning handler here */
339 fgState.WarningFunc(fmt, ap);
344 #if FREEGLUT_WARNINGS
347 fprintf( stderr, "freeglut ");
348 if( fgState.ProgramName )
349 fprintf( stderr, "(%s): ", fgState.ProgramName );
350 VFPRINTF( stderr, fmt, ap );
351 fprintf( stderr, "\n" );
360 * Indicates whether a redisplay is pending for ANY window.
362 * The current mechanism is to walk all of the windows and ask if
363 * a redisplay is pending. We have a short-circuit early
364 * return if we find any.
366 static void fghHavePendingRedisplaysCallback( SFG_Window* w, SFG_Enumerator* e)
368 if( w->State.Redisplay && w->State.Visible )
374 fgEnumSubWindows( w, fghHavePendingRedisplaysCallback, e );
376 static int fghHavePendingRedisplays (void)
378 SFG_Enumerator enumerator;
380 enumerator.found = GL_FALSE;
381 enumerator.data = NULL;
382 fgEnumWindows( fghHavePendingRedisplaysCallback, &enumerator );
383 return !!enumerator.data;
387 * Returns the number of GLUT ticks (milliseconds) till the next timer event.
389 static fg_time_t fghNextTimer( void )
391 fg_time_t currentTime = fgElapsedTime();
392 SFG_Timer *timer = fgState.Timers.First;
397 if( timer->TriggerTime < currentTime )
400 return timer->TriggerTime - currentTime;
403 static void fghSleepForEvents( void )
407 if( fghHavePendingRedisplays( ) )
410 msec = fghNextTimer( );
411 /* XXX Should use GLUT timers for joysticks... */
412 /* XXX Dumb; forces granularity to .01sec */
413 if( fgState.NumActiveJoysticks>0 && ( msec > 10 ) )
416 fgPlatformSleepForEvents ( msec );
420 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
423 * Executes a single iteration in the freeglut processing loop.
425 void FGAPIENTRY glutMainLoopEvent( void )
428 fgPlatformProcessSingleEvent ();
430 if( fgState.Timers.First )
432 if (fgState.NumActiveJoysticks>0) /* If zero, don't poll joysticks */
433 fghCheckJoystickPolls( );
435 /* Perform work on the window (position, reshape, etc) */
445 * Enters the freeglut processing loop.
446 * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
448 void FGAPIENTRY glutMainLoop( void )
452 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoop" );
454 if (!fgStructure.Windows.First)
455 fgError(" ERROR: glutMainLoop called with no windows created.");
457 fgPlatformMainLoopPreliminaryWork ();
459 fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
460 while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
464 glutMainLoopEvent( );
466 * Step through the list of windows, seeing if there are any
469 for( window = ( SFG_Window * )fgStructure.Windows.First;
471 window = ( SFG_Window * )window->Node.Next )
472 if ( ! ( window->IsMenu ) )
476 fgState.ExecState = GLUT_EXEC_STATE_STOP;
479 if( fgState.IdleCallback )
481 if( fgStructure.CurrentWindow &&
482 fgStructure.CurrentWindow->IsMenu )
484 fgSetWindow( window );
485 fgState.IdleCallback( );
488 fghSleepForEvents( );
493 * When this loop terminates, destroy the display, state and structure
494 * of a freeglut session, so that another glutInit() call can happen
496 * Save the "ActionOnWindowClose" because "fgDeinitialize" resets it.
498 action = fgState.ActionOnWindowClose;
500 if( action == GLUT_ACTION_EXIT )
505 * Leaves the freeglut processing loop.
507 void FGAPIENTRY glutLeaveMainLoop( void )
509 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveMainLoop" );
510 fgState.ExecState = GLUT_EXEC_STATE_STOP ;
515 /*** END OF FILE ***/