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