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