can now set the font used for a menu
[freeglut] / src / fg_init.c
1 /*
2  * freeglut_init.c
3  *
4  * Various freeglut initialization functions.
5  *
6  * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7  * Written by Pawel W. Olszta, <olszta@sourceforge.net>
8  * Creation date: Thu Dec 2 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 #define FREEGLUT_BUILDING_LIB
29 #include <GL/freeglut.h>
30 #include "fg_internal.h"
31
32 /*
33  * TODO BEFORE THE STABLE RELEASE:
34  *
35  *  fgDeinitialize()        -- Win32's OK, X11 needs the OS-specific
36  *                             deinitialization done
37  *  glutInitDisplayString() -- display mode string parsing
38  *
39  * Wouldn't it be cool to use gettext() for error messages? I just love
40  * bash saying  "nie znaleziono pliku" instead of "file not found" :)
41  * Is gettext easily portable?
42  */
43
44 /* -- GLOBAL VARIABLES ----------------------------------------------------- */
45
46 /*
47  * A structure pointed by fgDisplay holds all information
48  * regarding the display, screen, root window etc.
49  */
50 SFG_Display fgDisplay;
51
52 /*
53  * The settings for the current freeglut session
54  */
55 SFG_State fgState = { { -1, -1, GL_FALSE },  /* Position */
56                       { 300, 300, GL_TRUE }, /* Size */
57                       GLUT_RGBA | GLUT_SINGLE | GLUT_DEPTH,  /* DisplayMode */
58                       GL_FALSE,              /* Initialised */
59                       GLUT_TRY_DIRECT_CONTEXT,  /* DirectContext */
60                       GL_FALSE,              /* ForceIconic */
61                       GL_FALSE,              /* UseCurrentContext */
62                       GL_FALSE,              /* GLDebugSwitch */
63                       GL_FALSE,              /* XSyncSwitch */
64                       GLUT_KEY_REPEAT_ON,    /* KeyRepeat */
65                       INVALID_MODIFIERS,     /* Modifiers */
66                       0,                     /* FPSInterval */
67                       0,                     /* SwapCount */
68                       0,                     /* SwapTime */
69                       0,                     /* Time */
70                       { NULL, NULL },         /* Timers */
71                       { NULL, NULL },         /* FreeTimers */
72                       NULL,                   /* IdleCallback */
73                       0,                      /* ActiveMenus */
74                       NULL,                   /* MenuStateCallback */
75                       NULL,                   /* MenuStatusCallback */
76                       FREEGLUT_MENU_FONT,
77                       { -1, -1, GL_TRUE },    /* GameModeSize */
78                       -1,                     /* GameModeDepth */
79                       -1,                     /* GameModeRefresh */
80                       GLUT_ACTION_EXIT,       /* ActionOnWindowClose */
81                       GLUT_EXEC_STATE_INIT,   /* ExecState */
82                       NULL,                   /* ProgramName */
83                       GL_FALSE,               /* JoysticksInitialised */
84                       0,                      /* NumActiveJoysticks */
85                       GL_FALSE,               /* InputDevsInitialised */
86                       0,                      /* MouseWheelTicks */
87                       1,                      /* AuxiliaryBufferNumber */
88                       4,                      /* SampleNumber */
89                       GL_FALSE,               /* SkipStaleMotion */
90                       1,                      /* OpenGL context MajorVersion */
91                       0,                      /* OpenGL context MinorVersion */
92                       0,                      /* OpenGL ContextFlags */
93                       0,                      /* OpenGL ContextProfile */
94                       0,                      /* HasOpenGL20 */
95                       NULL,                   /* ErrorFunc */
96                       NULL                    /* WarningFunc */
97 };
98
99
100 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
101
102 extern void fgPlatformInitialize( const char* displayName );
103 extern void fgPlatformDeinitialiseInputDevices ( void );
104 extern void fgPlatformCloseDisplay ( void );
105 extern void fgPlatformDestroyContext ( SFG_PlatformDisplay pDisplay, SFG_WindowContextType MContext );
106
107 void fghParseCommandLineArguments ( int* pargc, char** argv, char **pDisplayName, char **pGeometry )
108 {
109 #ifndef _WIN32_WCE
110     int i, j, argc = *pargc;
111
112     {
113             /* check if GLUT_FPS env var is set */
114         const char *fps = getenv( "GLUT_FPS" );
115
116         if( fps )
117         {
118             int interval;
119             sscanf( fps, "%d", &interval );
120
121             if( interval <= 0 )
122                 fgState.FPSInterval = 5000;  /* 5000 millisecond default */
123             else
124                 fgState.FPSInterval = interval;
125         }
126     }
127
128     *pDisplayName = getenv( "DISPLAY" );
129
130     for( i = 1; i < argc; i++ )
131     {
132         if( strcmp( argv[ i ], "-display" ) == 0 )
133         {
134             if( ++i >= argc )
135                 fgError( "-display parameter must be followed by display name" );
136
137             *pDisplayName = argv[ i ];
138
139             argv[ i - 1 ] = NULL;
140             argv[ i     ] = NULL;
141             ( *pargc ) -= 2;
142         }
143         else if( strcmp( argv[ i ], "-geometry" ) == 0 )
144         {
145             if( ++i >= argc )
146                 fgError( "-geometry parameter must be followed by window "
147                          "geometry settings" );
148
149             *pGeometry = argv[ i ];
150
151             argv[ i - 1 ] = NULL;
152             argv[ i     ] = NULL;
153             ( *pargc ) -= 2;
154         }
155         else if( strcmp( argv[ i ], "-direct" ) == 0)
156         {
157             if( fgState.DirectContext == GLUT_FORCE_INDIRECT_CONTEXT )
158                 fgError( "parameters ambiguity, -direct and -indirect "
159                     "cannot be both specified" );
160
161             fgState.DirectContext = GLUT_FORCE_DIRECT_CONTEXT;
162             argv[ i ] = NULL;
163             ( *pargc )--;
164         }
165         else if( strcmp( argv[ i ], "-indirect" ) == 0 )
166         {
167             if( fgState.DirectContext == GLUT_FORCE_DIRECT_CONTEXT )
168                 fgError( "parameters ambiguity, -direct and -indirect "
169                     "cannot be both specified" );
170
171             fgState.DirectContext = GLUT_FORCE_INDIRECT_CONTEXT;
172             argv[ i ] = NULL;
173             (*pargc)--;
174         }
175         else if( strcmp( argv[ i ], "-iconic" ) == 0 )
176         {
177             fgState.ForceIconic = GL_TRUE;
178             argv[ i ] = NULL;
179             ( *pargc )--;
180         }
181         else if( strcmp( argv[ i ], "-gldebug" ) == 0 )
182         {
183             fgState.GLDebugSwitch = GL_TRUE;
184             argv[ i ] = NULL;
185             ( *pargc )--;
186         }
187         else if( strcmp( argv[ i ], "-sync" ) == 0 )
188         {
189             fgState.XSyncSwitch = GL_TRUE;
190             argv[ i ] = NULL;
191             ( *pargc )--;
192         }
193     }
194
195     /* Compact {argv}. */
196     for( i = j = 1; i < *pargc; i++, j++ )
197     {
198         /* Guaranteed to end because there are "*pargc" arguments left */
199         while ( argv[ j ] == NULL )
200             j++;
201         if ( i != j )
202             argv[ i ] = argv[ j ];
203     }
204
205 #endif /* _WIN32_WCE */
206
207 }
208
209
210 void fghCloseInputDevices ( void )
211 {
212     if ( fgState.JoysticksInitialised )
213         fgJoystickClose( );
214
215     if ( fgState.InputDevsInitialised )
216         fgInputDeviceClose( );
217 }
218
219
220 /*
221  * Perform the freeglut deinitialization...
222  */
223 void fgDeinitialize( void )
224 {
225     SFG_Timer *timer;
226
227     if( !fgState.Initialised )
228     {
229         return;
230     }
231
232         /* If we're in game mode, we want to leave game mode */
233     if( fgStructure.GameModeWindow ) {
234         glutLeaveGameMode();
235     }
236
237     /* If there was a menu created, destroy the rendering context */
238     if( fgStructure.MenuContext )
239     {
240                 fgPlatformDestroyContext (fgDisplay.pDisplay, fgStructure.MenuContext->MContext );
241         free( fgStructure.MenuContext );
242         fgStructure.MenuContext = NULL;
243     }
244
245     fgDestroyStructure( );
246
247     while( ( timer = fgState.Timers.First) )
248     {
249         fgListRemove( &fgState.Timers, &timer->Node );
250         free( timer );
251     }
252
253     while( ( timer = fgState.FreeTimers.First) )
254     {
255         fgListRemove( &fgState.FreeTimers, &timer->Node );
256         free( timer );
257     }
258
259         fgPlatformDeinitialiseInputDevices ();
260
261         fgState.MouseWheelTicks = 0;
262
263     fgState.MajorVersion = 1;
264     fgState.MinorVersion = 0;
265     fgState.ContextFlags = 0;
266     fgState.ContextProfile = 0;
267
268     fgState.Initialised = GL_FALSE;
269
270     fgState.Position.X = -1;
271     fgState.Position.Y = -1;
272     fgState.Position.Use = GL_FALSE;
273
274     fgState.Size.X = 300;
275     fgState.Size.Y = 300;
276     fgState.Size.Use = GL_TRUE;
277
278     fgState.DisplayMode = GLUT_RGBA | GLUT_SINGLE | GLUT_DEPTH;
279
280     fgState.DirectContext  = GLUT_TRY_DIRECT_CONTEXT;
281     fgState.ForceIconic         = GL_FALSE;
282     fgState.UseCurrentContext   = GL_FALSE;
283     fgState.GLDebugSwitch       = GL_FALSE;
284     fgState.XSyncSwitch         = GL_FALSE;
285     fgState.ActionOnWindowClose = GLUT_ACTION_EXIT;
286     fgState.ExecState           = GLUT_EXEC_STATE_INIT;
287
288     fgState.KeyRepeat       = GLUT_KEY_REPEAT_ON;
289     fgState.Modifiers       = INVALID_MODIFIERS;
290
291     fgState.GameModeSize.X  = -1;
292     fgState.GameModeSize.Y  = -1;
293     fgState.GameModeDepth   = -1;
294     fgState.GameModeRefresh = -1;
295
296     fgListInit( &fgState.Timers );
297     fgListInit( &fgState.FreeTimers );
298
299     fgState.IdleCallback = NULL;
300     fgState.MenuStateCallback = ( FGCBMenuState )NULL;
301     fgState.MenuStatusCallback = ( FGCBMenuStatus )NULL;
302
303     fgState.SwapCount   = 0;
304     fgState.SwapTime    = 0;
305     fgState.FPSInterval = 0;
306
307     if( fgState.ProgramName )
308     {
309         free( fgState.ProgramName );
310         fgState.ProgramName = NULL;
311     }
312
313         fgPlatformCloseDisplay ();
314
315     fgState.Initialised = GL_FALSE;
316 }
317
318
319 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
320 #if defined(NEED_XPARSEGEOMETRY_IMPL)
321 #   include "util/xparsegeometry_repl.h"
322 #endif
323
324 /*
325  * Perform initialization. This usually happens on the program startup
326  * and restarting after glutMainLoop termination...
327  */
328 void FGAPIENTRY glutInit( int* pargc, char** argv )
329 {
330     char* displayName = NULL;
331     char* geometry = NULL;
332     if( fgState.Initialised )
333         fgError( "illegal glutInit() reinitialization attempt" );
334
335     if (pargc && *pargc && argv && *argv && **argv)
336     {
337         fgState.ProgramName = strdup (*argv);
338
339         if( !fgState.ProgramName )
340             fgError ("Could not allocate space for the program's name.");
341     }
342
343     fgCreateStructure( );
344
345         fghParseCommandLineArguments ( pargc, argv, &displayName, &geometry );
346
347     /*
348      * Have the display created now. If there wasn't a "-display"
349      * in the program arguments, we will use the DISPLAY environment
350      * variable for opening the X display (see code above):
351      */
352     fgPlatformInitialize( displayName );
353
354     /*
355      * Geometry parsing deferred until here because we may need the screen
356      * size.
357      */
358
359     if (geometry )
360     {
361         unsigned int parsedWidth, parsedHeight;
362         int mask = XParseGeometry( geometry,
363                                    &fgState.Position.X, &fgState.Position.Y,
364                                    &parsedWidth, &parsedHeight );
365         /* TODO: Check for overflow? */
366         fgState.Size.X = parsedWidth;
367         fgState.Size.Y = parsedHeight;
368
369         if( (mask & (WidthValue|HeightValue)) == (WidthValue|HeightValue) )
370             fgState.Size.Use = GL_TRUE;
371
372         if( mask & XNegative )
373             fgState.Position.X += fgDisplay.ScreenWidth - fgState.Size.X;
374
375         if( mask & YNegative )
376             fgState.Position.Y += fgDisplay.ScreenHeight - fgState.Size.Y;
377
378         if( (mask & (XValue|YValue)) == (XValue|YValue) )
379             fgState.Position.Use = GL_TRUE;
380     }
381 }
382
383 /*
384  * Undoes all the "glutInit" stuff
385  */
386 void FGAPIENTRY glutExit ( void )
387 {
388   fgDeinitialize ();
389 }
390
391 /*
392  * Sets the default initial window position for new windows
393  */
394 void FGAPIENTRY glutInitWindowPosition( int x, int y )
395 {
396     fgState.Position.X = x;
397     fgState.Position.Y = y;
398
399     if( ( x >= 0 ) && ( y >= 0 ) )
400         fgState.Position.Use = GL_TRUE;
401     else
402         fgState.Position.Use = GL_FALSE;
403 }
404
405 /*
406  * Sets the default initial window size for new windows
407  */
408 void FGAPIENTRY glutInitWindowSize( int width, int height )
409 {
410     fgState.Size.X = width;
411     fgState.Size.Y = height;
412
413     if( ( width > 0 ) && ( height > 0 ) )
414         fgState.Size.Use = GL_TRUE;
415     else
416         fgState.Size.Use = GL_FALSE;
417 }
418
419 /*
420  * Sets the default display mode for all new windows
421  */
422 void FGAPIENTRY glutInitDisplayMode( unsigned int displayMode )
423 {
424     /* We will make use of this value when creating a new OpenGL context... */
425     fgState.DisplayMode = displayMode;
426 }
427
428
429 /* -- INIT DISPLAY STRING PARSING ------------------------------------------ */
430
431 static char* Tokens[] =
432 {
433     "alpha", "acca", "acc", "blue", "buffer", "conformant", "depth", "double",
434     "green", "index", "num", "red", "rgba", "rgb", "luminance", "stencil",
435     "single", "stereo", "samples", "slow", "win32pdf", "win32pfd", "xvisual",
436     "xstaticgray", "xgrayscale", "xstaticcolor", "xpseudocolor",
437     "xtruecolor", "xdirectcolor",
438     "xstaticgrey", "xgreyscale", "xstaticcolour", "xpseudocolour",
439     "xtruecolour", "xdirectcolour", "borderless", "aux"
440 };
441 #define NUM_TOKENS             (sizeof(Tokens) / sizeof(*Tokens))
442
443 void FGAPIENTRY glutInitDisplayString( const char* displayMode )
444 {
445     int glut_state_flag = 0 ;
446     /*
447      * Unpack a lot of options from a character string.  The options are
448      * delimited by blanks or tabs.
449      */
450     char *token ;
451     size_t len = strlen ( displayMode );
452     char *buffer = (char *)malloc ( (len+1) * sizeof(char) );
453     memcpy ( buffer, displayMode, len );
454     buffer[len] = '\0';
455
456     token = strtok ( buffer, " \t" );
457
458     while ( token )
459     {
460         /* Process this token */
461         int i ;
462
463         /* Temporary fix:  Ignore any length specifications and at least
464          * process the basic token
465          * TODO:  Fix this permanently
466          */
467         size_t cleanlength = strcspn ( token, "=<>~!" );
468
469         for ( i = 0; i < NUM_TOKENS; i++ )
470         {
471             if ( strncmp ( token, Tokens[i], cleanlength ) == 0 ) break ;
472         }
473
474         switch ( i )
475         {
476         case 0 :  /* "alpha":  Alpha color buffer precision in bits */
477             glut_state_flag |= GLUT_ALPHA ;  /* Somebody fix this for me! */
478             break ;
479
480         case 1 :  /* "acca":  Red, green, blue, and alpha accumulation buffer
481                      precision in bits */
482             break ;
483
484         case 2 :  /* "acc":  Red, green, and blue accumulation buffer precision
485                      in bits with zero bits alpha */
486             glut_state_flag |= GLUT_ACCUM ;  /* Somebody fix this for me! */
487             break ;
488
489         case 3 :  /* "blue":  Blue color buffer precision in bits */
490             break ;
491
492         case 4 :  /* "buffer":  Number of bits in the color index color buffer
493                    */
494             break ;
495
496         case 5 :  /* "conformant":  Boolean indicating if the frame buffer
497                      configuration is conformant or not */
498             break ;
499
500         case 6 : /* "depth":  Number of bits of precision in the depth buffer */
501             glut_state_flag |= GLUT_DEPTH ;  /* Somebody fix this for me! */
502             break ;
503
504         case 7 :  /* "double":  Boolean indicating if the color buffer is
505                      double buffered */
506             glut_state_flag |= GLUT_DOUBLE ;
507             break ;
508
509         case 8 :  /* "green":  Green color buffer precision in bits */
510             break ;
511
512         case 9 :  /* "index":  Boolean if the color model is color index or not
513                    */
514             glut_state_flag |= GLUT_INDEX ;
515             break ;
516
517         case 10 :  /* "num":  A special capability  name indicating where the
518                       value represents the Nth frame buffer configuration
519                       matching the description string */
520             break ;
521
522         case 11 :  /* "red":  Red color buffer precision in bits */
523             break ;
524
525         case 12 :  /* "rgba":  Number of bits of red, green, blue, and alpha in
526                       the RGBA color buffer */
527             glut_state_flag |= GLUT_RGBA ;  /* Somebody fix this for me! */
528             break ;
529
530         case 13 :  /* "rgb":  Number of bits of red, green, and blue in the
531                       RGBA color buffer with zero bits alpha */
532             glut_state_flag |= GLUT_RGB ;  /* Somebody fix this for me! */
533             break ;
534
535         case 14 :  /* "luminance":  Number of bits of red in the RGBA and zero
536                       bits of green, blue (alpha not specified) of color buffer
537                       precision */
538             glut_state_flag |= GLUT_LUMINANCE ; /* Somebody fix this for me! */
539             break ;
540
541         case 15 :  /* "stencil":  Number of bits in the stencil buffer */
542             glut_state_flag |= GLUT_STENCIL;  /* Somebody fix this for me! */
543             break ;
544
545         case 16 :  /* "single":  Boolean indicate the color buffer is single
546                       buffered */
547             glut_state_flag |= GLUT_SINGLE ;
548             break ;
549
550         case 17 :  /* "stereo":  Boolean indicating the color buffer supports
551                       OpenGL-style stereo */
552             glut_state_flag |= GLUT_STEREO ;
553             break ;
554
555         case 18 :  /* "samples":  Indicates the number of multisamples to use
556                       based on GLX's SGIS_multisample extension (for
557                       antialiasing) */
558             glut_state_flag |= GLUT_MULTISAMPLE ; /*Somebody fix this for me!*/
559             break ;
560
561         case 19 :  /* "slow":  Boolean indicating if the frame buffer
562                       configuration is slow or not */
563             break ;
564
565         case 20 :  /* "win32pdf": (incorrect spelling but was there before */
566         case 21 :  /* "win32pfd":  matches the Win32 Pixel Format Descriptor by
567                       number */
568 #if TARGET_HOST_MS_WINDOWS
569 #endif
570             break ;
571
572         case 22 :  /* "xvisual":  matches the X visual ID by number */
573 #if TARGET_HOST_POSIX_X11
574 #endif
575             break ;
576
577         case 23 :  /* "xstaticgray": */
578         case 29 :  /* "xstaticgrey":  boolean indicating if the frame buffer
579                       configuration's X visual is of type StaticGray */
580 #if TARGET_HOST_POSIX_X11
581 #endif
582             break ;
583
584         case 24 :  /* "xgrayscale": */
585         case 30 :  /* "xgreyscale":  boolean indicating if the frame buffer
586                       configuration's X visual is of type GrayScale */
587 #if TARGET_HOST_POSIX_X11
588 #endif
589             break ;
590
591         case 25 :  /* "xstaticcolor": */
592         case 31 :  /* "xstaticcolour":  boolean indicating if the frame buffer
593                       configuration's X visual is of type StaticColor */
594 #if TARGET_HOST_POSIX_X11
595 #endif
596             break ;
597
598         case 26 :  /* "xpseudocolor": */
599         case 32 :  /* "xpseudocolour":  boolean indicating if the frame buffer
600                       configuration's X visual is of type PseudoColor */
601 #if TARGET_HOST_POSIX_X11
602 #endif
603             break ;
604
605         case 27 :  /* "xtruecolor": */
606         case 33 :  /* "xtruecolour":  boolean indicating if the frame buffer
607                       configuration's X visual is of type TrueColor */
608 #if TARGET_HOST_POSIX_X11
609 #endif
610             break ;
611
612         case 28 :  /* "xdirectcolor": */
613         case 34 :  /* "xdirectcolour":  boolean indicating if the frame buffer
614                       configuration's X visual is of type DirectColor */
615 #if TARGET_HOST_POSIX_X11
616 #endif
617             break ;
618
619         case 35 :  /* "borderless":  windows should not have borders */
620             glut_state_flag |= GLUT_BORDERLESS;
621             break ;
622
623         case 36 :  /* "aux":  some number of aux buffers */
624             glut_state_flag |= GLUT_AUX;
625             break ;
626
627         case 37 :  /* Unrecognized */
628             fgWarning ( "WARNING - Display string token not recognized:  %s",
629                         token );
630             break ;
631         }
632
633         token = strtok ( NULL, " \t" );
634     }
635
636     free ( buffer );
637
638     /* We will make use of this value when creating a new OpenGL context... */
639     fgState.DisplayMode = glut_state_flag;
640 }
641
642 /* -- SETTING OPENGL 3.0 CONTEXT CREATION PARAMETERS ---------------------- */
643
644 void FGAPIENTRY glutInitContextVersion( int majorVersion, int minorVersion )
645 {
646     /* We will make use of these value when creating a new OpenGL context... */
647     fgState.MajorVersion = majorVersion;
648     fgState.MinorVersion = minorVersion;
649 }
650
651
652 void FGAPIENTRY glutInitContextFlags( int flags )
653 {
654     /* We will make use of this value when creating a new OpenGL context... */
655     fgState.ContextFlags = flags;
656 }
657
658 void FGAPIENTRY glutInitContextProfile( int profile )
659 {
660     /* We will make use of this value when creating a new OpenGL context... */
661     fgState.ContextProfile = profile;
662 }
663
664 /* -------------- User Defined Error/Warning Handler Support -------------- */
665
666 /*
667  * Sets the user error handler (note the use of va_list for the args to the fmt)
668  */
669 void FGAPIENTRY glutInitErrorFunc( FGError callback )
670 {
671     /* This allows user programs to handle freeglut errors */
672     fgState.ErrorFunc = callback;
673 }
674
675 /*
676  * Sets the user warning handler (note the use of va_list for the args to the fmt)
677  */
678 void FGAPIENTRY glutInitWarningFunc( FGWarning callback )
679 {
680     /* This allows user programs to handle freeglut warnings */
681     fgState.WarningFunc = callback;
682 }
683
684 /*** END OF FILE ***/