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