Fixed the buglet about failing to report the program's name on error-
[freeglut] / src / 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 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #define G_LOG_DOMAIN "freeglut-init"
33
34 #include "../include/GL/freeglut.h"
35 #include "freeglut_internal.h"
36
37 /*
38  * TODO BEFORE THE STABLE RELEASE:
39  *
40  *  fgDeinitialize()        -- Win32's OK, X11 needs the OS-specific deinitialization done
41  *  glutInitDisplayString() -- display mode string parsing
42  *
43  * Wouldn't it be cool to use gettext() for error messages? I just love bash saying 
44  * "nie znaleziono pliku" instead of "file not found" :) Is gettext easily portable?
45  */
46
47 /* -- GLOBAL VARIABLES ----------------------------------------------------- */
48
49 /*
50  * A structure pointed by g_pDisplay holds all information
51  * regarding the display, screen, root window etc.
52  */
53 SFG_Display fgDisplay;
54
55 /*
56  * The settings for the current freeglut session
57  */
58 SFG_State fgState = { { -1, -1, FALSE },  /* Position */
59                       { 300, 300, TRUE }, /* Size */
60                       GLUT_RGBA | GLUT_SINGLE | GLUT_DEPTH,  /* DisplayMode */
61                       FALSE, /* ForceDirectContext */
62                       TRUE,  /* TryDirectContext */
63                       FALSE, /* ForceIconic */
64                       FALSE, /* UseCurrentContext */
65                       FALSE, /* GLDebugSwitch */
66                       FALSE, /* XSyncSwitch */
67                       TRUE,  /* IgnoreKeyRepeat */
68                       0,     /* FPSInterval */
69                       0,     /* SwapCount */
70                       0,     /* SwapTime */
71 #if TARGET_HOST_WIN32
72                       { 0, FALSE }, /* Time */
73 #else
74                       { { 0, 0 }, FALSE },
75 #endif
76                       { NULL, NULL }, /* Timers */
77                       NULL, /* IdleCallback */
78                       FALSE, /* BuildingAMenu */
79                       0,    /* ActiveMenus */
80                       NULL, /* MenuStateCallback */
81                       NULL, /* MenuStatusCallback */
82                       { 640, 480, TRUE }, /* GameModeSize */
83                       16,  /* GameModeDepth */
84                       72,  /* GameModeRefresh */
85                       GLUT_ACTION_EXIT, /* ActionOnWindowClose */
86                       GLUT_EXEC_STATE_INIT /* ExecState */
87 } ;
88
89
90 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
91
92 /*
93  * A call to this function should initialize all the display stuff...
94  */
95 void fgInitialize( const char* displayName )
96 {
97 #if TARGET_HOST_UNIX_X11
98     /*
99      * Have the display created
100      */
101     fgDisplay.Display = XOpenDisplay( displayName );
102
103     if( fgDisplay.Display == NULL )
104     {
105         /*
106          * Failed to open a display. That's no good.
107          */
108         fgError( "failed to open display '%s'", XDisplayName( displayName ) );
109     }
110
111     /*
112      * Check for the OpenGL GLX extension availability:
113      */
114     if( !glXQueryExtension( fgDisplay.Display, NULL, NULL ) )
115     {
116         /*
117          * GLX extensions have not been found...
118          */
119         fgError( "OpenGL GLX extension not supported by display '%s'", XDisplayName( displayName ) );
120     }
121
122     /*
123      * Grab the default screen for the display we have just opened
124      */
125     fgDisplay.Screen = DefaultScreen( fgDisplay.Display );
126
127     /*
128      * The same applying to the root window
129      */
130     fgDisplay.RootWindow = RootWindow(
131         fgDisplay.Display,
132         fgDisplay.Screen
133     );
134
135     /*
136      * Grab the logical screen's geometry
137      */
138     fgDisplay.ScreenWidth  = DisplayWidth(
139         fgDisplay.Display,
140         fgDisplay.Screen
141     );
142
143     fgDisplay.ScreenHeight = DisplayHeight(
144         fgDisplay.Display,
145         fgDisplay.Screen
146     );
147
148     /*
149      * Grab the physical screen's geometry
150      */
151     fgDisplay.ScreenWidthMM = DisplayWidthMM(
152         fgDisplay.Display,
153         fgDisplay.Screen
154     );
155
156     fgDisplay.ScreenHeightMM = DisplayHeightMM(
157         fgDisplay.Display,
158         fgDisplay.Screen
159     );
160
161     /*
162      * The display's connection number
163      */
164     fgDisplay.Connection = ConnectionNumber( fgDisplay.Display );
165
166     /*
167      * Create the window deletion atom
168      */
169     fgDisplay.DeleteWindow = XInternAtom(
170         fgDisplay.Display,
171         "WM_DELETE_WINDOW",
172         FALSE
173     );
174
175 #elif TARGET_HOST_WIN32
176
177     WNDCLASS wc;
178     ATOM atom;
179
180     /*
181      * What we need to do is to initialize the fgDisplay global structure here...
182      */
183     fgDisplay.Instance = GetModuleHandle( NULL );
184
185     /*
186      * Check if the freeglut window class has been registered before...
187      */
188     atom = GetClassInfo( fgDisplay.Instance, "FREEGLUT", &wc );
189
190     /*
191      * ...nope, it has not, and we have to do it right now:
192      */
193     if( atom == 0 )
194     {
195         /*
196          * Make sure the unitialized fields are reset to zero
197          */
198         ZeroMemory( &wc, sizeof(WNDCLASS) );
199
200         /*
201          * Each of the windows should have its own device context...
202          */
203         wc.style          = CS_OWNDC;
204         wc.lpfnWndProc    = fgWindowProc;
205         wc.cbClsExtra     = 0;
206         wc.cbWndExtra     = 0;
207         wc.hInstance      = fgDisplay.Instance;
208         wc.hIcon          = LoadIcon( fgDisplay.Instance, "GLUT_ICON" );
209         if (!wc.hIcon)
210           wc.hIcon        = LoadIcon( NULL, IDI_WINLOGO );
211
212         wc.hCursor        = LoadCursor( NULL, IDC_ARROW );
213         wc.hbrBackground  = NULL;
214         wc.lpszMenuName   = NULL;
215         wc.lpszClassName  = "FREEGLUT";
216
217         /*
218          * Register the window class
219          */
220         atom = RegisterClass( &wc );
221         assert( atom != 0 );
222     }
223
224     /*
225      * The screen dimensions can be obtained via GetSystemMetrics() calls
226      */
227     fgDisplay.ScreenWidth  = GetSystemMetrics( SM_CXSCREEN );
228     fgDisplay.ScreenHeight = GetSystemMetrics( SM_CYSCREEN );
229
230     {
231         /*
232          * Checking the display's size in millimeters isn't too hard, too:
233          */
234         HWND desktop = GetDesktopWindow();
235         HDC  context = GetDC( desktop );
236
237         /*
238          * Grab the appropriate values now (HORZSIZE and VERTSIZE respectably):
239          */
240         fgDisplay.ScreenWidthMM  = GetDeviceCaps( context, HORZSIZE );
241         fgDisplay.ScreenHeightMM = GetDeviceCaps( context, VERTSIZE );
242
243         /*
244          * Whoops, forgot to release the device context :)
245          */
246         ReleaseDC( desktop, context );
247     }
248
249 #endif
250
251     /*
252      * Have the joystick device initialized now
253      */
254     fgJoystickInit( 0 );
255 }
256
257 /*
258  * Perform the freeglut deinitialization...
259  */
260 void fgDeinitialize( void )
261 {
262     SFG_Timer *timer;
263
264     /*
265      * Check if initialization has been performed before
266      */
267     if( !fgState.Time.Set )
268     {
269         fgWarning( "fgDeinitialize(): fgState.Timer is null => no valid initialization has been performed" );
270         return;
271     }
272
273     /*
274      * If there was a menu created, destroy the rendering context
275      */
276     if ( fgStructure.MenuContext )
277     {
278       free ( fgStructure.MenuContext ) ;
279       fgStructure.MenuContext = NULL ;
280     }
281
282     /*
283      * Perform the freeglut structure deinitialization
284      */
285     fgDestroyStructure();
286
287     /*
288      * Delete all the timers and their storage list
289      */
290     while ( (timer = (SFG_Timer *)fgState.Timers.First) != NULL )
291     {
292       fgListRemove ( &fgState.Timers, &timer->Node ) ;
293       free ( timer ) ;
294     }
295
296     /*
297      * Deinitialize the joystick device
298      */
299     fgJoystickClose();
300
301     /*
302      * Reset the state structure
303      */
304
305     fgState.Position.X = -1 ;
306     fgState.Position.Y = -1 ;
307     fgState.Position.Use = FALSE ;
308
309     fgState.Size.X = 300 ;
310     fgState.Size.Y = 300 ;
311     fgState.Size.Use = TRUE ;
312
313     /*
314      * The default display mode to be used
315      */
316     fgState.DisplayMode = GLUT_RGBA | GLUT_SINGLE | GLUT_DEPTH;
317
318     fgState.ForceDirectContext  = FALSE;
319     fgState.TryDirectContext    = TRUE;
320     fgState.ForceIconic         = FALSE;
321     fgState.UseCurrentContext   = FALSE;
322     fgState.GLDebugSwitch       = FALSE;
323     fgState.XSyncSwitch         = FALSE;
324     fgState.ActionOnWindowClose = GLUT_ACTION_EXIT ;
325     fgState.ExecState           = GLUT_EXEC_STATE_INIT ;
326
327     /*
328      * Assume we want to ignore the automatic key repeat
329      */
330     fgState.IgnoreKeyRepeat = TRUE;
331
332     /*
333      * Set the default game mode settings
334      */
335     fgState.GameModeSize.X  = 640;
336     fgState.GameModeSize.Y  = 480;
337     fgState.GameModeDepth   =  16;
338     fgState.GameModeRefresh =  72;
339
340     fgState.Time.Set = FALSE ;
341
342     fgState.Timers.First = fgState.Timers.Last = NULL ;
343     fgState.IdleCallback = NULL ;
344     fgState.MenuStateCallback = (FGCBmenuState)NULL ;
345     fgState.MenuStatusCallback = (FGCBmenuStatus)NULL ;
346
347     /*
348      * FPS display
349      */
350     fgState.SwapCount   = 0;
351     fgState.SwapTime    = 0;
352     fgState.FPSInterval = 0;
353
354
355 #if TARGET_HOST_UNIX_X11
356
357     /*
358      * Make sure all X-client data we have created will be destroyed on display closing
359      */
360     XSetCloseDownMode( fgDisplay.Display, DestroyAll );
361
362     /*
363      * Close the display connection, destroying all windows we have created so far
364      */
365     XCloseDisplay( fgDisplay.Display );
366
367 #endif
368 }
369
370
371 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
372
373 /*
374  * Perform initialization. This usually happens on the program startup
375  * and restarting after glutMainLoop termination...
376  */
377 void FGAPIENTRY glutInit( int* pargc, char** argv )
378 {
379     char* displayName = NULL;
380     int i, j, argc = *pargc;
381
382     if (pargc && *pargc && argv && *argv && **argv)
383         fgState.ProgramName = strdup (*argv);
384     else
385         fgState.ProgramName = strdup ("");
386     /*
387      * Do not allow multiple initialization of the library
388      */
389     if( fgState.Time.Set )
390     {
391         /*
392          * We can't have multiple initialization performed
393          */
394         fgError( "illegal glutInit() reinitialization attemp" );
395     }
396
397     /*
398      * Have the internal freeglut structure initialized now
399      */
400     fgCreateStructure();
401
402     /*
403      * Remember the function's call time
404      */
405 #if TARGET_HOST_UNIX_X11
406     gettimeofday(&fgState.Time.Value, NULL);
407 #elif TARGET_HOST_WIN32
408     fgState.Time.Value = timeGetTime();
409 #endif
410     fgState.Time.Set = TRUE;
411
412     /* check if GLUT_FPS env var is set */
413     {
414       const char *fps = getenv ( "GLUT_FPS" );
415       if ( fps )
416       {
417         sscanf ( fps, "%d", &fgState.FPSInterval ) ;
418         if ( fgState.FPSInterval <= 0 )
419           fgState.FPSInterval = 5000 ;  /* 5000 milliseconds */
420       }
421     }
422
423     /*
424      * Grab the environment variable indicating the X display to use.
425      * This is harmless under Win32, so let's let it stay here...
426      */
427 #if TARGET_HOST_WIN32
428     if ( !getenv ( "DISPLAY" ) )
429       displayName = strdup ( "" ) ;
430     else
431 #endif
432     displayName = strdup( getenv( "DISPLAY" ) );
433
434     /*
435      * Have the program arguments parsed.
436      */
437     for( i=1; i<argc; i++ )
438     {
439         /*
440          * The X display name settings
441          */
442         if( strcmp( argv[ i ], "-display" ) == 0 )
443         {
444             /*
445              * Check for possible lack of the next argument
446              */
447             if( ++i >= argc )
448                 fgError( "-display parameter must be followed by display name" );
449
450             /*
451              * Release the previous display name (the one from app's environment)
452              */
453             free( displayName );
454
455             /*
456              * Make a working copy of the name for us to use
457              */
458             displayName = strdup( argv[ i ] );
459
460             /*
461              * Have both arguments removed
462              */
463             argv[ i - 1 ] = NULL;
464             argv[   i   ] = NULL;
465             (* pargc) -= 2;
466         }
467
468         /*
469          * The geometry settings
470          */
471         else if( strcmp( argv[ i ], "-geometry" ) == 0 )
472         {
473           int result, x, y;
474           unsigned int w, h;
475
476           /*
477            * Again, check if there is at least one more argument
478            */
479           if ( ++i >= argc )
480             fgError( "-geometry parameter must be followed by window geometry settings" );
481
482           /*
483            * Otherwise scan the geometry settings...
484            */
485           result = sscanf ( argv[i], "%dx%d+%d+%d", &x, &y, &w, &h );
486
487           /*
488            * Check what we have been supplied with...
489            */
490           if ( result > 3 )
491             fgState.Size.Y = h ;
492
493           if ( result > 2 )
494             fgState.Size.X = w ;
495
496           if( result > 1 )
497           {
498             if( y < 0 )
499               fgState.Position.Y = fgDisplay.ScreenHeight + y - fgState.Size.Y;
500             else
501               fgState.Position.Y = y;
502           }
503
504           if( result > 0 )
505           {
506             if( x < 0 )
507               fgState.Position.X = fgDisplay.ScreenWidth + x - fgState.Size.X;
508             else
509               fgState.Position.X = x;
510           }
511
512           /*
513            * Have both arguments removed
514            */
515           argv[ i - 1 ] = NULL;
516           argv[   i   ] = NULL;
517           (* pargc) -= 2;
518         }
519
520         /*
521          * The direct/indirect OpenGL contexts settings
522          */
523         else if( strcmp( argv[ i ], "-direct" ) == 0)
524         {
525             /*
526              * We try to force direct rendering...
527              */
528             if( fgState.TryDirectContext == FALSE )
529                 fgError( "parameters ambiguity, -direct and -indirect cannot be both specified" );
530
531             fgState.ForceDirectContext = TRUE;
532             argv[ i ] = NULL;
533             (* pargc)--;
534         }
535         else if( strcmp( argv[ i ], "-indirect" ) == 0 )
536         {
537             /*
538              * We try to force indirect rendering...
539              */
540             if( fgState.ForceDirectContext == TRUE )
541                 fgError( "parameters ambiguity, -direct and -indirect cannot be both specified" );
542
543             fgState.TryDirectContext = FALSE;
544             argv[ i ] = NULL;
545             (* pargc)--;
546         }
547
548         /*
549          * The '-iconic' parameter makes all new top-level
550          * windows created in iconified state...
551          */
552         else if( strcmp( argv[ i ], "-iconic" ) == 0 )
553         {
554             fgState.ForceIconic = TRUE;
555             argv[ i ] = NULL;
556             (* pargc)--;
557         }
558
559         /*
560          * The '-gldebug' option activates some OpenGL state debugging features
561          */
562         else if( strcmp( argv[ i ], "-gldebug" ) == 0 )
563         {
564             fgState.GLDebugSwitch = TRUE;
565             argv[ i ] = NULL;
566             (* pargc)--;
567         }
568
569         /*
570          * The '-sync' option activates X protocol synchronization (for debugging purposes)
571          */
572         else if( strcmp( argv[ i ], "-sync" ) == 0 )
573         {
574             fgState.XSyncSwitch = TRUE;
575             argv[ i ] = NULL;
576             (* pargc)--;
577         }
578     }
579
580     /*
581      * Have the arguments list compacted now
582      */
583     j = 2 ;
584     for( i = 1; i < *pargc; i++, j++ )
585     {
586       if( argv[ i ] == NULL )
587       {
588         while ( argv[j] == NULL ) j++ ;  /* Guaranteed to end because there are "*pargc" arguments left */
589         argv[i] = argv[j] ;
590       }
591     }
592
593     /*
594      * Have the display created now. As I am too lazy to implement
595      * the program arguments parsing, we will have the DISPLAY
596      * environment variable used for opening the X display:
597      */
598     fgInitialize( displayName );
599
600     /*
601      * Check for the minus one settings for both position and size...
602      */
603     if( fgState.Position.X < 0 || fgState.Position.Y < 0 )
604         fgState.Position.Use = FALSE;
605
606     if( fgState.Size.X < 0 || fgState.Size.Y < 0 )
607         fgState.Size.Use = FALSE;
608
609     /*
610      * Do not forget about releasing the display name string
611      */
612     free( displayName );
613 }
614
615 /*
616  * Sets the default initial window position for new windows
617  */
618 void FGAPIENTRY glutInitWindowPosition( int x, int y )
619 {
620     /*
621      * The settings can be disables when both coordinates are negative
622      */
623     if( (x >= 0) && (y >= 0) )
624     {
625         /*
626          * We want to specify the initial position of each of the windows
627          */
628         fgState.Position.X   =    x;
629         fgState.Position.Y   =    y;
630         fgState.Position.Use = TRUE;
631     }
632     else
633     {
634         /*
635          * The initial position of each of the windows is specified by the wm
636          */
637         fgState.Position.X   =    -1;
638         fgState.Position.Y   =    -1;
639         fgState.Position.Use = FALSE;
640     }
641 }
642
643 /*
644  * Sets the default initial window size for new windows
645  */
646 void FGAPIENTRY glutInitWindowSize( int width, int height )
647 {
648     /*
649      * The settings can be disables when both values are negative
650      */
651     if( (width > 0) && (height > 0) )
652     {
653         /*
654          * We want to specify the initial size of each of the windows
655          */
656         fgState.Size.X   =  width;
657         fgState.Size.Y   = height;
658         fgState.Size.Use =   TRUE;
659     }
660     else
661     {
662         /*
663          * The initial size of each of the windows is specified by the wm (officially this is an error condition)
664          */
665         fgState.Size.X   =    -1;
666         fgState.Size.Y   =    -1;
667         fgState.Size.Use = FALSE;
668     }
669 }
670
671 /*
672  * Sets the default display mode for all new windows
673  */
674 void FGAPIENTRY glutInitDisplayMode( unsigned int displayMode )
675 {
676     /*
677      * We will make use of this value when creating a new OpenGL context...
678      */
679     fgState.DisplayMode = displayMode;
680 }
681
682
683 /* -- INIT DISPLAY STRING PARSING ------------------------------------------ */
684
685 #if 0 /* FIXME: CJP */
686 /*
687  * There is a discrete number of comparison operators we can encounter:
688  *
689  *     comparison ::= "=" | "!=" | "<" | ">" | "<=" | ">=" | "~"
690  */
691 #define  FG_NONE           0x0000
692 #define  FG_EQUAL          0x0001
693 #define  FG_NOT_EQUAL      0x0002
694 #define  FG_LESS           0x0003
695 #define  FG_MORE           0x0004
696 #define  FG_LESS_OR_EQUAL  0x0005
697 #define  FG_MORE_OR_EQUAL  0x0006
698 #define  FG_CLOSEST        0x0007
699
700 /*
701  * The caller can feed us with a number of capability tokens:
702  *
703  * capability ::= "alpha" | "acca" | "acc" | "blue" | "buffer" | "conformant" | "depth" | "double" |
704  *                "green" | "index" | "num" | "red" | "rgba" | "rgb" | "luminance" | "stencil" |
705  *                "single" | "stereo" | "samples" | "slow" | "win32pdf" | "xvisual" | "xstaticgray" |
706  *                "xgrayscale" | "xstaticcolor" | "xpseudocolor" | "xtruecolor" | "xdirectcolor"
707  */
708 static gchar* g_Tokens[] =
709 {
710     "none", "alpha", "acca", "acc", "blue", "buffer", "conformant", "depth", "double", "green",
711     "index", "num", "red", "rgba", "rgb", "luminance", "stencil", "single", "stereo", "samples",
712     "slow", "win32pdf", "xvisual", "xstaticgray", "xgrayscale", "xstaticcolor", "xpseudocolor",
713     "xtruecolor", "xdirectcolor", NULL
714 };
715
716 /*
717  * The structure to hold the parsed display string tokens
718  */
719 typedef struct tagSFG_Capability SFG_Capability;
720 struct tagSFG_Capability
721 {
722     gint capability;        /* the capability token enumerator */
723     gint comparison;        /* the comparison operator used    */
724     gint value;             /* the value we're comparing to    */
725 };
726
727 /*
728  * The scanner configuration for the init display string
729  */
730 static GScannerConfig fgInitDisplayStringScannerConfig =
731 {
732     ( " \t\r\n" )               /* cset_skip_characters     */,
733     (
734         G_CSET_a_2_z
735         "_"
736         G_CSET_A_2_Z
737     )                                   /* cset_identifier_first    */,
738     (
739         G_CSET_a_2_z
740         "_0123456789"
741         G_CSET_A_2_Z
742         G_CSET_LATINS
743         G_CSET_LATINC
744         "<>!=~"
745     )                                   /* cset_identifier_nth      */,
746     ( "#\n" )                       /* cpair_comment_single     */,
747     FALSE                                   /* case_sensitive           */,
748     TRUE                                    /* skip_comment_multi       */,
749     TRUE                                    /* skip_comment_single      */,
750     TRUE                                    /* scan_comment_multi       */,
751     TRUE                                    /* scan_identifier          */,
752     FALSE                                   /* scan_identifier_1char    */,
753     FALSE                                   /* scan_identifier_NULL     */,
754     TRUE                                    /* scan_symbols             */,
755     FALSE                                   /* scan_binary              */,
756     TRUE                                    /* scan_octal               */,
757     TRUE                                    /* scan_float               */,
758     TRUE                                    /* scan_hex                 */,
759     FALSE                                   /* scan_hex_dollar          */,
760     TRUE                                    /* scan_string_sq           */,
761     TRUE                                    /* scan_string_dq           */,
762     TRUE                                    /* numbers_2_int            */,
763     FALSE                                   /* int_2_float              */,
764     FALSE                                   /* identifier_2_string      */,
765     TRUE                                    /* char_2_token             */,
766     FALSE                                   /* symbol_2_token           */,
767     FALSE                                   /* scope_0_fallback         */,
768 };
769
770 /*
771  * Sets the default display mode for all new windows using a string
772  */
773 void FGAPIENTRY glutInitDisplayString( char* displayMode )
774 {
775     /*
776      * display_string ::= (switch)
777      * switch         ::= capability [comparison value]
778      * comparison     ::= "=" | "!=" | "<" | ">" | "<=" | ">=" | "~"
779      * capability     ::= "alpha" | "acca" | "acc" | "blue" | "buffer" | "conformant" |
780      *                    "depth" | "double" | "green" | "index" | "num" | "red" | "rgba" |
781      *                    "rgb" | "luminance" | "stencil" | "single" | "stereo" |
782      *                    "samples" | "slow" | "win32pdf" | "xvisual" | "xstaticgray" |
783      *                    "xgrayscale" | "xstaticcolor" | "xpseudocolor" |
784      *                    "xtruecolor" | "xdirectcolor"
785      * value          ::= 0..9 [value]
786      *
787      * The display string grammar. This should be EBNF, but I couldn't find the definitions so, to
788      * clarify: (expr) means 0 or more times the expression, [expr] means 0 or 1 times expr.
789      *
790      * Create a new GLib lexical analyzer to process the display mode string
791      */
792     GScanner* scanner = g_scanner_new( &fgInitDisplayStringScannerConfig );
793     GList* caps = NULL;
794     gint i;
795
796     /*
797      * Fail if the display mode string is empty or the scanner failed to initialize
798      */
799     freeglut_return_if_fail( (scanner != NULL) && (strlen( displayMode ) > 0) );
800
801     /*
802      * Set the scanner's input name (for debugging)
803      */
804     scanner->input_name = "glutInitDisplayString";
805
806     /*
807      * Start the lexical analysis of the extensions string
808      */
809     g_scanner_input_text( scanner, displayMode, strlen( displayMode ) );
810
811     /*
812      * While there are any more tokens to be checked...
813      */
814     while( !g_scanner_eof( scanner ) )
815     {
816         /*
817          * Actually we're expecting only string tokens
818          */
819         GTokenType tokenType = g_scanner_get_next_token( scanner );
820
821         /*
822          * We are looking for identifiers
823          */
824         if( tokenType == G_TOKEN_IDENTIFIER )
825         {
826             gchar* capability  = NULL;  /* the capability identifier string (always present) */
827             gint   capID       =    0;  /* the capability identifier value (from g_Tokens)   */
828             gint   comparison  =    0;  /* the comparison operator value, see definitions    */
829             gchar* valueString = NULL;  /* if the previous one is present, this is needed    */
830             gint   value       =    0;  /* the integer value converted using a strtol call   */
831             SFG_Capability* capStruct;  /* the capability description structure              */
832
833             /*
834              * OK. The general rule of thumb that we always should be getting a capability identifier
835              * string (see the grammar description). If it is followed by a comparison identifier, then
836              * there must follow an integer value we're comparing the capability to...
837              *
838              * Have the current token analyzed with that in mind...
839              */
840             for( i=0; i<(gint) strlen( scanner->value.v_identifier ); i++ )
841             {
842                 gchar c = scanner->value.v_identifier[ i ];
843
844                 if( (c == '=') || (c == '!') || (c == '<') || (c == '>') || (c == '~') )
845                     break;
846             }
847
848             /*
849              * Here we go with the length of the capability identifier string.
850              * In the worst of cases, it is as long as the token identifier.
851              */
852             capability = g_strndup( scanner->value.v_identifier, i );
853
854             /*
855              * OK. Is there a chance for comparison and value identifiers to follow?
856              * Note: checking against i+1 as this handles two cases: single character
857              * comparison operator and first of value's digits, which must always be
858              * there, or the two-character comparison operators.
859              */
860             if( (i + 1) < (gint) strlen( scanner->value.v_identifier ) )
861             {
862                 /*
863                  * Yeah, indeed, it is the i-th character to start the identifier, then.
864                  */
865                 gchar c1 = scanner->value.v_identifier[ i + 0 ];
866                 gchar c2 = scanner->value.v_identifier[ i + 1 ];
867
868                 if( (c1 == '=')                ) { i += 1; comparison = FG_EQUAL;         } else
869                 if( (c1 == '!') && (c2 == '=') ) { i += 2; comparison = FG_NOT_EQUAL;     } else
870                 if( (c1 == '<') && (c2 == '=') ) { i += 2; comparison = FG_LESS_OR_EQUAL; } else
871                 if( (c1 == '>') && (c2 == '=') ) { i += 2; comparison = FG_MORE_OR_EQUAL; } else
872                 if( (c1 == '<')                ) { i += 1; comparison = FG_LESS;          } else
873                 if( (c1 == '>')                ) { i += 1; comparison = FG_MORE;          } else
874                 if( (c1 == '~')                ) { i += 1; comparison = FG_CLOSEST;       } else
875                 g_warning( "invalid comparison operator in token `%s'", scanner->value.v_identifier );
876             }
877
878             /*
879              * Grab the value string that must follow the comparison operator...
880              */
881             if( comparison != FG_NONE && i < (gint) strlen( scanner->value.v_identifier ) )
882                 valueString = strdup( scanner->value.v_identifier + i );
883
884             /*
885              * If there was a value string, convert it to integer...
886              */
887             if( comparison != FG_NONE && strlen( valueString ) > 0 )
888                 value = strtol( valueString, NULL, 0 );
889
890             /*
891              * Now we need to match the capability string and its ID
892              */
893             for( i=0; g_Tokens[ i ]!=NULL; i++ )
894             {
895                 if( strcmp( capability, g_Tokens[ i ] ) == 0 )
896                 {
897                     /*
898                      * Looks like we've found the one we were looking for
899                      */
900                     capID = i;
901                     break;
902                 }
903             }
904
905             /*
906              * Create a new capability description structure
907              */
908             capStruct = g_new0( SFG_Capability, 1 );
909
910             /*
911              * Fill in the cap's values, as we have parsed it:
912              */
913             capStruct->capability =      capID;
914             capStruct->comparison = comparison;
915             capStruct->value      =      value;
916
917             /*
918              * Add the new capabaility to the caps list
919              */
920             caps = g_list_append( caps, capStruct );
921
922             /*
923              * Clean up the local mess and keep rolling on
924              */
925             g_free( valueString );
926             g_free( capability );
927         }
928     }
929
930     /*
931      * Now that we have converted the string into somewhat more machine-friendly
932      * form, proceed with matching the frame buffer configuration...
933      *
934      * The caps list could be passed to a function that would try finding the closest 
935      * matching pixel format, visual, frame buffer configuration or whatever. It would 
936      * be good to convert the glutInitDisplayMode() to use the same method.
937      */
938 #if 0
939     g_message( "found %i capability preferences", g_list_length( caps ) );
940
941     g_message( "token `%s': cap: %i, com: %i, val: %i",
942         scanner->value.v_identifier,
943         capStruct->capability,
944         capStruct->comparison,
945         capStruct->value
946     );
947 #endif
948
949     /*
950      * Free the capabilities we have parsed
951      */
952     for( i=0; i<(gint) g_list_length( caps ); i++ )
953         g_free( g_list_nth( caps, i )->data );
954
955     /*
956      * Destroy the capabilities list itself
957      */
958     g_list_free( caps );
959
960     /*
961      * Free the lexical scanner now...
962      */
963     g_scanner_destroy( scanner );
964 }
965 #endif
966
967 #define NUM_TOKENS             28
968 static char* Tokens[] =
969 {
970     "alpha", "acca", "acc", "blue", "buffer", "conformant", "depth", "double", "green",
971     "index", "num", "red", "rgba", "rgb", "luminance", "stencil", "single", "stereo", "samples",
972     "slow", "win32pdf", "xvisual", "xstaticgray", "xgrayscale", "xstaticcolor", "xpseudocolor",
973     "xtruecolor", "xdirectcolor"
974 };
975
976 static int TokenLengths[] =
977 {
978          5,      4,     3,      4,        6,           10,       5,        6,       5,
979          5,     3,     3,      4,     3,           9,         7,        6,        6,         7,
980         4,          8,         7,            11,           10,             12,             12,
981              10,             12
982 };
983
984 void FGAPIENTRY glutInitDisplayString( const char* displayMode )
985 {
986   int glut_state_flag = 0 ;
987   /*
988    * Unpack a lot of options from a character string.  The options are delimited by blanks or tabs.
989    */
990   char *token ;
991   int len = strlen ( displayMode ) ;
992   char *buffer = (char *)malloc ( (len+1) * sizeof(char) ) ;
993   memcpy ( buffer, displayMode, len ) ;
994   buffer[len] = '\0' ;
995
996   token = strtok ( buffer, " \t" ) ;
997   while ( token )
998   {
999     /*
1000      * Process this token
1001      */
1002     int i ;
1003     for ( i = 0; i < NUM_TOKENS; i++ )
1004     {
1005       if ( strncmp ( token, Tokens[i], TokenLengths[i] ) == 0 ) break ;
1006     }
1007
1008     switch ( i )
1009     {
1010     case 0 :  /* "alpha":  Alpha color buffer precision in bits */
1011       glut_state_flag |= GLUT_ALPHA ;  /* Somebody fix this for me! */
1012       break ;
1013
1014     case 1 :  /* "acca":  Red, green, blue, and alpha accumulation buffer precision in bits */
1015       break ;
1016
1017     case 2 :  /* "acc":  Red, green, and blue accumulation buffer precision in bits with zero bits alpha */
1018       glut_state_flag |= GLUT_ACCUM ;  /* Somebody fix this for me! */
1019       break ;
1020
1021     case 3 :  /* "blue":  Blue color buffer precision in bits */
1022       break ;
1023
1024     case 4 :  /* "buffer":  Number of bits in the color index color buffer */
1025       break ;
1026
1027     case 5 :  /* "conformant":  Boolean indicating if the frame buffer configuration is conformant or not */
1028       break ;
1029
1030     case 6 :  /* "depth":  Number of bits of precsion in the depth buffer */
1031       glut_state_flag |= GLUT_DEPTH ;  /* Somebody fix this for me! */
1032       break ;
1033
1034     case 7 :  /* "double":  Boolean indicating if the color buffer is double buffered */
1035       glut_state_flag |= GLUT_DOUBLE ;
1036       break ;
1037
1038     case 8 :  /* "green":  Green color buffer precision in bits */
1039       break ;
1040
1041     case 9 :  /* "index":  Boolean if the color model is color index or not */
1042       glut_state_flag |= GLUT_INDEX ;
1043       break ;
1044
1045     case 10 :  /* "num":  A special capability  name indicating where the value represents the Nth frame buffer configuration matching the description string */
1046       break ;
1047
1048     case 11 :  /* "red":  Red color buffer precision in bits */
1049       break ;
1050
1051     case 12 :  /* "rgba":  Number of bits of red, green, blue, and alpha in the RGBA color buffer */
1052       glut_state_flag |= GLUT_RGBA ;  /* Somebody fix this for me! */
1053       break ;
1054
1055     case 13 :  /* "rgb":  Number of bits of red, green, and blue in the RGBA color buffer with zero bits alpha */
1056       glut_state_flag |= GLUT_RGB ;  /* Somebody fix this for me! */
1057       break ;
1058
1059     case 14 :  /* "luminance":  Number of bits of red in the RGBA and zero bits of green, blue (alpha not specified) of color buffer precision */
1060       glut_state_flag |= GLUT_LUMINANCE ;  /* Somebody fix this for me! */
1061       break ;
1062
1063     case 15 :  /* "stencil":  Number of bits in the stencil buffer */
1064       glut_state_flag |= GLUT_STENCIL ;  /* Somebody fix this for me! */
1065       break ;
1066
1067     case 16 :  /* "single":  Boolean indicate the color buffer is single buffered */
1068       glut_state_flag |= GLUT_SINGLE ;
1069       break ;
1070
1071     case 17 :  /* "stereo":  Boolean indicating the color buffer supports OpenGL-style stereo */
1072       glut_state_flag |= GLUT_STEREO ;
1073       break ;
1074
1075     case 18 :  /* "samples":  Indicates the number of multisamples to use based on GLX's SGIS_multisample extension (for antialiasing) */
1076       glut_state_flag |= GLUT_MULTISAMPLE ;  /* Somebody fix this for me! */
1077       break ;
1078
1079     case 19 :  /* "slow":  Boolean indicating if the frame buffer configuration is slow or not */
1080       break ;
1081
1082     case 20 :  /* "win32pdf":  matches the Win32 Pixel Format Descriptor by number */
1083 #if TARGET_HOST_WIN32
1084 #endif
1085       break ;
1086
1087     case 21 :  /* "xvisual":  matches the X visual ID by number */
1088 #if TARGET_HOST_UNIX_X11
1089 #endif
1090       break ;
1091
1092     case 22 :  /* "xstaticgray":  boolean indicating if the frame buffer configuration's X visual is of type StaticGray */
1093 #if TARGET_HOST_UNIX_X11
1094 #endif
1095       break ;
1096
1097     case 23 :  /* "xgrayscale":  boolean indicating if the frame buffer configuration's X visual is of type GrayScale */
1098 #if TARGET_HOST_UNIX_X11
1099 #endif
1100       break ;
1101
1102     case 24 :  /* "xstaticcolor":  boolean indicating if the frame buffer configuration's X visual is of type StaticColor */
1103 #if TARGET_HOST_UNIX_X11
1104 #endif
1105       break ;
1106
1107     case 25 :  /* "xpseudocolor":  boolean indicating if the frame buffer configuration's X visual is of type PseudoColor */
1108 #if TARGET_HOST_UNIX_X11
1109 #endif
1110       break ;
1111
1112     case 26 :  /* "xtruecolor":  boolean indicating if the frame buffer configuration's X visual is of type TrueColor */
1113 #if TARGET_HOST_UNIX_X11
1114 #endif
1115       break ;
1116
1117     case 27 :  /* "xdirectcolor":  boolean indicating if the frame buffer configuration's X visual is of type DirectColor */
1118 #if TARGET_HOST_UNIX_X11
1119 #endif
1120       break ;
1121
1122     case 28 :  /* Unrecognized */
1123       printf ( "WARNING - Display string token not recognized:  %s\n", token ) ;
1124       break ;
1125     }
1126
1127     token = strtok ( NULL, " \t" ) ;
1128   }
1129
1130   free ( buffer ) ;
1131
1132   /*
1133    * We will make use of this value when creating a new OpenGL context...
1134    */
1135   fgState.DisplayMode = glut_state_flag;
1136 }
1137
1138 /*** END OF FILE ***/