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