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