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