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