freeglut_assert_ready is going to have to go at some point.
[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 #include "../include/GL/freeglut.h"
33 #include "freeglut_internal.h"
34
35 /*
36  * TODO BEFORE THE STABLE RELEASE:
37  *
38  *  fgDeinitialize()        -- Win32's OK, X11 needs the OS-specific
39  *                             deinitialization done
40  *  glutInitDisplayString() -- display mode string parsing
41  *
42  * Wouldn't it be cool to use gettext() for error messages? I just love
43  * bash saying  "nie znaleziono pliku" instead of "file not found" :)
44  * Is gettext easily portable?
45  */
46
47 /* -- GLOBAL VARIABLES ----------------------------------------------------- */
48
49 /*
50  * A structure pointed by g_pDisplay holds all information
51  * regarding the display, screen, root window etc.
52  */
53 SFG_Display fgDisplay;
54
55 /*
56  * The settings for the current freeglut session
57  */
58 SFG_State fgState = { { -1, -1, GL_FALSE },  /* Position */
59                       { 300, 300, GL_TRUE }, /* Size */
60                       GLUT_RGBA | GLUT_SINGLE | GLUT_DEPTH,  /* DisplayMode */
61                       GL_FALSE,              /* Initalized */
62                       GL_FALSE,              /* ForceDirectContext */
63                       GL_TRUE,               /* TryDirectContext */
64                       GL_FALSE,              /* ForceIconic */
65                       GL_FALSE,              /* UseCurrentContext */
66                       GL_FALSE,              /* GLDebugSwitch */
67                       GL_FALSE,              /* XSyncSwitch */
68                       GL_TRUE,               /* IgnoreKeyRepeat */
69                       0,                     /* FPSInterval */
70                       0,                     /* SwapCount */
71                       0,                     /* SwapTime */
72 #if TARGET_HOST_WIN32
73                       { 0, GL_FALSE },       /* Time */
74 #else
75                       { { 0, 0 }, GL_FALSE },
76 #endif
77                       { NULL, NULL },         /* Timers */
78                       NULL,                   /* IdleCallback */
79                       GL_FALSE,               /* BuildingAMenu */
80                       0,                      /* ActiveMenus */
81                       NULL,                   /* MenuStateCallback */
82                       NULL,                   /* MenuStatusCallback */
83                       { 640, 480, GL_TRUE },  /* GameModeSize */
84                       16,                     /* GameModeDepth */
85                       72,                     /* GameModeRefresh */
86                       GLUT_ACTION_EXIT,       /* ActionOnWindowClose */
87                       GLUT_EXEC_STATE_INIT    /* ExecState */
88 } ;
89
90
91 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
92
93 /*
94  * A call to this function should initialize all the display stuff...
95  */
96 void fgInitialize( const char* displayName )
97 {
98 #if TARGET_HOST_UNIX_X11
99     fgDisplay.Display = XOpenDisplay( displayName );
100
101     if( fgDisplay.Display == NULL )
102         fgError( "failed to open display '%s'", XDisplayName( displayName ) );
103
104     if( !glXQueryExtension( fgDisplay.Display, NULL, NULL ) )
105         fgError( "OpenGL GLX extension not supported by display '%s'",
106             XDisplayName( displayName ) );
107
108     fgDisplay.Screen = DefaultScreen( fgDisplay.Display );
109     fgDisplay.RootWindow = RootWindow(
110         fgDisplay.Display,
111         fgDisplay.Screen
112     );
113
114     fgDisplay.ScreenWidth  = DisplayWidth(
115         fgDisplay.Display,
116         fgDisplay.Screen
117     );
118     fgDisplay.ScreenHeight = DisplayHeight(
119         fgDisplay.Display,
120         fgDisplay.Screen
121     );
122
123     fgDisplay.ScreenWidthMM = DisplayWidthMM(
124         fgDisplay.Display,
125         fgDisplay.Screen
126     );
127     fgDisplay.ScreenHeightMM = DisplayHeightMM(
128         fgDisplay.Display,
129         fgDisplay.Screen
130     );
131
132     fgDisplay.Connection = ConnectionNumber( fgDisplay.Display );
133
134     /*
135      * Create the window deletion atom
136      */
137     fgDisplay.DeleteWindow = XInternAtom(
138         fgDisplay.Display,
139         "WM_DELETE_WINDOW",
140         FALSE
141     );
142
143 #elif TARGET_HOST_WIN32
144
145     WNDCLASS wc;
146     ATOM atom;
147
148     /*
149      * What we need to do is to initialize the fgDisplay global structure here...
150      */
151     fgDisplay.Instance = GetModuleHandle( NULL );
152
153     atom = GetClassInfo( fgDisplay.Instance, "FREEGLUT", &wc );
154     if( atom == 0 )
155     {
156         ZeroMemory( &wc, sizeof(WNDCLASS) );
157
158         /*
159          * Each of the windows should have its own device context...
160          */
161         wc.style          = CS_OWNDC;
162         wc.lpfnWndProc    = fgWindowProc;
163         wc.cbClsExtra     = 0;
164         wc.cbWndExtra     = 0;
165         wc.hInstance      = fgDisplay.Instance;
166         wc.hIcon          = LoadIcon( fgDisplay.Instance, "GLUT_ICON" );
167         if (!wc.hIcon)
168           wc.hIcon        = LoadIcon( NULL, IDI_WINLOGO );
169
170         wc.hCursor        = LoadCursor( NULL, IDC_ARROW );
171         wc.hbrBackground  = NULL;
172         wc.lpszMenuName   = NULL;
173         wc.lpszClassName  = "FREEGLUT";
174
175         /*
176          * Register the window class
177          */
178         atom = RegisterClass( &wc );
179         assert( atom );
180     }
181
182     /*
183      * The screen dimensions can be obtained via GetSystemMetrics() calls
184      */
185     fgDisplay.ScreenWidth  = GetSystemMetrics( SM_CXSCREEN );
186     fgDisplay.ScreenHeight = GetSystemMetrics( SM_CYSCREEN );
187
188     {
189         HWND desktop = GetDesktopWindow( );
190         HDC  context = GetDC( desktop );
191
192         fgDisplay.ScreenWidthMM  = GetDeviceCaps( context, HORZSIZE );
193         fgDisplay.ScreenHeightMM = GetDeviceCaps( context, VERTSIZE );
194
195         ReleaseDC( desktop, context );
196     }
197
198 #endif
199
200     fgJoystickInit( 0 );
201
202     fgState.Initalized = GL_TRUE;
203 }
204
205 /*
206  * Perform the freeglut deinitialization...
207  */
208 void fgDeinitialize( void )
209 {
210     SFG_Timer *timer;
211
212     if( !fgState.Initalized )
213     {
214         fgWarning( "fgDeinitialize(): "
215                    "no valid initialization has been performed" );
216         return;
217     }
218
219     /* fgState.Initalized = GL_FALSE; */
220
221     /*
222      * If there was a menu created, destroy the rendering context
223      */
224     if( fgStructure.MenuContext )
225     {
226         free( fgStructure.MenuContext );
227         fgStructure.MenuContext = NULL;
228     }
229
230     fgDestroyStructure( );
231
232     while( timer = ( SFG_Timer * )fgState.Timers.First )
233     {
234         fgListRemove ( &fgState.Timers, &timer->Node );
235         free( timer );
236     }
237
238     fgJoystickClose( );
239
240     fgState.Initalized = GL_FALSE;
241
242     fgState.Position.X = -1;
243     fgState.Position.Y = -1;
244     fgState.Position.Use = GL_FALSE;
245
246     fgState.Size.X = 300;
247     fgState.Size.Y = 300;
248     fgState.Size.Use = GL_TRUE;
249
250     fgState.DisplayMode = GLUT_RGBA | GLUT_SINGLE | GLUT_DEPTH;
251
252     fgState.ForceDirectContext  = GL_FALSE;
253     fgState.TryDirectContext    = GL_TRUE;
254     fgState.ForceIconic         = GL_FALSE;
255     fgState.UseCurrentContext   = GL_FALSE;
256     fgState.GLDebugSwitch       = GL_FALSE;
257     fgState.XSyncSwitch         = GL_FALSE;
258     fgState.ActionOnWindowClose = GLUT_ACTION_EXIT ;
259     fgState.ExecState           = GLUT_EXEC_STATE_INIT ;
260
261     fgState.IgnoreKeyRepeat = GL_TRUE;
262
263     fgState.GameModeSize.X  = 640;
264     fgState.GameModeSize.Y  = 480;
265     fgState.GameModeDepth   =  16;
266     fgState.GameModeRefresh =  72;
267
268     fgState.Time.Set = GL_FALSE;
269
270     fgState.Timers.First = fgState.Timers.Last = NULL;
271     fgState.IdleCallback = NULL;
272     fgState.MenuStateCallback = ( FGCBMenuState )NULL;
273     fgState.MenuStatusCallback = ( FGCBMenuStatus )NULL;
274
275     fgState.SwapCount   = 0;
276     fgState.SwapTime    = 0;
277     fgState.FPSInterval = 0;
278
279     if( fgState.ProgramName )
280     {
281         free( fgState.ProgramName );
282         fgState.ProgramName = NULL;
283     }
284     
285
286 #if TARGET_HOST_UNIX_X11
287
288     /*
289      * Make sure all X-client data we have created will be destroyed on
290      * display closing
291      */
292     XSetCloseDownMode( fgDisplay.Display, DestroyAll );
293
294     /*
295      * Close the display connection, destroying all windows we have
296      * created so far
297      */
298     XCloseDisplay( fgDisplay.Display );
299
300 #endif
301 }
302
303
304 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
305
306 /*
307  * Perform initialization. This usually happens on the program startup
308  * and restarting after glutMainLoop termination...
309  */
310 void FGAPIENTRY glutInit( int* pargc, char** argv )
311 {
312     char* displayName = NULL;
313     int i, j, argc = *pargc;
314
315     if (pargc && *pargc && argv && *argv && **argv)
316         fgState.ProgramName = strdup (*argv);
317     else
318         fgState.ProgramName = strdup ("");
319     if( !fgState.ProgramName )
320         fgError ("Could not allocate space for the program's name.");
321
322     if( fgState.Initalized )
323         fgError( "illegal glutInit() reinitialization attemp" );
324
325     fgCreateStructure( );
326
327     fgElapsedTime( );
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 )
407                 fgError( "parameters ambiguity, -direct and -indirect "
408                     "cannot be both specified" );
409
410             fgState.ForceDirectContext = GL_TRUE;
411             argv[ i ] = NULL;
412             ( *pargc )--;
413         }
414         else if( strcmp( argv[ i ], "-indirect" ) == 0 )
415         {
416             if( fgState.ForceDirectContext )
417                 fgError( "parameters ambiguity, -direct and -indirect "
418                     "cannot be both specified" );
419
420             fgState.TryDirectContext = GL_FALSE;
421             argv[ i ] = NULL;
422             (*pargc)--;
423         }
424         else if( strcmp( argv[ i ], "-iconic" ) == 0 )
425         {
426             fgState.ForceIconic = GL_TRUE;
427             argv[ i ] = NULL;
428             ( *pargc )--;
429         }
430         else if( strcmp( argv[ i ], "-gldebug" ) == 0 )
431         {
432             fgState.GLDebugSwitch = GL_TRUE;
433             argv[ i ] = NULL;
434             ( *pargc )--;
435         }
436         else if( strcmp( argv[ i ], "-sync" ) == 0 )
437         {
438             fgState.XSyncSwitch = GL_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 = GL_FALSE;
477
478     if( fgState.Size.X < 0 || fgState.Size.Y < 0 )
479         fgState.Size.Use = GL_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 = GL_TRUE;
495     }
496     else
497     {
498         fgState.Position.X   =       -1;
499         fgState.Position.Y   =       -1;
500         fgState.Position.Use = GL_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 = GL_TRUE;
514     }
515     else
516     {
517         fgState.Size.X   =       -1;
518         fgState.Size.Y   =       -1;
519         fgState.Size.Use = GL_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 ***/