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