timers internally now use 64bit unsigned int, if available
[freeglut] / src / Common / freeglut_main.c
1 /*
2  * freeglut_main.c
3  *
4  * The windows message processing methods.
5  *
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
9  *
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:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
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.
26  */
27
28 #include <GL/freeglut.h>
29 #include "freeglut_internal.h"
30 #ifdef HAVE_ERRNO_H
31 #    include <errno.h>
32 #endif
33 #include <stdarg.h>
34 #ifdef  HAVE_VFPRINTF
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))
38 #else
39 #    define VFPRINTF(s,f,a)
40 #endif
41
42 /*
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.
45  */
46 #ifdef HAVE_LIMITS_H
47 #    include <limits.h>
48 #endif
49 #ifndef INT_MAX
50 #    define INT_MAX 32767
51 #endif
52
53 #ifndef MIN
54 #    define MIN(a,b) (((a)<(b)) ? (a) : (b))
55 #endif
56
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 );
63
64
65
66
67 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
68
69 static void fghReshapeWindow ( SFG_Window *window, int width, int height )
70 {
71     SFG_Window *current_window = fgStructure.CurrentWindow;
72
73     freeglut_return_if_fail( window != NULL );
74
75         fgPlatformReshapeWindow ( window, width, height );
76
77     if( FETCH_WCB( *window, Reshape ) )
78         INVOKE_WCB( *window, Reshape, ( width, height ) );
79     else
80     {
81         fgSetWindow( window );
82         glViewport( 0, 0, width, height );
83     }
84
85     /*
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
90      * window.
91      */
92     window->State.Redisplay = GL_TRUE;
93
94     if( window->IsMenu )
95         fgSetWindow( current_window );
96 }
97
98 /*
99  * Calls a window's redraw method. This is used when
100  * a redraw is forced by the incoming window messages.
101  */
102 void fghRedrawWindow ( SFG_Window *window )
103 {
104     SFG_Window *current_window = fgStructure.CurrentWindow;
105
106     freeglut_return_if_fail( window );
107     freeglut_return_if_fail( FETCH_WCB ( *window, Display ) );
108
109     window->State.Redisplay = GL_FALSE;
110
111     freeglut_return_if_fail( window->State.Visible );
112
113     fgSetWindow( window );
114
115     if( window->State.NeedToResize )
116     {
117         fghReshapeWindow(
118             window,
119             window->State.Width,
120             window->State.Height
121         );
122
123         window->State.NeedToResize = GL_FALSE;
124     }
125
126     INVOKE_WCB( *window, Display, ( ) );
127
128     fgSetWindow( current_window );
129 }
130
131
132 static void fghcbDisplayWindow( SFG_Window *window,
133                                 SFG_Enumerator *enumerator )
134 {
135     if( window->State.Redisplay &&
136         window->State.Visible )
137     {
138         window->State.Redisplay = GL_FALSE;
139                 fgPlatformDisplayWindow ( window );
140     }
141
142     fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );
143 }
144
145 /*
146  * Make all windows perform a display call
147  */
148 static void fghDisplayAll( void )
149 {
150     SFG_Enumerator enumerator;
151
152     enumerator.found = GL_FALSE;
153     enumerator.data  =  NULL;
154
155     fgEnumWindows( fghcbDisplayWindow, &enumerator );
156 }
157
158 /*
159  * Window enumerator callback to check for the joystick polling code
160  */
161 static void fghcbCheckJoystickPolls( SFG_Window *window,
162                                      SFG_Enumerator *enumerator )
163 {
164     fg_time_t checkTime = fgElapsedTime( );
165
166     if( window->State.JoystickLastPoll + window->State.JoystickPollRate <=
167         checkTime )
168     {
169 #if !defined(_WIN32_WCE)
170         fgJoystickPollWindow( window );
171 #endif /* !defined(_WIN32_WCE) */
172         window->State.JoystickLastPoll = checkTime;
173     }
174
175     fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );
176 }
177
178 /*
179  * Check all windows for joystick polling
180  */
181 static void fghCheckJoystickPolls( void )
182 {
183     SFG_Enumerator enumerator;
184
185     enumerator.found = GL_FALSE;
186     enumerator.data  =  NULL;
187
188     fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );
189 }
190
191 /*
192  * Check the global timers
193  */
194 static void fghCheckTimers( void )
195 {
196     fg_time_t checkTime = fgElapsedTime( );
197
198     while( fgState.Timers.First )
199     {
200         SFG_Timer *timer = fgState.Timers.First;
201
202         if( timer->TriggerTime > checkTime )
203             break;
204
205         fgListRemove( &fgState.Timers, &timer->Node );
206         fgListAppend( &fgState.FreeTimers, &timer->Node );
207
208         timer->Callback( timer->ID );
209     }
210 }
211
212  
213 /* Platform-dependent time in milliseconds, as an unsigned 64-bit integer.
214  * This doesn't overflow in any reasonable time, so no need to worry about
215  * that. The GLUT API return value will however overflow after 49.7 days,
216  * and on Windows we (currently) do not have access to a 64-bit timestamp,
217  * which means internal time will still get in trouble when running the
218  * application for more than 49.7 days.
219  * This value wraps every 49.7 days, but integer overflows cancel
220  * when subtracting an initial start time, unless the total time exceeds
221  * 32-bit, where the GLUT API return value is also overflowed.
222  */  
223 fg_time_t fgSystemTime(void)
224 {
225         return fgPlatformSystemTime();
226 }
227   
228 /*
229  * Elapsed Time
230  */
231 fg_time_t fgElapsedTime( void )
232 {
233     return fgSystemTime() - fgState.Time;
234 }
235
236 /*
237  * Error Messages.
238  */
239 void fgError( const char *fmt, ... )
240 {
241     va_list ap;
242
243     if (fgState.ErrorFunc) {
244
245         va_start( ap, fmt );
246
247         /* call user set error handler here */
248         fgState.ErrorFunc(fmt, ap);
249
250         va_end( ap );
251
252     } else {
253
254         va_start( ap, fmt );
255
256         fprintf( stderr, "freeglut ");
257         if( fgState.ProgramName )
258             fprintf( stderr, "(%s): ", fgState.ProgramName );
259         VFPRINTF( stderr, fmt, ap );
260         fprintf( stderr, "\n" );
261
262         va_end( ap );
263
264         if ( fgState.Initialised )
265             fgDeinitialize ();
266
267         exit( 1 );
268     }
269 }
270
271 void fgWarning( const char *fmt, ... )
272 {
273     va_list ap;
274
275     if (fgState.WarningFunc) {
276
277         va_start( ap, fmt );
278
279         /* call user set warning handler here */
280         fgState.WarningFunc(fmt, ap);
281
282         va_end( ap );
283
284     } else {
285
286         va_start( ap, fmt );
287
288         fprintf( stderr, "freeglut ");
289         if( fgState.ProgramName )
290             fprintf( stderr, "(%s): ", fgState.ProgramName );
291         VFPRINTF( stderr, fmt, ap );
292         fprintf( stderr, "\n" );
293
294         va_end( ap );
295     }
296 }
297
298
299 /*
300  * Indicates whether Joystick events are being used by ANY window.
301  *
302  * The current mechanism is to walk all of the windows and ask if
303  * there is a joystick callback.  We have a short-circuit early
304  * return if we find any joystick handler registered.
305  *
306  * The real way to do this is to make use of the glutTimer() API
307  * to more cleanly re-implement the joystick API.  Then, this code
308  * and all other "joystick timer" code can be yanked.
309  *
310  */
311 static void fghCheckJoystickCallback( SFG_Window* w, SFG_Enumerator* e)
312 {
313     if( FETCH_WCB( *w, Joystick ) )
314     {
315         e->found = GL_TRUE;
316         e->data = w;
317     }
318     fgEnumSubWindows( w, fghCheckJoystickCallback, e );
319 }
320 static int fghHaveJoystick( void )
321 {
322     SFG_Enumerator enumerator;
323
324     enumerator.found = GL_FALSE;
325     enumerator.data = NULL;
326     fgEnumWindows( fghCheckJoystickCallback, &enumerator );
327     return !!enumerator.data;
328 }
329 static void fghHavePendingRedisplaysCallback( SFG_Window* w, SFG_Enumerator* e)
330 {
331     if( w->State.Redisplay && w->State.Visible )
332     {
333         e->found = GL_TRUE;
334         e->data = w;
335     }
336     fgEnumSubWindows( w, fghHavePendingRedisplaysCallback, e );
337 }
338 static int fghHavePendingRedisplays (void)
339 {
340     SFG_Enumerator enumerator;
341
342     enumerator.found = GL_FALSE;
343     enumerator.data = NULL;
344     fgEnumWindows( fghHavePendingRedisplaysCallback, &enumerator );
345     return !!enumerator.data;
346 }
347 /*
348  * Returns the number of GLUT ticks (milliseconds) till the next timer event.
349  */
350 static fg_time_t fghNextTimer( void )
351 {
352     fg_time_t currentTime = fgElapsedTime();
353     SFG_Timer *timer = fgState.Timers.First;
354
355     if( !timer )
356         return INT_MAX;
357
358     if( timer->TriggerTime < currentTime )
359         return 0;
360     else
361         return timer->TriggerTime - currentTime;
362 }
363
364 static void fghSleepForEvents( void )
365 {
366     fg_time_t msec;
367
368     if( fgState.IdleCallback || fghHavePendingRedisplays( ) )
369         return;
370
371     msec = fghNextTimer( );
372     /* XXX Use GLUT timers for joysticks... */
373     /* XXX Dumb; forces granularity to .01sec */
374     if( fghHaveJoystick( ) && ( msec > 10 ) )     
375         msec = 10;
376
377         fgPlatformSleepForEvents ( msec );
378 }
379
380
381 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
382
383 /*
384  * Executes a single iteration in the freeglut processing loop.
385  */
386 void FGAPIENTRY glutMainLoopEvent( void )
387 {
388         fgPlatformProcessSingleEvent ();
389
390     if( fgState.Timers.First )
391         fghCheckTimers( );
392     fghCheckJoystickPolls( );
393     fghDisplayAll( );
394
395     fgCloseWindows( );
396 }
397
398 /*
399  * Enters the freeglut processing loop.
400  * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
401  */
402 void FGAPIENTRY glutMainLoop( void )
403 {
404     int action;
405
406     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoop" );
407
408         fgPlatformMainLoopPreliminaryWork ();
409
410     fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
411     while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
412     {
413         SFG_Window *window;
414
415         glutMainLoopEvent( );
416         /*
417          * Step through the list of windows, seeing if there are any
418          * that are not menus
419          */
420         for( window = ( SFG_Window * )fgStructure.Windows.First;
421              window;
422              window = ( SFG_Window * )window->Node.Next )
423             if ( ! ( window->IsMenu ) )
424                 break;
425
426         if( ! window )
427             fgState.ExecState = GLUT_EXEC_STATE_STOP;
428         else
429         {
430             if( fgState.IdleCallback )
431             {
432                 if( fgStructure.CurrentWindow &&
433                     fgStructure.CurrentWindow->IsMenu )
434                     /* fail safe */
435                     fgSetWindow( window );
436                 fgState.IdleCallback( );
437             }
438
439             fghSleepForEvents( );
440         }
441     }
442
443     /*
444      * When this loop terminates, destroy the display, state and structure
445      * of a freeglut session, so that another glutInit() call can happen
446      *
447      * Save the "ActionOnWindowClose" because "fgDeinitialize" resets it.
448      */
449     action = fgState.ActionOnWindowClose;
450     fgDeinitialize( );
451     if( action == GLUT_ACTION_EXIT )
452         exit( 0 );
453 }
454
455 /*
456  * Leaves the freeglut processing loop.
457  */
458 void FGAPIENTRY glutLeaveMainLoop( void )
459 {
460     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveMainLoop" );
461     fgState.ExecState = GLUT_EXEC_STATE_STOP ;
462 }
463
464
465
466 /*** END OF FILE ***/