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