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