abb5c019029a85962eed7ba1a99e49e42efcc96b
[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         GLboolean 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         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 #ifndef WIN32
353     gettimeofday(&fgState.Time.Value, NULL);
354 #else
355     fgState.Time.Value = timeGetTime();
356 #endif
357     fgState.Time.Set = TRUE;
358
359     /*
360      * Grab the environment variable indicating the X display to use.
361      * This is harmless under Win32, so let's let it stay here...
362      */
363     displayName = strdup( getenv( "DISPLAY" ) );
364
365     /*
366      * Have the program arguments parsed.
367      */
368     for( i=1; i<argc; i++ )
369     {
370         /*
371          * The X display name settings
372          */
373         if( strcmp( argv[ i ], "-display" ) == 0 )
374         {
375             /*
376              * Check for possible lack of the next argument
377              */
378             if( ++i >= argc )
379                 fgError( "-display parameter must be followed by display name" );
380
381             /*
382              * Release the previous display name (the one from app's environment)
383              */
384             free( displayName );
385
386             /*
387              * Make a working copy of the name for us to use
388              */
389             displayName = strdup( argv[ i ] );
390
391             /*
392              * Have both arguments removed
393              */
394             argv[ i - 1 ] = NULL;
395             argv[   i   ] = NULL;
396             (* pargc) -= 2;
397         }
398
399         /*
400          * The geometry settings
401          */
402         else if( strcmp( argv[ i ], "-geometry" ) == 0 )
403         {
404             /*
405              * Again, check if there is at least one more argument
406              */
407             if( ++i >= argc )
408                 fgError( "-geometry parameter must be followed by window geometry settings" );
409
410             /*
411              * Otherwise make a duplicate of the geometry settings...
412              */
413             geometrySettings = strdup( argv[ i ] );
414
415             /*
416              * Have both arguments removed
417              */
418             argv[ i - 1 ] = NULL;
419             argv[   i   ] = NULL;
420             (* pargc) -= 2;
421         }
422
423         /*
424          * The direct/indirect OpenGL contexts settings
425          */
426         else if( strcmp( argv[ i ], "-direct" ) == 0)
427         {
428             /*
429              * We try to force direct rendering...
430              */
431             if( fgState.TryDirectContext == FALSE )
432                 fgError( "parameters ambiguity, -direct and -indirect cannot be both specified" );
433
434             fgState.ForceDirectContext = TRUE;
435             argv[ i ] = NULL;
436             (* pargc)--;
437         }
438         else if( strcmp( argv[ i ], "-indirect" ) == 0 )
439         {
440             /*
441              * We try to force indirect rendering...
442              */
443             if( fgState.ForceDirectContext == TRUE )
444                 fgError( "parameters ambiguity, -direct and -indirect cannot be both specified" );
445
446             fgState.TryDirectContext = FALSE;
447             argv[ i ] = NULL;
448             (* pargc)--;
449         }
450
451         /*
452          * The '-iconic' parameter makes all new top-level
453          * windows created in iconified state...
454          */
455         else if( strcmp( argv[ i ], "-iconic" ) == 0 )
456         {
457             fgState.ForceIconic = TRUE;
458             argv[ i ] = NULL;
459             (* pargc)--;
460         }
461
462         /*
463          * The '-gldebug' option activates some OpenGL state debugging features
464          */
465         else if( strcmp( argv[ i ], "-gldebug" ) == 0 )
466         {
467             fgState.GLDebugSwitch = TRUE;
468             argv[ i ] = NULL;
469             (* pargc)--;
470         }
471
472         /*
473          * The '-sync' option activates X protocol synchronization (for debugging purposes)
474          */
475         else if( strcmp( argv[ i ], "-sync" ) == 0 )
476         {
477             fgState.XSyncSwitch = TRUE;
478             argv[ i ] = NULL;
479             (* pargc)--;
480         }
481     }
482
483     /*
484      * Have the arguments list compacted now
485      */
486     for( i=1; i<argc; i++ )
487     {
488         if( argv[ i ] == NULL )
489         {
490             for( j=i; j<argc; j++ )
491             {
492                 if( argv[ j ] != NULL )
493                 {
494                     argv[ i ] = argv[ j ];
495                     argv[ j ] = NULL;
496                 }
497             }
498         }
499     }
500
501     /*
502      * Have the display created now. As I am too lazy to implement
503      * the program arguments parsing, we will have the DISPLAY
504      * environment variable used for opening the X display:
505      */
506     fgInitialize( displayName );
507
508 #if TARGET_HOST_UNIX_X11
509     /*
510      * We can process the default window geometry settings safely now
511      *
512      * WARNING: have this rewritten to be portable. That won't be hard.
513      */
514     if( geometrySettings != NULL )
515     {
516         int result, x, y;
517         unsigned int w, h;
518
519         /*
520          * Have the settings parsed now. This is easy.
521          * We will use the XParseGeometry function.
522          */
523         result = XParseGeometry( geometrySettings, &x, &y, &w, &h );
524
525         /*
526          * Check what we have been supplied with...
527          */
528         if( (result & WidthValue) && (w >= 0) )
529             fgState.Size.X = w;
530
531         if( (result & HeightValue) && (h >= 0) )
532             fgState.Size.Y = h;
533
534         if( result & XValue )
535         {
536             if( result & XNegative )
537                 fgState.Position.X = fgDisplay.ScreenWidth + x - fgState.Size.X;
538             else
539                 fgState.Position.X = x;
540         }
541
542         if( result & YValue )
543         {
544             if( result & YNegative )
545                 fgState.Position.Y = fgDisplay.ScreenHeight + y - fgState.Size.Y;
546             else
547                 fgState.Position.Y = y;
548         }
549
550         /*
551          * Free the geometry settings string
552          */
553         free( geometrySettings );
554     }
555 #endif
556
557     /*
558      * Check for the minus one settings for both position and size...
559      */
560     if( fgState.Position.X < 0 || fgState.Position.Y < 0 )
561         fgState.Position.Use = FALSE;
562
563     if( fgState.Size.X < 0 || fgState.Size.Y < 0 )
564         fgState.Size.Use = FALSE;
565
566     /*
567      * Do not forget about releasing the display name string
568      */
569     free( displayName );
570 }
571
572 /*
573  * Sets the default initial window position for new windows
574  */
575 void FGAPIENTRY glutInitWindowPosition( int x, int y )
576 {
577     /*
578      * The settings can be disables when both coordinates are negative
579      */
580     if( (x >= 0) && (y >= 0) )
581     {
582         /*
583          * We want to specify the initial position of each of the windows
584          */
585         fgState.Position.X   =    x;
586         fgState.Position.Y   =    y;
587         fgState.Position.Use = TRUE;
588     }
589     else
590     {
591         /*
592          * The initial position of each of the windows is specified by the wm
593          */
594         fgState.Position.X   =    -1;
595         fgState.Position.Y   =    -1;
596         fgState.Position.Use = FALSE;
597     }
598 }
599
600 /*
601  * Sets the default initial window size for new windows
602  */
603 void FGAPIENTRY glutInitWindowSize( int width, int height )
604 {
605     /*
606      * The settings can be disables when both values are negative
607      */
608     if( (width >= 0) && (height >= 0) )
609     {
610         /*
611          * We want to specify the initial size of each of the windows
612          */
613         fgState.Size.X   =  width;
614         fgState.Size.Y   = height;
615         fgState.Size.Use =   TRUE;
616     }
617     else
618     {
619         /*
620          * The initial size of each of the windows is specified by the wm
621          */
622         fgState.Size.X   =    -1;
623         fgState.Size.Y   =    -1;
624         fgState.Size.Use = FALSE;
625     }
626 }
627
628 /*
629  * Sets the default display mode for all new windows
630  */
631 void FGAPIENTRY glutInitDisplayMode( int displayMode )
632 {
633     /*
634      * We will make use of this value when creating a new OpenGL context...
635      */
636     fgState.DisplayMode = displayMode;
637 }
638
639
640 /* -- INIT DISPLAY STRING PARSING ------------------------------------------ */
641
642 #if 0 /* FIXME: CJP */
643 /*
644  * There is a discrete number of comparison operators we can encounter:
645  *
646  *     comparison ::= "=" | "!=" | "<" | ">" | "<=" | ">=" | "~"
647  */
648 #define  FG_NONE           0x0000
649 #define  FG_EQUAL          0x0001
650 #define  FG_NOT_EQUAL      0x0002
651 #define  FG_LESS           0x0003
652 #define  FG_MORE           0x0004
653 #define  FG_LESS_OR_EQUAL  0x0005
654 #define  FG_MORE_OR_EQUAL  0x0006
655 #define  FG_CLOSEST        0x0007
656
657 /*
658  * The caller can feed us with a number of capability tokens:
659  *
660  * capability ::= "alpha" | "acca" | "acc" | "blue" | "buffer" | "conformant" | "depth" | "double" |
661  *                "green" | "index" | "num" | "red" | "rgba" | "rgb" | "luminance" | "stencil" |
662  *                "single" | "stereo" | "samples" | "slow" | "win32pdf" | "xvisual" | "xstaticgray" |
663  *                "xgrayscale" | "xstaticcolor" | "xpseudocolor" | "xtruecolor" | "xdirectcolor"
664  */
665 static gchar* g_Tokens[] =
666 {
667     "none", "alpha", "acca", "acc", "blue", "buffer", "conformant", "depth", "double", "green",
668     "index", "num", "red", "rgba", "rgb", "luminance", "stencil", "single", "stereo", "samples",
669     "slow", "win32pdf", "xvisual", "xstaticgray", "xgrayscale", "xstaticcolor", "xpseudocolor",
670     "xtruecolor", "xdirectcolor", NULL
671 };
672
673 /*
674  * The structure to hold the parsed display string tokens
675  */
676 typedef struct tagSFG_Capability SFG_Capability;
677 struct tagSFG_Capability
678 {
679     gint capability;        /* the capability token enumerator */
680     gint comparison;        /* the comparison operator used    */
681     gint value;             /* the value we're comparing to    */
682 };
683
684 /*
685  * The scanner configuration for the init display string
686  */
687 static GScannerConfig fgInitDisplayStringScannerConfig =
688 {
689     ( " \t\r\n" )               /* cset_skip_characters     */,
690     (
691         G_CSET_a_2_z
692         "_"
693         G_CSET_A_2_Z
694     )                                   /* cset_identifier_first    */,
695     (
696         G_CSET_a_2_z
697         "_0123456789"
698         G_CSET_A_2_Z
699         G_CSET_LATINS
700         G_CSET_LATINC
701         "<>!=~"
702     )                                   /* cset_identifier_nth      */,
703     ( "#\n" )                       /* cpair_comment_single     */,
704     FALSE                                   /* case_sensitive           */,
705     TRUE                                    /* skip_comment_multi       */,
706     TRUE                                    /* skip_comment_single      */,
707     TRUE                                    /* scan_comment_multi       */,
708     TRUE                                    /* scan_identifier          */,
709     FALSE                                   /* scan_identifier_1char    */,
710     FALSE                                   /* scan_identifier_NULL     */,
711     TRUE                                    /* scan_symbols             */,
712     FALSE                                   /* scan_binary              */,
713     TRUE                                    /* scan_octal               */,
714     TRUE                                    /* scan_float               */,
715     TRUE                                    /* scan_hex                 */,
716     FALSE                                   /* scan_hex_dollar          */,
717     TRUE                                    /* scan_string_sq           */,
718     TRUE                                    /* scan_string_dq           */,
719     TRUE                                    /* numbers_2_int            */,
720     FALSE                                   /* int_2_float              */,
721     FALSE                                   /* identifier_2_string      */,
722     TRUE                                    /* char_2_token             */,
723     FALSE                                   /* symbol_2_token           */,
724     FALSE                                   /* scope_0_fallback         */,
725 };
726
727 /*
728  * Sets the default display mode for all new windows using a string
729  */
730 void FGAPIENTRY glutInitDisplayString( char* displayMode )
731 {
732     /*
733      * display_string ::= (switch)
734      * switch         ::= capability [comparison value]
735      * comparison     ::= "=" | "!=" | "<" | ">" | "<=" | ">=" | "~"
736      * capability     ::= "alpha" | "acca" | "acc" | "blue" | "buffer" | "conformant" |
737      *                    "depth" | "double" | "green" | "index" | "num" | "red" | "rgba" |
738      *                    "rgb" | "luminance" | "stencil" | "single" | "stereo" |
739      *                    "samples" | "slow" | "win32pdf" | "xvisual" | "xstaticgray" |
740      *                    "xgrayscale" | "xstaticcolor" | "xpseudocolor" |
741      *                    "xtruecolor" | "xdirectcolor"
742      * value          ::= 0..9 [value]
743      *
744      * The display string grammar. This should be EBNF, but I couldn't find the definitions so, to
745      * clarify: (expr) means 0 or more times the expression, [expr] means 0 or 1 times expr.
746      *
747      * Create a new GLib lexical analyzer to process the display mode string
748      */
749     GScanner* scanner = g_scanner_new( &fgInitDisplayStringScannerConfig );
750     GList* caps = NULL;
751     gint i;
752
753     /*
754      * Fail if the display mode string is empty or the scanner failed to initialize
755      */
756     freeglut_return_if_fail( (scanner != NULL) && (strlen( displayMode ) > 0) );
757
758     /*
759      * Set the scanner's input name (for debugging)
760      */
761     scanner->input_name = "glutInitDisplayString";
762
763     /*
764      * Start the lexical analysis of the extensions string
765      */
766     g_scanner_input_text( scanner, displayMode, strlen( displayMode ) );
767
768     /*
769      * While there are any more tokens to be checked...
770      */
771     while( !g_scanner_eof( scanner ) )
772     {
773         /*
774          * Actually we're expecting only string tokens
775          */
776         GTokenType tokenType = g_scanner_get_next_token( scanner );
777
778         /*
779          * We are looking for identifiers
780          */
781         if( tokenType == G_TOKEN_IDENTIFIER )
782         {
783             gchar* capability  = NULL;  /* the capability identifier string (always present) */
784             gint   capID       =    0;  /* the capability identifier value (from g_Tokens)   */
785             gint   comparison  =    0;  /* the comparison operator value, see definitions    */
786             gchar* valueString = NULL;  /* if the previous one is present, this is needed    */
787             gint   value       =    0;  /* the integer value converted using a strtol call   */
788             SFG_Capability* capStruct;  /* the capability description structure              */
789
790             /*
791              * OK. The general rule of thumb that we always should be getting a capability identifier
792              * string (see the grammar description). If it is followed by a comparison identifier, then
793              * there must follow an integer value we're comparing the capability to...
794              *
795              * Have the current token analyzed with that in mind...
796              */
797             for( i=0; i<(gint) strlen( scanner->value.v_identifier ); i++ )
798             {
799                 gchar c = scanner->value.v_identifier[ i ];
800
801                 if( (c == '=') || (c == '!') || (c == '<') || (c == '>') || (c == '~') )
802                     break;
803             }
804
805             /*
806              * Here we go with the length of the capability identifier string.
807              * In the worst of cases, it is as long as the token identifier.
808              */
809             capability = g_strndup( scanner->value.v_identifier, i );
810
811             /*
812              * OK. Is there a chance for comparison and value identifiers to follow?
813              * Note: checking against i+1 as this handles two cases: single character
814              * comparison operator and first of value's digits, which must always be
815              * there, or the two-character comparison operators.
816              */
817             if( (i + 1) < (gint) strlen( scanner->value.v_identifier ) )
818             {
819                 /*
820                  * Yeah, indeed, it is the i-th character to start the identifier, then.
821                  */
822                 gchar c1 = scanner->value.v_identifier[ i + 0 ];
823                 gchar c2 = scanner->value.v_identifier[ i + 1 ];
824
825                 if( (c1 == '=')                ) { i += 1; comparison = FG_EQUAL;         } else
826                 if( (c1 == '!') && (c2 == '=') ) { i += 2; comparison = FG_NOT_EQUAL;     } else
827                 if( (c1 == '<') && (c2 == '=') ) { i += 2; comparison = FG_LESS_OR_EQUAL; } else
828                 if( (c1 == '>') && (c2 == '=') ) { i += 2; comparison = FG_MORE_OR_EQUAL; } else
829                 if( (c1 == '<')                ) { i += 1; comparison = FG_LESS;          } else
830                 if( (c1 == '>')                ) { i += 1; comparison = FG_MORE;          } else
831                 if( (c1 == '~')                ) { i += 1; comparison = FG_CLOSEST;       } else
832                 g_warning( "invalid comparison operator in token `%s'", scanner->value.v_identifier );
833             }
834
835             /*
836              * Grab the value string that must follow the comparison operator...
837              */
838             if( comparison != FG_NONE && i < (gint) strlen( scanner->value.v_identifier ) )
839                 valueString = g_strdup( scanner->value.v_identifier + i );
840
841             /*
842              * If there was a value string, convert it to integer...
843              */
844             if( comparison != FG_NONE && strlen( valueString ) > 0 )
845                 value = strtol( valueString, NULL, 0 );
846
847             /*
848              * Now we need to match the capability string and it's ID
849              */
850             for( i=0; g_Tokens[ i ]!=NULL; i++ )
851             {
852                 if( strcmp( capability, g_Tokens[ i ] ) == 0 )
853                 {
854                     /*
855                      * Looks like we've found the one we were looking for
856                      */
857                     capID = i;
858                     break;
859                 }
860             }
861
862             /*
863              * Create a new capability description structure
864              */
865             capStruct = g_new0( SFG_Capability, 1 );
866
867             /*
868              * Fill in the cap's values, as we have parsed it:
869              */
870             capStruct->capability =      capID;
871             capStruct->comparison = comparison;
872             capStruct->value      =      value;
873
874             /*
875              * Add the new capabaility to the caps list
876              */
877             caps = g_list_append( caps, capStruct );
878
879             /*
880              * Clean up the local mess and keep rolling on
881              */
882             g_free( valueString );
883             g_free( capability );
884         }
885     }
886
887     /*
888      * Now that we have converted the string into somewhat more machine-friendly
889      * form, proceed with matching the frame buffer configuration...
890      *
891      * The caps list could be passed to a function that would try finding the closest 
892      * matching pixel format, visual, frame buffer configuration or whatever. It would 
893      * be good to convert the glutInitDisplayMode() to use the same method.
894      */
895 #if 0
896     g_message( "found %i capability preferences", g_list_length( caps ) );
897
898     g_message( "token `%s': cap: %i, com: %i, val: %i",
899         scanner->value.v_identifier,
900         capStruct->capability,
901         capStruct->comparison,
902         capStruct->value
903     );
904 #endif
905
906     /*
907      * Free the capabilities we have parsed
908      */
909     for( i=0; i<(gint) g_list_length( caps ); i++ )
910         g_free( g_list_nth( caps, i )->data );
911
912     /*
913      * Destroy the capabilities list itself
914      */
915     g_list_free( caps );
916
917     /*
918      * Free the lexical scanner now...
919      */
920     g_scanner_destroy( scanner );
921 }
922 #endif
923
924 void FGAPIENTRY glutInitDisplayString( char* displayMode )
925 {
926 }
927
928 /*** END OF FILE ***/