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