removed vfprintf and checking from CMakeLists.txt, and all the fallbacks from
[freeglut] / src / fg_main.c
1 /*
2  * fg_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
35 /*
36  * Try to get the maximum value allowed for ints, falling back to the minimum
37  * guaranteed by ISO C99 if there is no suitable header.
38  */
39 #ifdef HAVE_LIMITS_H
40 #    include <limits.h>
41 #endif
42 #ifndef INT_MAX
43 #    define INT_MAX 32767
44 #endif
45
46 #ifndef MIN
47 #    define MIN(a,b) (((a)<(b)) ? (a) : (b))
48 #endif
49
50 extern void fgProcessWork   ( SFG_Window *window );
51 extern fg_time_t fgPlatformSystemTime ( void );
52 extern void fgPlatformSleepForEvents( fg_time_t msec );
53 extern void fgPlatformProcessSingleEvent ( void );
54 extern void fgPlatformMainLoopPreliminaryWork ( void );
55
56 extern void fgPlatformInitWork(SFG_Window* window);
57 extern void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask);
58 extern void fgPlatformVisibilityWork(SFG_Window* window);
59
60
61 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
62
63 void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify)
64 {
65     GLboolean notify = GL_FALSE;
66
67     if( width  != window->State.Width ||
68         height != window->State.Height )
69     {
70         window->State.Width = width;
71         window->State.Height = height;
72
73         notify = GL_TRUE;
74     }
75
76     if (notify || forceNotify)
77     {
78         SFG_Window *saved_window = fgStructure.CurrentWindow;
79
80         INVOKE_WCB( *window, Reshape, ( width, height ) );
81
82         /*
83          * Force a window redraw.  In Windows at least this is only a partial
84          * solution:  if the window is increasing in size in either dimension,
85          * the already-drawn part does not get drawn again and things look funny.
86          * But without this we get this bad behaviour whenever we resize the
87          * window.
88          * DN: Hmm.. the above sounds like a concern only in single buffered mode...
89          */
90         window->State.WorkMask |= GLUT_DISPLAY_WORK;
91         if( window->IsMenu )
92             fgSetWindow( saved_window );
93     }
94 }
95
96 void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify)
97 {
98     GLboolean notify = GL_FALSE;
99
100     if( x  != window->State.Xpos ||
101         y != window->State.Ypos )
102     {
103         window->State.Xpos = x;
104         window->State.Ypos = y;
105
106         notify = GL_TRUE;
107     }
108
109     if (notify || forceNotify)
110     {
111         SFG_Window *saved_window = fgStructure.CurrentWindow;
112         INVOKE_WCB( *window, Position, ( x, y ) );
113         fgSetWindow( saved_window );
114     }
115 }
116
117 /*
118  * Calls a window's redraw method. This is used when
119  * a redraw is forced by the incoming window messages,
120  * or if a redisplay is otherwise pending.
121  * this is lean and mean without checks as it is
122  * currently only called from fghcbDisplayWindow which
123  * only calls this if the window is visible and needs
124  * a redisplay.
125  * Note that the fgSetWindow call on Windows makes the
126  * right device context current on windows, allowing
127  * direct drawing without BeginPaint/EndPaint in the
128  * WM_PAINT handler.
129  */
130 void fghRedrawWindow ( SFG_Window *window )
131 {
132     SFG_Window *current_window = fgStructure.CurrentWindow;
133
134     fgSetWindow( window );
135     INVOKE_WCB( *window, Display, ( ) );
136
137     fgSetWindow( current_window );
138 }
139
140 void fghRedrawWindowAndChildren ( SFG_Window *window )
141 {
142     SFG_Window* child;
143
144     fghRedrawWindow(window);
145
146     for( child = ( SFG_Window * )window->Children.First;
147          child;
148          child = ( SFG_Window * )child->Node.Next )
149     {
150         fghRedrawWindowAndChildren(child);
151     }
152 }
153
154
155 static void fghcbProcessWork( SFG_Window *window,
156                               SFG_Enumerator *enumerator )
157 {
158     if( window->State.WorkMask )
159                 fgProcessWork ( window );
160
161     fgEnumSubWindows( window, fghcbProcessWork, enumerator );
162 }
163
164 /*
165  * Make all windows process their work list
166  */
167 static void fghProcessWork( void )
168 {
169     SFG_Enumerator enumerator;
170
171     enumerator.found = GL_FALSE;
172     enumerator.data  =  NULL;
173
174     fgEnumWindows( fghcbProcessWork, &enumerator );
175 }
176
177 /*
178  * Window enumerator callback to check for the joystick polling code
179  */
180 static void fghcbCheckJoystickPolls( SFG_Window *window,
181                                      SFG_Enumerator *enumerator )
182 {
183     fg_time_t checkTime;
184     
185     if (window->State.JoystickPollRate > 0 && FETCH_WCB( *window, Joystick ))
186     {
187         /* This window has a joystick to be polled (if pollrate <= 0, user needs to poll manually with glutForceJoystickFunc */
188         checkTime= fgElapsedTime( );
189
190         if( window->State.JoystickLastPoll + window->State.JoystickPollRate <=
191             checkTime )
192         {
193 #if !defined(_WIN32_WCE)
194             fgJoystickPollWindow( window );
195 #endif /* !defined(_WIN32_WCE) */
196             window->State.JoystickLastPoll = checkTime;
197         }
198     }
199
200     fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );
201 }
202
203 /*
204  * Check all windows for joystick polling
205  * 
206  * The real way to do this is to make use of the glutTimer() API
207  * to more cleanly re-implement the joystick API.  Then, this code
208  * and all other "joystick timer" code can be yanked.
209  */
210 static void fghCheckJoystickPolls( void )
211 {
212     SFG_Enumerator enumerator;
213
214     enumerator.found = GL_FALSE;
215     enumerator.data  =  NULL;
216
217     fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );
218 }
219
220 /*
221  * Check the global timers
222  */
223 static void fghCheckTimers( void )
224 {
225     fg_time_t checkTime = fgElapsedTime( );
226
227     while( fgState.Timers.First )
228     {
229         SFG_Timer *timer = fgState.Timers.First;
230
231         if( timer->TriggerTime > checkTime )
232             /* Timers are sorted by triggerTime */
233             break;
234
235         fgListRemove( &fgState.Timers, &timer->Node );
236         fgListAppend( &fgState.FreeTimers, &timer->Node );
237
238         timer->Callback( timer->ID );
239     }
240 }
241
242  
243 /* Platform-dependent time in milliseconds, as an unsigned 64-bit integer.
244  * This doesn't overflow in any reasonable time, so no need to worry about
245  * that. The GLUT API return value will however overflow after 49.7 days,
246  * which means you will still get in trouble when running the
247  * application for more than 49.7 days.
248  */  
249 fg_time_t fgSystemTime(void)
250 {
251         return fgPlatformSystemTime();
252 }
253   
254 /*
255  * Elapsed Time
256  */
257 fg_time_t fgElapsedTime( void )
258 {
259     return fgSystemTime() - fgState.Time;
260 }
261
262 /*
263  * Error Messages.
264  */
265 void fgError( const char *fmt, ... )
266 {
267     va_list ap;
268
269     if (fgState.ErrorFunc) {
270
271         va_start( ap, fmt );
272
273         /* call user set error handler here */
274         fgState.ErrorFunc(fmt, ap);
275
276         va_end( ap );
277
278     } else {
279 #ifdef FREEGLUT_PRINT_ERRORS
280         va_start( ap, fmt );
281
282         fprintf( stderr, "freeglut ");
283         if( fgState.ProgramName )
284             fprintf( stderr, "(%s): ", fgState.ProgramName );
285         vfprintf( stderr, fmt, ap );
286         fprintf( stderr, "\n" );
287
288         va_end( ap );
289 #endif
290
291         if ( fgState.Initialised )
292             fgDeinitialize ();
293
294         exit( 1 );
295     }
296 }
297
298 void fgWarning( const char *fmt, ... )
299 {
300     va_list ap;
301
302     if (fgState.WarningFunc) {
303
304         va_start( ap, fmt );
305
306         /* call user set warning handler here */
307         fgState.WarningFunc(fmt, ap);
308
309         va_end( ap );
310
311     } else {
312 #ifdef FREEGLUT_PRINT_WARNINGS
313         va_start( ap, fmt );
314
315         fprintf( stderr, "freeglut ");
316         if( fgState.ProgramName )
317             fprintf( stderr, "(%s): ", fgState.ProgramName );
318         vfprintf( stderr, fmt, ap );
319         fprintf( stderr, "\n" );
320
321         va_end( ap );
322 #endif
323     }
324 }
325
326
327 /*
328  * Indicates whether work is pending for ANY window.
329  *
330  * The current mechanism is to walk all of the windows and ask if
331  * work is pending. We have a short-circuit early return if we find any.
332  */
333 static void fghHavePendingWorkCallback( SFG_Window* w, SFG_Enumerator* e)
334 {
335     if( w->State.WorkMask )
336     {
337         e->found = GL_TRUE;
338         e->data = w;
339         return;
340     }
341     fgEnumSubWindows( w, fghHavePendingWorkCallback, e );
342 }
343 static int fghHavePendingWork (void)
344 {
345     SFG_Enumerator enumerator;
346
347     enumerator.found = GL_FALSE;
348     enumerator.data = NULL;
349     fgEnumWindows( fghHavePendingWorkCallback, &enumerator );
350     return !!enumerator.data;
351 }
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;
359     SFG_Timer *timer = fgState.Timers.First;    /* timers are sorted by trigger time, so only have to check the first */
360
361     if( !timer )
362         return INT_MAX;
363
364     currentTime = fgElapsedTime();
365     if( timer->TriggerTime < currentTime )
366         return 0;
367     else
368         return timer->TriggerTime - currentTime;
369 }
370
371 static void fghSleepForEvents( void )
372 {
373     fg_time_t msec;
374
375     if( fghHavePendingWork( ) )
376         return;
377
378     msec = fghNextTimer( );
379     /* XXX Should use GLUT timers for joysticks... */
380     /* XXX Dumb; forces granularity to .01sec */
381     if( fgState.NumActiveJoysticks>0 && ( msec > 10 ) )     
382         msec = 10;
383
384         fgPlatformSleepForEvents ( msec );
385 }
386
387
388 /* Step through the work list */
389 void fgProcessWork(SFG_Window *window)
390 {
391     unsigned int workMask = window->State.WorkMask;
392     /* Now clear it so that any callback generated by the actions below can set work again */
393     window->State.WorkMask = 0;
394
395     if (workMask&~GLUT_DISPLAY_WORK)    /* Display work is the common case, skip all the below at once */
396     {
397         if (workMask & GLUT_INIT_WORK)
398         {
399             /* This is before the first display callback: if needed for the platform,
400              * call a few callbacks to inform user of window size, position, etc
401              */
402             fgPlatformInitWork(window);
403
404             /* Call init context callback */
405             INVOKE_WCB( *window, InitContext, ());
406
407             /* Lastly, check if we have a display callback, error out if not
408              * This is the right place to do it, as the redisplay will be
409              * next right after we exit this function, so there is no more
410              * opportunity for the user to register a callback for this window.
411              */
412             if (!FETCH_WCB(*window, Display))
413                 fgError ( "ERROR:  No display callback registered for window %d\n", window->ID );
414         }
415
416         /* On windows we can position, resize and change z order at the same time */
417         if (workMask & (GLUT_POSITION_WORK|GLUT_SIZE_WORK|GLUT_ZORDER_WORK|GLUT_FULL_SCREEN_WORK))
418         {
419             fgPlatformPosResZordWork(window,workMask);
420         }
421
422         if (workMask & GLUT_VISIBILITY_WORK)
423         {
424             fgPlatformVisibilityWork(window);
425         }
426     }
427
428     /* check window state's workmask as well as some of the above callbacks might have generated redisplay requests. We can deal with those right now instead of wait for the next mainloop iteration. */
429     if (workMask & GLUT_DISPLAY_WORK || window->State.WorkMask & GLUT_DISPLAY_WORK)
430     {
431         if( window->State.Visible )
432         {
433             /* Strip out display work from the work list */
434             /* NB: do this before the display callback is called as user might call postredisplay in his display callback */
435             window->State.WorkMask &= ~GLUT_DISPLAY_WORK;
436
437             fghRedrawWindow ( window );
438         }
439     }
440 }
441
442
443 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
444
445 /*
446  * Executes a single iteration in the freeglut processing loop.
447  */
448 void FGAPIENTRY glutMainLoopEvent( void )
449 {
450     /* Process input */
451         fgPlatformProcessSingleEvent ();
452
453     if( fgState.Timers.First )
454         fghCheckTimers( );
455     if (fgState.NumActiveJoysticks>0)   /* If zero, don't poll joysticks */
456         fghCheckJoystickPolls( );
457
458     /* Perform work on the window (position, reshape, display, etc) */
459     fghProcessWork( );
460
461     /* Check OpenGL error state if requested.
462      * Don't call if no more open windows (can happen if user closes window from
463      * title bar), would lead to infinite error loop in glutReportErrors
464      */
465     if (fgState.GLDebugSwitch && fgStructure.CurrentWindow)
466         glutReportErrors( );
467
468     fgCloseWindows( );
469 }
470
471 /*
472  * Enters the freeglut processing loop.
473  * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
474  */
475 void FGAPIENTRY glutMainLoop( void )
476 {
477     int action;
478
479     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoop" );
480
481     if (!fgStructure.Windows.First)
482         fgError(" ERROR:  glutMainLoop called with no windows created.");
483
484         fgPlatformMainLoopPreliminaryWork ();
485
486     fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
487     while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
488     {
489         SFG_Window *window;
490
491         glutMainLoopEvent( );
492         /*
493          * Step through the list of windows, seeing if there are any
494          * that are not menus
495          */
496         for( window = ( SFG_Window * )fgStructure.Windows.First;
497              window;
498              window = ( SFG_Window * )window->Node.Next )
499             if ( ! ( window->IsMenu ) )
500                 break;
501
502         if( ! window )
503             fgState.ExecState = GLUT_EXEC_STATE_STOP;
504         else
505         {
506             if( fgState.IdleCallback )
507             {
508                 if( fgStructure.CurrentWindow &&
509                     fgStructure.CurrentWindow->IsMenu )
510                     /* fail safe */
511                     fgSetWindow( window );
512                 fgState.IdleCallback( );
513             }
514             else
515                 fghSleepForEvents( );
516         }
517     }
518
519     /*
520      * When this loop terminates, destroy the display, state and structure
521      * of a freeglut session, so that another glutInit() call can happen
522      *
523      * Save the "ActionOnWindowClose" because "fgDeinitialize" resets it.
524      */
525     action = fgState.ActionOnWindowClose;
526     fgDeinitialize( );
527     if( action == GLUT_ACTION_EXIT )
528         exit( 0 );
529 }
530
531 /*
532  * Leaves the freeglut processing loop.
533  */
534 void FGAPIENTRY glutLeaveMainLoop( void )
535 {
536     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveMainLoop" );
537     fgState.ExecState = GLUT_EXEC_STATE_STOP ;
538 }
539
540
541
542 /*** END OF FILE ***/