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