763783c39a29f18445476389041475b5b8e43cae
[freeglut] / src / Common / freeglut_main.c
1 /*\r
2  * freeglut_main.c\r
3  *\r
4  * The windows message processing methods.\r
5  *\r
6  * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.\r
7  * Written by Pawel W. Olszta, <olszta@sourceforge.net>\r
8  * Creation date: Fri Dec 3 1999\r
9  *\r
10  * Permission is hereby granted, free of charge, to any person obtaining a\r
11  * copy of this software and associated documentation files (the "Software"),\r
12  * to deal in the Software without restriction, including without limitation\r
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
14  * and/or sell copies of the Software, and to permit persons to whom the\r
15  * Software is furnished to do so, subject to the following conditions:\r
16  *\r
17  * The above copyright notice and this permission notice shall be included\r
18  * in all copies or substantial portions of the Software.\r
19  *\r
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS\r
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL\r
23  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
24  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
26  */\r
27 \r
28 #include <GL/freeglut.h>\r
29 #include "freeglut_internal.h"\r
30 #ifdef HAVE_ERRNO_H\r
31 #    include <errno.h>\r
32 #endif\r
33 #include <stdarg.h>\r
34 #ifdef  HAVE_VFPRINTF\r
35 #    define VFPRINTF(s,f,a) vfprintf((s),(f),(a))\r
36 #elif defined(HAVE__DOPRNT)\r
37 #    define VFPRINTF(s,f,a) _doprnt((f),(a),(s))\r
38 #else\r
39 #    define VFPRINTF(s,f,a)\r
40 #endif\r
41 \r
42 #ifdef _WIN32_WCE\r
43 \r
44 typedef struct GXDisplayProperties GXDisplayProperties;\r
45 typedef struct GXKeyList GXKeyList;\r
46 #include <gx.h>\r
47 \r
48 typedef struct GXKeyList (*GXGETDEFAULTKEYS)(int);\r
49 typedef int (*GXOPENINPUT)();\r
50 \r
51 GXGETDEFAULTKEYS GXGetDefaultKeys_ = NULL;\r
52 GXOPENINPUT GXOpenInput_ = NULL;\r
53 \r
54 struct GXKeyList gxKeyList;\r
55 \r
56 #endif /* _WIN32_WCE */\r
57 \r
58 /*\r
59  * Try to get the maximum value allowed for ints, falling back to the minimum\r
60  * guaranteed by ISO C99 if there is no suitable header.\r
61  */\r
62 #ifdef HAVE_LIMITS_H\r
63 #    include <limits.h>\r
64 #endif\r
65 #ifndef INT_MAX\r
66 #    define INT_MAX 32767\r
67 #endif\r
68 \r
69 #ifndef MIN\r
70 #    define MIN(a,b) (((a)<(b)) ? (a) : (b))\r
71 #endif\r
72 \r
73 extern void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height );\r
74 extern void fgPlatformDisplayWindow ( SFG_Window *window );\r
75 extern unsigned long fgPlatformSystemTime ( void );\r
76 extern void fgPlatformSleepForEvents( long msec );\r
77 extern void fgPlatformProcessSingleEvent ( void );\r
78 extern void fgPlatformMainLoopPreliminaryWork ( void );\r
79 \r
80 \r
81 \r
82 \r
83 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */\r
84 \r
85 static void fghReshapeWindow ( SFG_Window *window, int width, int height )\r
86 {\r
87     SFG_Window *current_window = fgStructure.CurrentWindow;\r
88 \r
89     freeglut_return_if_fail( window != NULL );\r
90 \r
91         fgPlatformReshapeWindow ( window, width, height );\r
92 \r
93     if( FETCH_WCB( *window, Reshape ) )\r
94         INVOKE_WCB( *window, Reshape, ( width, height ) );\r
95     else\r
96     {\r
97         fgSetWindow( window );\r
98         glViewport( 0, 0, width, height );\r
99     }\r
100 \r
101     /*\r
102      * Force a window redraw.  In Windows at least this is only a partial\r
103      * solution:  if the window is increasing in size in either dimension,\r
104      * the already-drawn part does not get drawn again and things look funny.\r
105      * But without this we get this bad behaviour whenever we resize the\r
106      * window.\r
107      */\r
108     window->State.Redisplay = GL_TRUE;\r
109 \r
110     if( window->IsMenu )\r
111         fgSetWindow( current_window );\r
112 }\r
113 \r
114 /*\r
115  * Calls a window's redraw method. This is used when\r
116  * a redraw is forced by the incoming window messages.\r
117  */\r
118 void fghRedrawWindow ( SFG_Window *window )\r
119 {\r
120     SFG_Window *current_window = fgStructure.CurrentWindow;\r
121 \r
122     freeglut_return_if_fail( window );\r
123     freeglut_return_if_fail( FETCH_WCB ( *window, Display ) );\r
124 \r
125     window->State.Redisplay = GL_FALSE;\r
126 \r
127     freeglut_return_if_fail( window->State.Visible );\r
128 \r
129     fgSetWindow( window );\r
130 \r
131     if( window->State.NeedToResize )\r
132     {\r
133         fghReshapeWindow(\r
134             window,\r
135             window->State.Width,\r
136             window->State.Height\r
137         );\r
138 \r
139         window->State.NeedToResize = GL_FALSE;\r
140     }\r
141 \r
142     INVOKE_WCB( *window, Display, ( ) );\r
143 \r
144     fgSetWindow( current_window );\r
145 }\r
146 \r
147 \r
148 static void fghcbDisplayWindow( SFG_Window *window,\r
149                                 SFG_Enumerator *enumerator )\r
150 {\r
151     if( window->State.Redisplay &&\r
152         window->State.Visible )\r
153     {\r
154         window->State.Redisplay = GL_FALSE;\r
155                 fgPlatformDisplayWindow ( window );\r
156     }\r
157 \r
158     fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );\r
159 }\r
160 \r
161 /*\r
162  * Make all windows perform a display call\r
163  */\r
164 static void fghDisplayAll( void )\r
165 {\r
166     SFG_Enumerator enumerator;\r
167 \r
168     enumerator.found = GL_FALSE;\r
169     enumerator.data  =  NULL;\r
170 \r
171     fgEnumWindows( fghcbDisplayWindow, &enumerator );\r
172 }\r
173 \r
174 /*\r
175  * Window enumerator callback to check for the joystick polling code\r
176  */\r
177 static void fghcbCheckJoystickPolls( SFG_Window *window,\r
178                                      SFG_Enumerator *enumerator )\r
179 {\r
180     long int checkTime = fgElapsedTime( );\r
181 \r
182     if( window->State.JoystickLastPoll + window->State.JoystickPollRate <=\r
183         checkTime )\r
184     {\r
185 #if !defined(_WIN32_WCE)\r
186         fgJoystickPollWindow( window );\r
187 #endif /* !defined(_WIN32_WCE) */\r
188         window->State.JoystickLastPoll = checkTime;\r
189     }\r
190 \r
191     fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );\r
192 }\r
193 \r
194 /*\r
195  * Check all windows for joystick polling\r
196  */\r
197 static void fghCheckJoystickPolls( void )\r
198 {\r
199     SFG_Enumerator enumerator;\r
200 \r
201     enumerator.found = GL_FALSE;\r
202     enumerator.data  =  NULL;\r
203 \r
204     fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );\r
205 }\r
206 \r
207 /*\r
208  * Check the global timers\r
209  */\r
210 static void fghCheckTimers( void )\r
211 {\r
212     long checkTime = fgElapsedTime( );\r
213 \r
214     while( fgState.Timers.First )\r
215     {\r
216         SFG_Timer *timer = fgState.Timers.First;\r
217 \r
218         if( timer->TriggerTime > checkTime )\r
219             break;\r
220 \r
221         fgListRemove( &fgState.Timers, &timer->Node );\r
222         fgListAppend( &fgState.FreeTimers, &timer->Node );\r
223 \r
224         timer->Callback( timer->ID );\r
225     }\r
226 }\r
227 \r
228  \r
229 /* Platform-dependent time in milliseconds, as an unsigned 32-bit integer.\r
230  * This value wraps every 49.7 days, but integer overflows cancel\r
231  * when subtracting an initial start time, unless the total time exceeds\r
232  * 32-bit, where the GLUT API return value is also overflowed.\r
233  */  \r
234 unsigned long fgSystemTime(void)\r
235 {\r
236         return fgPlatformSystemTime ();\r
237 }\r
238   \r
239 /*\r
240  * Elapsed Time\r
241  */\r
242 long fgElapsedTime( void )\r
243 {\r
244     return (long) (fgSystemTime() - fgState.Time);\r
245 }\r
246 \r
247 /*\r
248  * Error Messages.\r
249  */\r
250 void fgError( const char *fmt, ... )\r
251 {\r
252     va_list ap;\r
253 \r
254     if (fgState.ErrorFunc) {\r
255 \r
256         va_start( ap, fmt );\r
257 \r
258         /* call user set error handler here */\r
259         fgState.ErrorFunc(fmt, ap);\r
260 \r
261         va_end( ap );\r
262 \r
263     } else {\r
264 \r
265         va_start( ap, fmt );\r
266 \r
267         fprintf( stderr, "freeglut ");\r
268         if( fgState.ProgramName )\r
269             fprintf( stderr, "(%s): ", fgState.ProgramName );\r
270         VFPRINTF( stderr, fmt, ap );\r
271         fprintf( stderr, "\n" );\r
272 \r
273         va_end( ap );\r
274 \r
275         if ( fgState.Initialised )\r
276             fgDeinitialize ();\r
277 \r
278         exit( 1 );\r
279     }\r
280 }\r
281 \r
282 void fgWarning( const char *fmt, ... )\r
283 {\r
284     va_list ap;\r
285 \r
286     if (fgState.WarningFunc) {\r
287 \r
288         va_start( ap, fmt );\r
289 \r
290         /* call user set warning handler here */\r
291         fgState.WarningFunc(fmt, ap);\r
292 \r
293         va_end( ap );\r
294 \r
295     } else {\r
296 \r
297         va_start( ap, fmt );\r
298 \r
299         fprintf( stderr, "freeglut ");\r
300         if( fgState.ProgramName )\r
301             fprintf( stderr, "(%s): ", fgState.ProgramName );\r
302         VFPRINTF( stderr, fmt, ap );\r
303         fprintf( stderr, "\n" );\r
304 \r
305         va_end( ap );\r
306     }\r
307 }\r
308 \r
309 \r
310 /*\r
311  * Indicates whether Joystick events are being used by ANY window.\r
312  *\r
313  * The current mechanism is to walk all of the windows and ask if\r
314  * there is a joystick callback.  We have a short-circuit early\r
315  * return if we find any joystick handler registered.\r
316  *\r
317  * The real way to do this is to make use of the glutTimer() API\r
318  * to more cleanly re-implement the joystick API.  Then, this code\r
319  * and all other "joystick timer" code can be yanked.\r
320  *\r
321  */\r
322 static void fghCheckJoystickCallback( SFG_Window* w, SFG_Enumerator* e)\r
323 {\r
324     if( FETCH_WCB( *w, Joystick ) )\r
325     {\r
326         e->found = GL_TRUE;\r
327         e->data = w;\r
328     }\r
329     fgEnumSubWindows( w, fghCheckJoystickCallback, e );\r
330 }\r
331 static int fghHaveJoystick( void )\r
332 {\r
333     SFG_Enumerator enumerator;\r
334 \r
335     enumerator.found = GL_FALSE;\r
336     enumerator.data = NULL;\r
337     fgEnumWindows( fghCheckJoystickCallback, &enumerator );\r
338     return !!enumerator.data;\r
339 }\r
340 static void fghHavePendingRedisplaysCallback( SFG_Window* w, SFG_Enumerator* e)\r
341 {\r
342     if( w->State.Redisplay && w->State.Visible )\r
343     {\r
344         e->found = GL_TRUE;\r
345         e->data = w;\r
346     }\r
347     fgEnumSubWindows( w, fghHavePendingRedisplaysCallback, e );\r
348 }\r
349 static int fghHavePendingRedisplays (void)\r
350 {\r
351     SFG_Enumerator enumerator;\r
352 \r
353     enumerator.found = GL_FALSE;\r
354     enumerator.data = NULL;\r
355     fgEnumWindows( fghHavePendingRedisplaysCallback, &enumerator );\r
356     return !!enumerator.data;\r
357 }\r
358 /*\r
359  * Returns the number of GLUT ticks (milliseconds) till the next timer event.\r
360  */\r
361 static long fghNextTimer( void )\r
362 {\r
363     long ret = INT_MAX;\r
364     SFG_Timer *timer = fgState.Timers.First;\r
365 \r
366     if( timer )\r
367         ret = timer->TriggerTime - fgElapsedTime();\r
368     if( ret < 0 )\r
369         ret = 0;\r
370 \r
371     return ret;\r
372 }\r
373 \r
374 static void fghSleepForEvents( void )\r
375 {\r
376     long msec;\r
377 \r
378     if( fgState.IdleCallback || fghHavePendingRedisplays( ) )\r
379         return;\r
380 \r
381     msec = fghNextTimer( );\r
382     /* XXX Use GLUT timers for joysticks... */\r
383     /* XXX Dumb; forces granularity to .01sec */\r
384     if( fghHaveJoystick( ) && ( msec > 10 ) )     \r
385         msec = 10;\r
386 \r
387         fgPlatformSleepForEvents ( msec );\r
388 }\r
389 \r
390 \r
391 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */\r
392 \r
393 /*\r
394  * Executes a single iteration in the freeglut processing loop.\r
395  */\r
396 void FGAPIENTRY glutMainLoopEvent( void )\r
397 {\r
398         fgPlatformProcessSingleEvent ();\r
399 \r
400     if( fgState.Timers.First )\r
401         fghCheckTimers( );\r
402     fghCheckJoystickPolls( );\r
403     fghDisplayAll( );\r
404 \r
405     fgCloseWindows( );\r
406 }\r
407 \r
408 /*\r
409  * Enters the freeglut processing loop.\r
410  * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".\r
411  */\r
412 void FGAPIENTRY glutMainLoop( void )\r
413 {\r
414     int action;\r
415 \r
416     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoop" );\r
417 \r
418         fgPlatformMainLoopPreliminaryWork ();\r
419 \r
420     fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;\r
421     while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )\r
422     {\r
423         SFG_Window *window;\r
424 \r
425         glutMainLoopEvent( );\r
426         /*\r
427          * Step through the list of windows, seeing if there are any\r
428          * that are not menus\r
429          */\r
430         for( window = ( SFG_Window * )fgStructure.Windows.First;\r
431              window;\r
432              window = ( SFG_Window * )window->Node.Next )\r
433             if ( ! ( window->IsMenu ) )\r
434                 break;\r
435 \r
436         if( ! window )\r
437             fgState.ExecState = GLUT_EXEC_STATE_STOP;\r
438         else\r
439         {\r
440             if( fgState.IdleCallback )\r
441             {\r
442                 if( fgStructure.CurrentWindow &&\r
443                     fgStructure.CurrentWindow->IsMenu )\r
444                     /* fail safe */\r
445                     fgSetWindow( window );\r
446                 fgState.IdleCallback( );\r
447             }\r
448 \r
449             fghSleepForEvents( );\r
450         }\r
451     }\r
452 \r
453     /*\r
454      * When this loop terminates, destroy the display, state and structure\r
455      * of a freeglut session, so that another glutInit() call can happen\r
456      *\r
457      * Save the "ActionOnWindowClose" because "fgDeinitialize" resets it.\r
458      */\r
459     action = fgState.ActionOnWindowClose;\r
460     fgDeinitialize( );\r
461     if( action == GLUT_ACTION_EXIT )\r
462         exit( 0 );\r
463 }\r
464 \r
465 /*\r
466  * Leaves the freeglut processing loop.\r
467  */\r
468 void FGAPIENTRY glutLeaveMainLoop( void )\r
469 {\r
470     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveMainLoop" );\r
471     fgState.ExecState = GLUT_EXEC_STATE_STOP ;\r
472 }\r
473 \r
474 \r
475 \r
476 /*** END OF FILE ***/\r