4 * The windows message processing methods.
\r
6 * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
\r
7 * Written by Pawel W. Olszta, <olszta@sourceforge.net>
\r
8 * Creation date: Fri Dec 3 1999
\r
10 * Permission is hereby granted, free of charge, to any person obtaining a
\r
11 * copy of this software and associated documentation files (the "Software"),
\r
12 * to deal in the Software without restriction, including without limitation
\r
13 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
\r
14 * and/or sell copies of the Software, and to permit persons to whom the
\r
15 * Software is furnished to do so, subject to the following conditions:
\r
17 * The above copyright notice and this permission notice shall be included
\r
18 * in all copies or substantial portions of the Software.
\r
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
\r
21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
\r
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
\r
23 * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
24 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
25 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
28 #include <GL/freeglut.h>
\r
29 #include "freeglut_internal.h"
\r
34 #ifdef HAVE_VFPRINTF
\r
35 # define VFPRINTF(s,f,a) vfprintf((s),(f),(a))
\r
36 #elif defined(HAVE__DOPRNT)
\r
37 # define VFPRINTF(s,f,a) _doprnt((f),(a),(s))
\r
39 # define VFPRINTF(s,f,a)
\r
44 typedef struct GXDisplayProperties GXDisplayProperties;
\r
45 typedef struct GXKeyList GXKeyList;
\r
48 typedef struct GXKeyList (*GXGETDEFAULTKEYS)(int);
\r
49 typedef int (*GXOPENINPUT)();
\r
51 GXGETDEFAULTKEYS GXGetDefaultKeys_ = NULL;
\r
52 GXOPENINPUT GXOpenInput_ = NULL;
\r
54 struct GXKeyList gxKeyList;
\r
56 #endif /* _WIN32_WCE */
\r
59 * Try to get the maximum value allowed for ints, falling back to the minimum
\r
60 * guaranteed by ISO C99 if there is no suitable header.
\r
62 #ifdef HAVE_LIMITS_H
\r
63 # include <limits.h>
\r
66 # define INT_MAX 32767
\r
70 # define MIN(a,b) (((a)<(b)) ? (a) : (b))
\r
73 extern void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height );
\r
74 extern void fgPlatformDisplayWindow ( SFG_Window *window );
\r
75 extern unsigned long fgPlatformSystemTime ( void );
\r
76 extern void fgPlatformSleepForEvents( long msec );
\r
77 extern void fgPlatformProcessSingleEvent ( void );
\r
78 extern void fgPlatformMainLoopPreliminaryWork ( void );
\r
83 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
\r
85 static void fghReshapeWindow ( SFG_Window *window, int width, int height )
\r
87 SFG_Window *current_window = fgStructure.CurrentWindow;
\r
89 freeglut_return_if_fail( window != NULL );
\r
91 fgPlatformReshapeWindow ( window, width, height );
\r
93 if( FETCH_WCB( *window, Reshape ) )
\r
94 INVOKE_WCB( *window, Reshape, ( width, height ) );
\r
97 fgSetWindow( window );
\r
98 glViewport( 0, 0, width, height );
\r
102 * Force a window redraw. In Windows at least this is only a partial
\r
103 * solution: if the window is increasing in size in either dimension,
\r
104 * the already-drawn part does not get drawn again and things look funny.
\r
105 * But without this we get this bad behaviour whenever we resize the
\r
108 window->State.Redisplay = GL_TRUE;
\r
110 if( window->IsMenu )
\r
111 fgSetWindow( current_window );
\r
115 * Calls a window's redraw method. This is used when
\r
116 * a redraw is forced by the incoming window messages.
\r
118 void fghRedrawWindow ( SFG_Window *window )
\r
120 SFG_Window *current_window = fgStructure.CurrentWindow;
\r
122 freeglut_return_if_fail( window );
\r
123 freeglut_return_if_fail( FETCH_WCB ( *window, Display ) );
\r
125 window->State.Redisplay = GL_FALSE;
\r
127 freeglut_return_if_fail( window->State.Visible );
\r
129 fgSetWindow( window );
\r
131 if( window->State.NeedToResize )
\r
135 window->State.Width,
\r
136 window->State.Height
\r
139 window->State.NeedToResize = GL_FALSE;
\r
142 INVOKE_WCB( *window, Display, ( ) );
\r
144 fgSetWindow( current_window );
\r
148 static void fghcbDisplayWindow( SFG_Window *window,
\r
149 SFG_Enumerator *enumerator )
\r
151 if( window->State.Redisplay &&
\r
152 window->State.Visible )
\r
154 window->State.Redisplay = GL_FALSE;
\r
155 fgPlatformDisplayWindow ( window );
\r
158 fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );
\r
162 * Make all windows perform a display call
\r
164 static void fghDisplayAll( void )
\r
166 SFG_Enumerator enumerator;
\r
168 enumerator.found = GL_FALSE;
\r
169 enumerator.data = NULL;
\r
171 fgEnumWindows( fghcbDisplayWindow, &enumerator );
\r
175 * Window enumerator callback to check for the joystick polling code
\r
177 static void fghcbCheckJoystickPolls( SFG_Window *window,
\r
178 SFG_Enumerator *enumerator )
\r
180 long int checkTime = fgElapsedTime( );
\r
182 if( window->State.JoystickLastPoll + window->State.JoystickPollRate <=
\r
185 #if !defined(_WIN32_WCE)
\r
186 fgJoystickPollWindow( window );
\r
187 #endif /* !defined(_WIN32_WCE) */
\r
188 window->State.JoystickLastPoll = checkTime;
\r
191 fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );
\r
195 * Check all windows for joystick polling
\r
197 static void fghCheckJoystickPolls( void )
\r
199 SFG_Enumerator enumerator;
\r
201 enumerator.found = GL_FALSE;
\r
202 enumerator.data = NULL;
\r
204 fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );
\r
208 * Check the global timers
\r
210 static void fghCheckTimers( void )
\r
212 long checkTime = fgElapsedTime( );
\r
214 while( fgState.Timers.First )
\r
216 SFG_Timer *timer = fgState.Timers.First;
\r
218 if( timer->TriggerTime > checkTime )
\r
221 fgListRemove( &fgState.Timers, &timer->Node );
\r
222 fgListAppend( &fgState.FreeTimers, &timer->Node );
\r
224 timer->Callback( timer->ID );
\r
229 /* Platform-dependent time in milliseconds, as an unsigned 32-bit integer.
\r
230 * This value wraps every 49.7 days, but integer overflows cancel
\r
231 * when subtracting an initial start time, unless the total time exceeds
\r
232 * 32-bit, where the GLUT API return value is also overflowed.
\r
234 unsigned long fgSystemTime(void)
\r
236 return fgPlatformSystemTime ();
\r
242 long fgElapsedTime( void )
\r
244 return (long) (fgSystemTime() - fgState.Time);
\r
250 void fgError( const char *fmt, ... )
\r
254 if (fgState.ErrorFunc) {
\r
256 va_start( ap, fmt );
\r
258 /* call user set error handler here */
\r
259 fgState.ErrorFunc(fmt, ap);
\r
265 va_start( ap, fmt );
\r
267 fprintf( stderr, "freeglut ");
\r
268 if( fgState.ProgramName )
\r
269 fprintf( stderr, "(%s): ", fgState.ProgramName );
\r
270 VFPRINTF( stderr, fmt, ap );
\r
271 fprintf( stderr, "\n" );
\r
275 if ( fgState.Initialised )
\r
282 void fgWarning( const char *fmt, ... )
\r
286 if (fgState.WarningFunc) {
\r
288 va_start( ap, fmt );
\r
290 /* call user set warning handler here */
\r
291 fgState.WarningFunc(fmt, ap);
\r
297 va_start( ap, fmt );
\r
299 fprintf( stderr, "freeglut ");
\r
300 if( fgState.ProgramName )
\r
301 fprintf( stderr, "(%s): ", fgState.ProgramName );
\r
302 VFPRINTF( stderr, fmt, ap );
\r
303 fprintf( stderr, "\n" );
\r
311 * Indicates whether Joystick events are being used by ANY window.
\r
313 * The current mechanism is to walk all of the windows and ask if
\r
314 * there is a joystick callback. We have a short-circuit early
\r
315 * return if we find any joystick handler registered.
\r
317 * The real way to do this is to make use of the glutTimer() API
\r
318 * to more cleanly re-implement the joystick API. Then, this code
\r
319 * and all other "joystick timer" code can be yanked.
\r
322 static void fghCheckJoystickCallback( SFG_Window* w, SFG_Enumerator* e)
\r
324 if( FETCH_WCB( *w, Joystick ) )
\r
326 e->found = GL_TRUE;
\r
329 fgEnumSubWindows( w, fghCheckJoystickCallback, e );
\r
331 static int fghHaveJoystick( void )
\r
333 SFG_Enumerator enumerator;
\r
335 enumerator.found = GL_FALSE;
\r
336 enumerator.data = NULL;
\r
337 fgEnumWindows( fghCheckJoystickCallback, &enumerator );
\r
338 return !!enumerator.data;
\r
340 static void fghHavePendingRedisplaysCallback( SFG_Window* w, SFG_Enumerator* e)
\r
342 if( w->State.Redisplay && w->State.Visible )
\r
344 e->found = GL_TRUE;
\r
347 fgEnumSubWindows( w, fghHavePendingRedisplaysCallback, e );
\r
349 static int fghHavePendingRedisplays (void)
\r
351 SFG_Enumerator enumerator;
\r
353 enumerator.found = GL_FALSE;
\r
354 enumerator.data = NULL;
\r
355 fgEnumWindows( fghHavePendingRedisplaysCallback, &enumerator );
\r
356 return !!enumerator.data;
\r
359 * Returns the number of GLUT ticks (milliseconds) till the next timer event.
\r
361 static long fghNextTimer( void )
\r
363 long ret = INT_MAX;
\r
364 SFG_Timer *timer = fgState.Timers.First;
\r
367 ret = timer->TriggerTime - fgElapsedTime();
\r
374 static void fghSleepForEvents( void )
\r
378 if( fgState.IdleCallback || fghHavePendingRedisplays( ) )
\r
381 msec = fghNextTimer( );
\r
382 /* XXX Use GLUT timers for joysticks... */
\r
383 /* XXX Dumb; forces granularity to .01sec */
\r
384 if( fghHaveJoystick( ) && ( msec > 10 ) )
\r
387 fgPlatformSleepForEvents ( msec );
\r
391 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
\r
394 * Executes a single iteration in the freeglut processing loop.
\r
396 void FGAPIENTRY glutMainLoopEvent( void )
\r
398 fgPlatformProcessSingleEvent ();
\r
400 if( fgState.Timers.First )
\r
402 fghCheckJoystickPolls( );
\r
409 * Enters the freeglut processing loop.
\r
410 * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
\r
412 void FGAPIENTRY glutMainLoop( void )
\r
416 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoop" );
\r
418 fgPlatformMainLoopPreliminaryWork ();
\r
420 fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
\r
421 while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
\r
423 SFG_Window *window;
\r
425 glutMainLoopEvent( );
\r
427 * Step through the list of windows, seeing if there are any
\r
428 * that are not menus
\r
430 for( window = ( SFG_Window * )fgStructure.Windows.First;
\r
432 window = ( SFG_Window * )window->Node.Next )
\r
433 if ( ! ( window->IsMenu ) )
\r
437 fgState.ExecState = GLUT_EXEC_STATE_STOP;
\r
440 if( fgState.IdleCallback )
\r
442 if( fgStructure.CurrentWindow &&
\r
443 fgStructure.CurrentWindow->IsMenu )
\r
445 fgSetWindow( window );
\r
446 fgState.IdleCallback( );
\r
449 fghSleepForEvents( );
\r
454 * When this loop terminates, destroy the display, state and structure
\r
455 * of a freeglut session, so that another glutInit() call can happen
\r
457 * Save the "ActionOnWindowClose" because "fgDeinitialize" resets it.
\r
459 action = fgState.ActionOnWindowClose;
\r
461 if( action == GLUT_ACTION_EXIT )
\r
466 * Leaves the freeglut processing loop.
\r
468 void FGAPIENTRY glutLeaveMainLoop( void )
\r
470 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveMainLoop" );
\r
471 fgState.ExecState = GLUT_EXEC_STATE_STOP ;
\r
476 /*** END OF FILE ***/
\r