Removed glib dependancy
[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 "../include/GL/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;
59
60
61 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
62
63 /*
64  * A call to this function should initialize all the display stuff...
65  */
66 void fgInitialize( const char* displayName )
67 {
68 #if TARGET_HOST_UNIX_X11
69     /*
70      * Have the display created
71      */
72     fgDisplay.Display = XOpenDisplay( displayName );
73
74     if( fgDisplay.Display == NULL )
75     {
76         /*
77          * Failed to open a display. That's no good.
78          */
79         fgError( "failed to open display '%s'", XDisplayName( displayName ) );
80     }
81
82     /*
83      * Check for the OpenGL GLX extension availability:
84      */
85     if( !glXQueryExtension( fgDisplay.Display, NULL, NULL ) )
86     {
87         /*
88          * GLX extensions have not been found...
89          */
90         fgError( "OpenGL GLX extension not supported by display '%s'", XDisplayName( displayName ) );
91     }
92
93     /*
94      * Grab the default screen for the display we have just opened
95      */
96     fgDisplay.Screen = DefaultScreen( fgDisplay.Display );
97
98     /*
99      * The same applying to the root window
100      */
101     fgDisplay.RootWindow = RootWindow(
102         fgDisplay.Display,
103         fgDisplay.Screen
104     );
105
106     /*
107      * Grab the logical screen's geometry
108      */
109     fgDisplay.ScreenWidth  = DisplayWidth(
110         fgDisplay.Display,
111         fgDisplay.Screen
112     );
113
114     fgDisplay.ScreenHeight = DisplayHeight(
115         fgDisplay.Display,
116         fgDisplay.Screen
117     );
118
119     /*
120      * Grab the physical screen's geometry
121      */
122     fgDisplay.ScreenWidthMM = DisplayWidthMM(
123         fgDisplay.Display,
124         fgDisplay.Screen
125     );
126
127     fgDisplay.ScreenHeightMM = DisplayHeightMM(
128         fgDisplay.Display,
129         fgDisplay.Screen
130     );
131
132     /*
133      * The display's connection number
134      */
135     fgDisplay.Connection = ConnectionNumber( fgDisplay.Display );
136
137     /*
138      * Create the window deletion atom
139      */
140     fgDisplay.DeleteWindow = XInternAtom(
141         fgDisplay.Display,
142         "WM_DELETE_WINDOW",
143         FALSE
144     );
145
146 #elif TARGET_HOST_WIN32
147
148     WNDCLASS wc;
149     ATOM atom;
150
151     /*
152      * What we need to do is to initialize the fgDisplay global structure here...
153      */
154     fgDisplay.Instance = GetModuleHandle( NULL );
155
156     /*
157      * Check if the freeglut window class has been registered before...
158      */
159     atom = GetClassInfo( fgDisplay.Instance, "FREEGLUT", &wc );
160
161     /*
162      * ...nope, it has not, and we have to do it right now:
163      */
164     if( atom == 0 )
165     {
166         gboolean retval;
167
168         /*
169          * Make sure the unitialized fields are reset to zero
170          */
171         ZeroMemory( &wc, sizeof(WNDCLASS) );
172
173         /*
174          * Each of the windows should have it's own device context...
175          */
176         wc.style          = CS_OWNDC;
177         wc.lpfnWndProc    = fgWindowProc;
178         wc.cbClsExtra     = 0;
179         wc.cbWndExtra     = 0;
180         wc.hInstance      = fgDisplay.Instance;
181         wc.hIcon          = LoadIcon( NULL, IDI_WINLOGO );
182         wc.hCursor        = LoadCursor( NULL, IDC_ARROW );
183         wc.hbrBackground  = NULL;
184         wc.lpszMenuName   = NULL;
185         wc.lpszClassName  = "FREEGLUT";
186
187         /*
188          * Register the window class
189          */
190         retval = RegisterClass( &wc );
191         g_assert( retval != FALSE );
192     }
193
194     /*
195      * The screen dimensions can be obtained via GetSystemMetrics() calls
196      */
197     fgDisplay.ScreenWidth  = GetSystemMetrics( SM_CXSCREEN );
198     fgDisplay.ScreenHeight = GetSystemMetrics( SM_CYSCREEN );
199
200     {
201         /*
202          * Checking the display's size in millimeters isn't too hard, too:
203          */
204         HWND desktop = GetDesktopWindow();
205         HDC  context = GetDC( desktop );
206
207         /*
208          * Grab the appropriate values now (HORZSIZE and VERTSIZE respectably):
209          */
210         fgDisplay.ScreenWidthMM  = GetDeviceCaps( context, HORZSIZE );
211         fgDisplay.ScreenHeightMM = GetDeviceCaps( context, VERTSIZE );
212
213         /*
214          * Whoops, forgot to release the device context :)
215          */
216         ReleaseDC( desktop, context );
217     }
218
219 #endif
220
221     /*
222      * Have the joystick device initialized now
223      */
224     fgJoystickInit( 0 );
225 }
226
227 /*
228  * Perform the freeglut deinitialization...
229  */
230 void fgDeinitialize( void )
231 {
232     SFG_Timer *timer;
233
234     /*
235      * Check if initialization has been performed before
236      */
237     if( !fgState.Time.Set )
238     {
239         fgWarning( "fgDeinitialize(): fgState.Timer is null => no valid initialization has been performed" );
240         return;
241     }
242
243     /*
244      * Perform the freeglut structure deinitialization
245      */
246     fgDestroyStructure();
247
248     /*
249      * Delete all the timers and their storage list
250      */
251     while ( (timer = fgState.Timers.First) != NULL )
252     {
253         fgListRemove(&fgState.Timers, &timer->Node);
254         free(timer);
255     }
256
257     /*
258      * Deinitialize the joystick device
259      */
260     fgJoystickClose();
261
262 #if TARGET_HOST_UNIX_X11
263
264     /*
265      * Make sure all X-client data we have created will be destroyed on display closing
266      */
267     XSetCloseDownMode( fgDisplay.Display, DestroyAll );
268
269     /*
270      * Close the display connection, destroying all windows we have created so far
271      */
272     XCloseDisplay( fgDisplay.Display );
273
274 #endif
275 }
276
277
278 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
279
280 /*
281  * Perform initialization. This usually happens on the program startup
282  * and restarting after glutMainLoop termination...
283  */
284 void FGAPIENTRY glutInit( int* pargc, char** argv )
285 {
286     char* geometrySettings = NULL;
287     char* displayName = NULL;
288     int i, j, argc = *pargc;
289
290     /*
291      * Do not allow multiple initialization of the library
292      */
293     if( fgState.Time.Set )
294     {
295         /*
296          * We can't have multiple initialization performed
297          */
298         fgError( "illegal glutInit() reinitialization attemp" );
299     }
300
301     /*
302      * Have the internal freeglut structure initialized now
303      */
304     fgCreateStructure();
305
306     /*
307      * Fill in the default values that have not been passed in yet.
308      */
309     if( fgState.Position.Use == FALSE )
310     {
311         fgState.Position.X = -1;
312         fgState.Position.Y = -1;
313     }
314
315     if( fgState.Size.Use == FALSE )
316     {
317         fgState.Size.X   =  300;
318         fgState.Size.Y   =  300;
319         fgState.Size.Use = TRUE;
320     }
321
322     /*
323      * Some more settings to come
324      */
325     fgState.ForceDirectContext = FALSE;
326     fgState.TryDirectContext   = TRUE;
327     fgState.ForceIconic        = FALSE;
328     fgState.GLDebugSwitch      = FALSE;
329     fgState.XSyncSwitch        = FALSE;
330
331     /*
332      * Assume we want to ignore the automatic key repeat
333      */
334     fgState.IgnoreKeyRepeat = TRUE;
335
336     /*
337      * The default display mode to be used
338      */
339     fgState.DisplayMode = GLUT_RGBA | GLUT_SINGLE | GLUT_DEPTH;
340
341     /*
342      * Set the default game mode settings
343      */
344     fgState.GameModeSize.X  = 640;
345     fgState.GameModeSize.Y  = 480;
346     fgState.GameModeDepth   =  16;
347     fgState.GameModeRefresh =  72;
348
349     /*
350      * Remember the function's call time
351      */
352     gettimeofday(&fgState.Time.Value, NULL);
353     fgState.Time.Set = TRUE;
354
355     /*
356      * Grab the environment variable indicating the X display to use.
357      * This is harmless under Win32, so let's let it stay here...
358      */
359     displayName = strdup( getenv( "DISPLAY" ) );
360
361     /*
362      * Have the program arguments parsed.
363      */
364     for( i=1; i<argc; i++ )
365     {
366         /*
367          * The X display name settings
368          */
369         if( strcmp( argv[ i ], "-display" ) == 0 )
370         {
371             /*
372              * Check for possible lack of the next argument
373              */
374             if( ++i >= argc )
375                 fgError( "-display parameter must be followed by display name" );
376
377             /*
378              * Release the previous display name (the one from app's environment)
379              */
380             free( displayName );
381
382             /*
383              * Make a working copy of the name for us to use
384              */
385             displayName = strdup( argv[ i ] );
386
387             /*
388              * Have both arguments removed
389              */
390             argv[ i - 1 ] = NULL;
391             argv[   i   ] = NULL;
392             (* pargc) -= 2;
393         }
394
395         /*
396          * The geometry settings
397          */
398         else if( strcmp( argv[ i ], "-geometry" ) == 0 )
399         {
400             /*
401              * Again, check if there is at least one more argument
402              */
403             if( ++i >= argc )
404                 fgError( "-geometry parameter must be followed by window geometry settings" );
405
406             /*
407              * Otherwise make a duplicate of the geometry settings...
408              */
409             geometrySettings = strdup( argv[ i ] );
410
411             /*
412              * Have both arguments removed
413              */
414             argv[ i - 1 ] = NULL;
415             argv[   i   ] = NULL;
416             (* pargc) -= 2;
417         }
418
419         /*
420          * The direct/indirect OpenGL contexts settings
421          */
422         else if( strcmp( argv[ i ], "-direct" ) == 0)
423         {
424             /*
425              * We try to force direct rendering...
426              */
427             if( fgState.TryDirectContext == FALSE )
428                 fgError( "parameters ambiguity, -direct and -indirect cannot be both specified" );
429
430             fgState.ForceDirectContext = TRUE;
431             argv[ i ] = NULL;
432             (* pargc)--;
433         }
434         else if( strcmp( argv[ i ], "-indirect" ) == 0 )
435         {
436             /*
437              * We try to force indirect rendering...
438              */
439             if( fgState.ForceDirectContext == TRUE )
440                 fgError( "parameters ambiguity, -direct and -indirect cannot be both specified" );
441
442             fgState.TryDirectContext = FALSE;
443             argv[ i ] = NULL;
444             (* pargc)--;
445         }
446
447         /*
448          * The '-iconic' parameter makes all new top-level
449          * windows created in iconified state...
450          */
451         else if( strcmp( argv[ i ], "-iconic" ) == 0 )
452         {
453             fgState.ForceIconic = TRUE;
454             argv[ i ] = NULL;
455             (* pargc)--;
456         }
457
458         /*
459          * The '-gldebug' option activates some OpenGL state debugging features
460          */
461         else if( strcmp( argv[ i ], "-gldebug" ) == 0 )
462         {
463             fgState.GLDebugSwitch = TRUE;
464             argv[ i ] = NULL;
465             (* pargc)--;
466         }
467
468         /*
469          * The '-sync' option activates X protocol synchronization (for debugging purposes)
470          */
471         else if( strcmp( argv[ i ], "-sync" ) == 0 )
472         {
473             fgState.XSyncSwitch = TRUE;
474             argv[ i ] = NULL;
475             (* pargc)--;
476         }
477     }
478
479     /*
480      * Have the arguments list compacted now
481      */
482     for( i=1; i<argc; i++ )
483     {
484         if( argv[ i ] == NULL )
485         {
486             for( j=i; j<argc; j++ )
487             {
488                 if( argv[ j ] != NULL )
489                 {
490                     argv[ i ] = argv[ j ];
491                     argv[ j ] = NULL;
492                 }
493             }
494         }
495     }
496
497     /*
498      * Have the display created now. As I am too lazy to implement
499      * the program arguments parsing, we will have the DISPLAY
500      * environment variable used for opening the X display:
501      */
502     fgInitialize( displayName );
503
504 #if TARGET_HOST_UNIX_X11
505     /*
506      * We can process the default window geometry settings safely now
507      *
508      * WARNING: have this rewritten to be portable. That won't be hard.
509      */
510     if( geometrySettings != NULL )
511     {
512         int result, x, y;
513         unsigned int w, h;
514
515         /*
516          * Have the settings parsed now. This is easy.
517          * We will use the XParseGeometry function.
518          */
519         result = XParseGeometry( geometrySettings, &x, &y, &w, &h );
520
521         /*
522          * Check what we have been supplied with...
523          */
524         if( (result & WidthValue) && (w >= 0) )
525             fgState.Size.X = w;
526
527         if( (result & HeightValue) && (h >= 0) )
528             fgState.Size.Y = h;
529
530         if( result & XValue )
531         {
532             if( result & XNegative )
533                 fgState.Position.X = fgDisplay.ScreenWidth + x - fgState.Size.X;
534             else
535                 fgState.Position.X = x;
536         }
537
538         if( result & YValue )
539         {
540             if( result & YNegative )
541                 fgState.Position.Y = fgDisplay.ScreenHeight + y - fgState.Size.Y;
542             else
543                 fgState.Position.Y = y;
544         }
545
546         /*
547          * Free the geometry settings string
548          */
549         free( geometrySettings );
550     }
551 #endif
552
553     /*
554      * Check for the minus one settings for both position and size...
555      */
556     if( fgState.Position.X < 0 || fgState.Position.Y < 0 )
557         fgState.Position.Use = FALSE;
558
559     if( fgState.Size.X < 0 || fgState.Size.Y < 0 )
560         fgState.Size.Use = FALSE;
561
562     /*
563      * Do not forget about releasing the display name string
564      */
565     free( displayName );
566 }
567
568 /*
569  * Sets the default initial window position for new windows
570  */
571 void FGAPIENTRY glutInitWindowPosition( int x, int y )
572 {
573     /*
574      * The settings can be disables when both coordinates are negative
575      */
576     if( (x >= 0) && (y >= 0) )
577     {
578         /*
579          * We want to specify the initial position of each of the windows
580          */
581         fgState.Position.X   =    x;
582         fgState.Position.Y   =    y;
583         fgState.Position.Use = TRUE;
584     }
585     else
586     {
587         /*
588          * The initial position of each of the windows is specified by the wm
589          */
590         fgState.Position.X   =    -1;
591         fgState.Position.Y   =    -1;
592         fgState.Position.Use = FALSE;
593     }
594 }
595
596 /*
597  * Sets the default initial window size for new windows
598  */
599 void FGAPIENTRY glutInitWindowSize( int width, int height )
600 {
601     /*
602      * The settings can be disables when both values are negative
603      */
604     if( (width >= 0) && (height >= 0) )
605     {
606         /*
607          * We want to specify the initial size of each of the windows
608          */
609         fgState.Size.X   =  width;
610         fgState.Size.Y   = height;
611         fgState.Size.Use =   TRUE;
612     }
613     else
614     {
615         /*
616          * The initial size of each of the windows is specified by the wm
617          */
618         fgState.Size.X   =    -1;
619         fgState.Size.Y   =    -1;
620         fgState.Size.Use = FALSE;
621     }
622 }
623
624 /*
625  * Sets the default display mode for all new windows
626  */
627 void FGAPIENTRY glutInitDisplayMode( int displayMode )
628 {
629     /*
630      * We will make use of this value when creating a new OpenGL context...
631      */
632     fgState.DisplayMode = displayMode;
633 }
634
635
636 /* -- INIT DISPLAY STRING PARSING ------------------------------------------ */
637
638 #if 0 /* FIXME: CJP */
639 /*
640  * There is a discrete number of comparison operators we can encounter:
641  *
642  *     comparison ::= "=" | "!=" | "<" | ">" | "<=" | ">=" | "~"
643  */
644 #define  FG_NONE           0x0000
645 #define  FG_EQUAL          0x0001
646 #define  FG_NOT_EQUAL      0x0002
647 #define  FG_LESS           0x0003
648 #define  FG_MORE           0x0004
649 #define  FG_LESS_OR_EQUAL  0x0005
650 #define  FG_MORE_OR_EQUAL  0x0006
651 #define  FG_CLOSEST        0x0007
652
653 /*
654  * The caller can feed us with a number of capability tokens:
655  *
656  * capability ::= "alpha" | "acca" | "acc" | "blue" | "buffer" | "conformant" | "depth" | "double" |
657  *                "green" | "index" | "num" | "red" | "rgba" | "rgb" | "luminance" | "stencil" |
658  *                "single" | "stereo" | "samples" | "slow" | "win32pdf" | "xvisual" | "xstaticgray" |
659  *                "xgrayscale" | "xstaticcolor" | "xpseudocolor" | "xtruecolor" | "xdirectcolor"
660  */
661 static gchar* g_Tokens[] =
662 {
663     "none", "alpha", "acca", "acc", "blue", "buffer", "conformant", "depth", "double", "green",
664     "index", "num", "red", "rgba", "rgb", "luminance", "stencil", "single", "stereo", "samples",
665     "slow", "win32pdf", "xvisual", "xstaticgray", "xgrayscale", "xstaticcolor", "xpseudocolor",
666     "xtruecolor", "xdirectcolor", NULL
667 };
668
669 /*
670  * The structure to hold the parsed display string tokens
671  */
672 typedef struct tagSFG_Capability SFG_Capability;
673 struct tagSFG_Capability
674 {
675     gint capability;        /* the capability token enumerator */
676     gint comparison;        /* the comparison operator used    */
677     gint value;             /* the value we're comparing to    */
678 };
679
680 /*
681  * The scanner configuration for the init display string
682  */
683 static GScannerConfig fgInitDisplayStringScannerConfig =
684 {
685     ( " \t\r\n" )               /* cset_skip_characters     */,
686     (
687         G_CSET_a_2_z
688         "_"
689         G_CSET_A_2_Z
690     )                                   /* cset_identifier_first    */,
691     (
692         G_CSET_a_2_z
693         "_0123456789"
694         G_CSET_A_2_Z
695         G_CSET_LATINS
696         G_CSET_LATINC
697         "<>!=~"
698     )                                   /* cset_identifier_nth      */,
699     ( "#\n" )                       /* cpair_comment_single     */,
700     FALSE                                   /* case_sensitive           */,
701     TRUE                                    /* skip_comment_multi       */,
702     TRUE                                    /* skip_comment_single      */,
703     TRUE                                    /* scan_comment_multi       */,
704     TRUE                                    /* scan_identifier          */,
705     FALSE                                   /* scan_identifier_1char    */,
706     FALSE                                   /* scan_identifier_NULL     */,
707     TRUE                                    /* scan_symbols             */,
708     FALSE                                   /* scan_binary              */,
709     TRUE                                    /* scan_octal               */,
710     TRUE                                    /* scan_float               */,
711     TRUE                                    /* scan_hex                 */,
712     FALSE                                   /* scan_hex_dollar          */,
713     TRUE                                    /* scan_string_sq           */,
714     TRUE                                    /* scan_string_dq           */,
715     TRUE                                    /* numbers_2_int            */,
716     FALSE                                   /* int_2_float              */,
717     FALSE                                   /* identifier_2_string      */,
718     TRUE                                    /* char_2_token             */,
719     FALSE                                   /* symbol_2_token           */,
720     FALSE                                   /* scope_0_fallback         */,
721 };
722
723 /*
724  * Sets the default display mode for all new windows using a string
725  */
726 void FGAPIENTRY glutInitDisplayString( char* displayMode )
727 {
728     /*
729      * display_string ::= (switch)
730      * switch         ::= capability [comparison value]
731      * comparison     ::= "=" | "!=" | "<" | ">" | "<=" | ">=" | "~"
732      * capability     ::= "alpha" | "acca" | "acc" | "blue" | "buffer" | "conformant" |
733      *                    "depth" | "double" | "green" | "index" | "num" | "red" | "rgba" |
734      *                    "rgb" | "luminance" | "stencil" | "single" | "stereo" |
735      *                    "samples" | "slow" | "win32pdf" | "xvisual" | "xstaticgray" |
736      *                    "xgrayscale" | "xstaticcolor" | "xpseudocolor" |
737      *                    "xtruecolor" | "xdirectcolor"
738      * value          ::= 0..9 [value]
739      *
740      * The display string grammar. This should be EBNF, but I couldn't find the definitions so, to
741      * clarify: (expr) means 0 or more times the expression, [expr] means 0 or 1 times expr.
742      *
743      * Create a new GLib lexical analyzer to process the display mode string
744      */
745     GScanner* scanner = g_scanner_new( &fgInitDisplayStringScannerConfig );
746     GList* caps = NULL;
747     gint i;
748
749     /*
750      * Fail if the display mode string is empty or the scanner failed to initialize
751      */
752     freeglut_return_if_fail( (scanner != NULL) && (strlen( displayMode ) > 0) );
753
754     /*
755      * Set the scanner's input name (for debugging)
756      */
757     scanner->input_name = "glutInitDisplayString";
758
759     /*
760      * Start the lexical analysis of the extensions string
761      */
762     g_scanner_input_text( scanner, displayMode, strlen( displayMode ) );
763
764     /*
765      * While there are any more tokens to be checked...
766      */
767     while( !g_scanner_eof( scanner ) )
768     {
769         /*
770          * Actually we're expecting only string tokens
771          */
772         GTokenType tokenType = g_scanner_get_next_token( scanner );
773
774         /*
775          * We are looking for identifiers
776          */
777         if( tokenType == G_TOKEN_IDENTIFIER )
778         {
779             gchar* capability  = NULL;  /* the capability identifier string (always present) */
780             gint   capID       =    0;  /* the capability identifier value (from g_Tokens)   */
781             gint   comparison  =    0;  /* the comparison operator value, see definitions    */
782             gchar* valueString = NULL;  /* if the previous one is present, this is needed    */
783             gint   value       =    0;  /* the integer value converted using a strtol call   */
784             SFG_Capability* capStruct;  /* the capability description structure              */
785
786             /*
787              * OK. The general rule of thumb that we always should be getting a capability identifier
788              * string (see the grammar description). If it is followed by a comparison identifier, then
789              * there must follow an integer value we're comparing the capability to...
790              *
791              * Have the current token analyzed with that in mind...
792              */
793             for( i=0; i<(gint) strlen( scanner->value.v_identifier ); i++ )
794             {
795                 gchar c = scanner->value.v_identifier[ i ];
796
797                 if( (c == '=') || (c == '!') || (c == '<') || (c == '>') || (c == '~') )
798                     break;
799             }
800
801             /*
802              * Here we go with the length of the capability identifier string.
803              * In the worst of cases, it is as long as the token identifier.
804              */
805             capability = g_strndup( scanner->value.v_identifier, i );
806
807             /*
808              * OK. Is there a chance for comparison and value identifiers to follow?
809              * Note: checking against i+1 as this handles two cases: single character
810              * comparison operator and first of value's digits, which must always be
811              * there, or the two-character comparison operators.
812              */
813             if( (i + 1) < (gint) strlen( scanner->value.v_identifier ) )
814             {
815                 /*
816                  * Yeah, indeed, it is the i-th character to start the identifier, then.
817                  */
818                 gchar c1 = scanner->value.v_identifier[ i + 0 ];
819                 gchar c2 = scanner->value.v_identifier[ i + 1 ];
820
821                 if( (c1 == '=')                ) { i += 1; comparison = FG_EQUAL;         } else
822                 if( (c1 == '!') && (c2 == '=') ) { i += 2; comparison = FG_NOT_EQUAL;     } else
823                 if( (c1 == '<') && (c2 == '=') ) { i += 2; comparison = FG_LESS_OR_EQUAL; } else
824                 if( (c1 == '>') && (c2 == '=') ) { i += 2; comparison = FG_MORE_OR_EQUAL; } else
825                 if( (c1 == '<')                ) { i += 1; comparison = FG_LESS;          } else
826                 if( (c1 == '>')                ) { i += 1; comparison = FG_MORE;          } else
827                 if( (c1 == '~')                ) { i += 1; comparison = FG_CLOSEST;       } else
828                 g_warning( "invalid comparison operator in token `%s'", scanner->value.v_identifier );
829             }
830
831             /*
832              * Grab the value string that must follow the comparison operator...
833              */
834             if( comparison != FG_NONE && i < (gint) strlen( scanner->value.v_identifier ) )
835                 valueString = g_strdup( scanner->value.v_identifier + i );
836
837             /*
838              * If there was a value string, convert it to integer...
839              */
840             if( comparison != FG_NONE && strlen( valueString ) > 0 )
841                 value = strtol( valueString, NULL, 0 );
842
843             /*
844              * Now we need to match the capability string and it's ID
845              */
846             for( i=0; g_Tokens[ i ]!=NULL; i++ )
847             {
848                 if( strcmp( capability, g_Tokens[ i ] ) == 0 )
849                 {
850                     /*
851                      * Looks like we've found the one we were looking for
852                      */
853                     capID = i;
854                     break;
855                 }
856             }
857
858             /*
859              * Create a new capability description structure
860              */
861             capStruct = g_new0( SFG_Capability, 1 );
862
863             /*
864              * Fill in the cap's values, as we have parsed it:
865              */
866             capStruct->capability =      capID;
867             capStruct->comparison = comparison;
868             capStruct->value      =      value;
869
870             /*
871              * Add the new capabaility to the caps list
872              */
873             caps = g_list_append( caps, capStruct );
874
875             /*
876              * Clean up the local mess and keep rolling on
877              */
878             g_free( valueString );
879             g_free( capability );
880         }
881     }
882
883     /*
884      * Now that we have converted the string into somewhat more machine-friendly
885      * form, proceed with matching the frame buffer configuration...
886      *
887      * The caps list could be passed to a function that would try finding the closest 
888      * matching pixel format, visual, frame buffer configuration or whatever. It would 
889      * be good to convert the glutInitDisplayMode() to use the same method.
890      */
891 #if 0
892     g_message( "found %i capability preferences", g_list_length( caps ) );
893
894     g_message( "token `%s': cap: %i, com: %i, val: %i",
895         scanner->value.v_identifier,
896         capStruct->capability,
897         capStruct->comparison,
898         capStruct->value
899     );
900 #endif
901
902     /*
903      * Free the capabilities we have parsed
904      */
905     for( i=0; i<(gint) g_list_length( caps ); i++ )
906         g_free( g_list_nth( caps, i )->data );
907
908     /*
909      * Destroy the capabilities list itself
910      */
911     g_list_free( caps );
912
913     /*
914      * Free the lexical scanner now...
915      */
916     g_scanner_destroy( scanner );
917 }
918 #endif
919
920 void FGAPIENTRY glutInitDisplayString( char* displayMode )
921 {
922 }
923
924 /*** END OF FILE ***/