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