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