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