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