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