Changed all references to the 'freeglut-1.3' directory to 'src', copied 'freeglut...
[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, /* 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     /* check if GLUT_FPS env var is set */
396     {
397         const char *fps = getenv("GLUT_FPS");
398         if (fps) {
399             sscanf(fps, "%d", &fgState.FPSInterval);
400             if (fgState.FPSInterval <= 0)
401                 fgState.FPSInterval = 5000;  /* 5000 milliseconds */
402         }
403     }
404
405     /*
406      * Grab the environment variable indicating the X display to use.
407      * This is harmless under Win32, so let's let it stay here...
408      */
409 #if TARGET_HOST_WIN32
410     if ( !getenv ( "DISPLAY" ) )
411       displayName = strdup ( "" ) ;
412     else
413 #endif
414     displayName = strdup( getenv( "DISPLAY" ) );
415
416     /*
417      * Have the program arguments parsed.
418      */
419     for( i=1; i<argc; i++ )
420     {
421         /*
422          * The X display name settings
423          */
424         if( strcmp( argv[ i ], "-display" ) == 0 )
425         {
426             /*
427              * Check for possible lack of the next argument
428              */
429             if( ++i >= argc )
430                 fgError( "-display parameter must be followed by display name" );
431
432             /*
433              * Release the previous display name (the one from app's environment)
434              */
435             free( displayName );
436
437             /*
438              * Make a working copy of the name for us to use
439              */
440             displayName = strdup( argv[ i ] );
441
442             /*
443              * Have both arguments removed
444              */
445             argv[ i - 1 ] = NULL;
446             argv[   i   ] = NULL;
447             (* pargc) -= 2;
448         }
449
450         /*
451          * The geometry settings
452          */
453         else if( strcmp( argv[ i ], "-geometry" ) == 0 )
454         {
455           int result, x, y;
456           unsigned int w, h;
457
458           /*
459            * Again, check if there is at least one more argument
460            */
461           if ( ++i >= argc )
462             fgError( "-geometry parameter must be followed by window geometry settings" );
463
464           /*
465            * Otherwise scan the geometry settings...
466            */
467           result = sscanf ( argv[i], "%dx%d+%d+%d", &x, &y, &w, &h );
468
469           /*
470            * Check what we have been supplied with...
471            */
472           if ( result > 3 )
473             fgState.Size.Y = h ;
474
475           if ( result > 2 )
476             fgState.Size.X = w ;
477
478           if( result > 1 )
479           {
480             if( y < 0 )
481               fgState.Position.Y = fgDisplay.ScreenHeight + y - fgState.Size.Y;
482             else
483               fgState.Position.Y = y;
484           }
485
486           if( result > 0 )
487           {
488             if( x < 0 )
489               fgState.Position.X = fgDisplay.ScreenWidth + x - fgState.Size.X;
490             else
491               fgState.Position.X = x;
492           }
493
494           /*
495            * Have both arguments removed
496            */
497           argv[ i - 1 ] = NULL;
498           argv[   i   ] = NULL;
499           (* pargc) -= 2;
500         }
501
502         /*
503          * The direct/indirect OpenGL contexts settings
504          */
505         else if( strcmp( argv[ i ], "-direct" ) == 0)
506         {
507             /*
508              * We try to force direct rendering...
509              */
510             if( fgState.TryDirectContext == FALSE )
511                 fgError( "parameters ambiguity, -direct and -indirect cannot be both specified" );
512
513             fgState.ForceDirectContext = TRUE;
514             argv[ i ] = NULL;
515             (* pargc)--;
516         }
517         else if( strcmp( argv[ i ], "-indirect" ) == 0 )
518         {
519             /*
520              * We try to force indirect rendering...
521              */
522             if( fgState.ForceDirectContext == TRUE )
523                 fgError( "parameters ambiguity, -direct and -indirect cannot be both specified" );
524
525             fgState.TryDirectContext = FALSE;
526             argv[ i ] = NULL;
527             (* pargc)--;
528         }
529
530         /*
531          * The '-iconic' parameter makes all new top-level
532          * windows created in iconified state...
533          */
534         else if( strcmp( argv[ i ], "-iconic" ) == 0 )
535         {
536             fgState.ForceIconic = TRUE;
537             argv[ i ] = NULL;
538             (* pargc)--;
539         }
540
541         /*
542          * The '-gldebug' option activates some OpenGL state debugging features
543          */
544         else if( strcmp( argv[ i ], "-gldebug" ) == 0 )
545         {
546             fgState.GLDebugSwitch = TRUE;
547             argv[ i ] = NULL;
548             (* pargc)--;
549         }
550
551         /*
552          * The '-sync' option activates X protocol synchronization (for debugging purposes)
553          */
554         else if( strcmp( argv[ i ], "-sync" ) == 0 )
555         {
556             fgState.XSyncSwitch = TRUE;
557             argv[ i ] = NULL;
558             (* pargc)--;
559         }
560     }
561
562     /*
563      * Have the arguments list compacted now
564      */
565     j = 2 ;
566     for( i = 1; i < *pargc; i++, j++ )
567     {
568       if( argv[ i ] == NULL )
569       {
570         while ( argv[j] == NULL ) j++ ;  /* Guaranteed to end because there are "*pargc" arguments left */
571         argv[i] = argv[j] ;
572       }
573     }
574
575     /*
576      * Have the display created now. As I am too lazy to implement
577      * the program arguments parsing, we will have the DISPLAY
578      * environment variable used for opening the X display:
579      */
580     fgInitialize( displayName );
581
582     /*
583      * Check for the minus one settings for both position and size...
584      */
585     if( fgState.Position.X < 0 || fgState.Position.Y < 0 )
586         fgState.Position.Use = FALSE;
587
588     if( fgState.Size.X < 0 || fgState.Size.Y < 0 )
589         fgState.Size.Use = FALSE;
590
591     /*
592      * Do not forget about releasing the display name string
593      */
594     free( displayName );
595 }
596
597 /*
598  * Sets the default initial window position for new windows
599  */
600 void FGAPIENTRY glutInitWindowPosition( int x, int y )
601 {
602     /*
603      * The settings can be disables when both coordinates are negative
604      */
605     if( (x >= 0) && (y >= 0) )
606     {
607         /*
608          * We want to specify the initial position of each of the windows
609          */
610         fgState.Position.X   =    x;
611         fgState.Position.Y   =    y;
612         fgState.Position.Use = TRUE;
613     }
614     else
615     {
616         /*
617          * The initial position of each of the windows is specified by the wm
618          */
619         fgState.Position.X   =    -1;
620         fgState.Position.Y   =    -1;
621         fgState.Position.Use = FALSE;
622     }
623 }
624
625 /*
626  * Sets the default initial window size for new windows
627  */
628 void FGAPIENTRY glutInitWindowSize( int width, int height )
629 {
630     /*
631      * The settings can be disables when both values are negative
632      */
633     if( (width > 0) && (height > 0) )
634     {
635         /*
636          * We want to specify the initial size of each of the windows
637          */
638         fgState.Size.X   =  width;
639         fgState.Size.Y   = height;
640         fgState.Size.Use =   TRUE;
641     }
642     else
643     {
644         /*
645          * The initial size of each of the windows is specified by the wm (officially this is an error condition)
646          */
647         fgState.Size.X   =    -1;
648         fgState.Size.Y   =    -1;
649         fgState.Size.Use = FALSE;
650     }
651 }
652
653 /*
654  * Sets the default display mode for all new windows
655  */
656 void FGAPIENTRY glutInitDisplayMode( unsigned int displayMode )
657 {
658     /*
659      * We will make use of this value when creating a new OpenGL context...
660      */
661     fgState.DisplayMode = displayMode;
662 }
663
664
665 /* -- INIT DISPLAY STRING PARSING ------------------------------------------ */
666
667 #if 0 /* FIXME: CJP */
668 /*
669  * There is a discrete number of comparison operators we can encounter:
670  *
671  *     comparison ::= "=" | "!=" | "<" | ">" | "<=" | ">=" | "~"
672  */
673 #define  FG_NONE           0x0000
674 #define  FG_EQUAL          0x0001
675 #define  FG_NOT_EQUAL      0x0002
676 #define  FG_LESS           0x0003
677 #define  FG_MORE           0x0004
678 #define  FG_LESS_OR_EQUAL  0x0005
679 #define  FG_MORE_OR_EQUAL  0x0006
680 #define  FG_CLOSEST        0x0007
681
682 /*
683  * The caller can feed us with a number of capability tokens:
684  *
685  * capability ::= "alpha" | "acca" | "acc" | "blue" | "buffer" | "conformant" | "depth" | "double" |
686  *                "green" | "index" | "num" | "red" | "rgba" | "rgb" | "luminance" | "stencil" |
687  *                "single" | "stereo" | "samples" | "slow" | "win32pdf" | "xvisual" | "xstaticgray" |
688  *                "xgrayscale" | "xstaticcolor" | "xpseudocolor" | "xtruecolor" | "xdirectcolor"
689  */
690 static gchar* g_Tokens[] =
691 {
692     "none", "alpha", "acca", "acc", "blue", "buffer", "conformant", "depth", "double", "green",
693     "index", "num", "red", "rgba", "rgb", "luminance", "stencil", "single", "stereo", "samples",
694     "slow", "win32pdf", "xvisual", "xstaticgray", "xgrayscale", "xstaticcolor", "xpseudocolor",
695     "xtruecolor", "xdirectcolor", NULL
696 };
697
698 /*
699  * The structure to hold the parsed display string tokens
700  */
701 typedef struct tagSFG_Capability SFG_Capability;
702 struct tagSFG_Capability
703 {
704     gint capability;        /* the capability token enumerator */
705     gint comparison;        /* the comparison operator used    */
706     gint value;             /* the value we're comparing to    */
707 };
708
709 /*
710  * The scanner configuration for the init display string
711  */
712 static GScannerConfig fgInitDisplayStringScannerConfig =
713 {
714     ( " \t\r\n" )               /* cset_skip_characters     */,
715     (
716         G_CSET_a_2_z
717         "_"
718         G_CSET_A_2_Z
719     )                                   /* cset_identifier_first    */,
720     (
721         G_CSET_a_2_z
722         "_0123456789"
723         G_CSET_A_2_Z
724         G_CSET_LATINS
725         G_CSET_LATINC
726         "<>!=~"
727     )                                   /* cset_identifier_nth      */,
728     ( "#\n" )                       /* cpair_comment_single     */,
729     FALSE                                   /* case_sensitive           */,
730     TRUE                                    /* skip_comment_multi       */,
731     TRUE                                    /* skip_comment_single      */,
732     TRUE                                    /* scan_comment_multi       */,
733     TRUE                                    /* scan_identifier          */,
734     FALSE                                   /* scan_identifier_1char    */,
735     FALSE                                   /* scan_identifier_NULL     */,
736     TRUE                                    /* scan_symbols             */,
737     FALSE                                   /* scan_binary              */,
738     TRUE                                    /* scan_octal               */,
739     TRUE                                    /* scan_float               */,
740     TRUE                                    /* scan_hex                 */,
741     FALSE                                   /* scan_hex_dollar          */,
742     TRUE                                    /* scan_string_sq           */,
743     TRUE                                    /* scan_string_dq           */,
744     TRUE                                    /* numbers_2_int            */,
745     FALSE                                   /* int_2_float              */,
746     FALSE                                   /* identifier_2_string      */,
747     TRUE                                    /* char_2_token             */,
748     FALSE                                   /* symbol_2_token           */,
749     FALSE                                   /* scope_0_fallback         */,
750 };
751
752 /*
753  * Sets the default display mode for all new windows using a string
754  */
755 void FGAPIENTRY glutInitDisplayString( char* displayMode )
756 {
757     /*
758      * display_string ::= (switch)
759      * switch         ::= capability [comparison value]
760      * comparison     ::= "=" | "!=" | "<" | ">" | "<=" | ">=" | "~"
761      * capability     ::= "alpha" | "acca" | "acc" | "blue" | "buffer" | "conformant" |
762      *                    "depth" | "double" | "green" | "index" | "num" | "red" | "rgba" |
763      *                    "rgb" | "luminance" | "stencil" | "single" | "stereo" |
764      *                    "samples" | "slow" | "win32pdf" | "xvisual" | "xstaticgray" |
765      *                    "xgrayscale" | "xstaticcolor" | "xpseudocolor" |
766      *                    "xtruecolor" | "xdirectcolor"
767      * value          ::= 0..9 [value]
768      *
769      * The display string grammar. This should be EBNF, but I couldn't find the definitions so, to
770      * clarify: (expr) means 0 or more times the expression, [expr] means 0 or 1 times expr.
771      *
772      * Create a new GLib lexical analyzer to process the display mode string
773      */
774     GScanner* scanner = g_scanner_new( &fgInitDisplayStringScannerConfig );
775     GList* caps = NULL;
776     gint i;
777
778     /*
779      * Fail if the display mode string is empty or the scanner failed to initialize
780      */
781     freeglut_return_if_fail( (scanner != NULL) && (strlen( displayMode ) > 0) );
782
783     /*
784      * Set the scanner's input name (for debugging)
785      */
786     scanner->input_name = "glutInitDisplayString";
787
788     /*
789      * Start the lexical analysis of the extensions string
790      */
791     g_scanner_input_text( scanner, displayMode, strlen( displayMode ) );
792
793     /*
794      * While there are any more tokens to be checked...
795      */
796     while( !g_scanner_eof( scanner ) )
797     {
798         /*
799          * Actually we're expecting only string tokens
800          */
801         GTokenType tokenType = g_scanner_get_next_token( scanner );
802
803         /*
804          * We are looking for identifiers
805          */
806         if( tokenType == G_TOKEN_IDENTIFIER )
807         {
808             gchar* capability  = NULL;  /* the capability identifier string (always present) */
809             gint   capID       =    0;  /* the capability identifier value (from g_Tokens)   */
810             gint   comparison  =    0;  /* the comparison operator value, see definitions    */
811             gchar* valueString = NULL;  /* if the previous one is present, this is needed    */
812             gint   value       =    0;  /* the integer value converted using a strtol call   */
813             SFG_Capability* capStruct;  /* the capability description structure              */
814
815             /*
816              * OK. The general rule of thumb that we always should be getting a capability identifier
817              * string (see the grammar description). If it is followed by a comparison identifier, then
818              * there must follow an integer value we're comparing the capability to...
819              *
820              * Have the current token analyzed with that in mind...
821              */
822             for( i=0; i<(gint) strlen( scanner->value.v_identifier ); i++ )
823             {
824                 gchar c = scanner->value.v_identifier[ i ];
825
826                 if( (c == '=') || (c == '!') || (c == '<') || (c == '>') || (c == '~') )
827                     break;
828             }
829
830             /*
831              * Here we go with the length of the capability identifier string.
832              * In the worst of cases, it is as long as the token identifier.
833              */
834             capability = g_strndup( scanner->value.v_identifier, i );
835
836             /*
837              * OK. Is there a chance for comparison and value identifiers to follow?
838              * Note: checking against i+1 as this handles two cases: single character
839              * comparison operator and first of value's digits, which must always be
840              * there, or the two-character comparison operators.
841              */
842             if( (i + 1) < (gint) strlen( scanner->value.v_identifier ) )
843             {
844                 /*
845                  * Yeah, indeed, it is the i-th character to start the identifier, then.
846                  */
847                 gchar c1 = scanner->value.v_identifier[ i + 0 ];
848                 gchar c2 = scanner->value.v_identifier[ i + 1 ];
849
850                 if( (c1 == '=')                ) { i += 1; comparison = FG_EQUAL;         } else
851                 if( (c1 == '!') && (c2 == '=') ) { i += 2; comparison = FG_NOT_EQUAL;     } else
852                 if( (c1 == '<') && (c2 == '=') ) { i += 2; comparison = FG_LESS_OR_EQUAL; } else
853                 if( (c1 == '>') && (c2 == '=') ) { i += 2; comparison = FG_MORE_OR_EQUAL; } else
854                 if( (c1 == '<')                ) { i += 1; comparison = FG_LESS;          } else
855                 if( (c1 == '>')                ) { i += 1; comparison = FG_MORE;          } else
856                 if( (c1 == '~')                ) { i += 1; comparison = FG_CLOSEST;       } else
857                 g_warning( "invalid comparison operator in token `%s'", scanner->value.v_identifier );
858             }
859
860             /*
861              * Grab the value string that must follow the comparison operator...
862              */
863             if( comparison != FG_NONE && i < (gint) strlen( scanner->value.v_identifier ) )
864                 valueString = strdup( scanner->value.v_identifier + i );
865
866             /*
867              * If there was a value string, convert it to integer...
868              */
869             if( comparison != FG_NONE && strlen( valueString ) > 0 )
870                 value = strtol( valueString, NULL, 0 );
871
872             /*
873              * Now we need to match the capability string and its ID
874              */
875             for( i=0; g_Tokens[ i ]!=NULL; i++ )
876             {
877                 if( strcmp( capability, g_Tokens[ i ] ) == 0 )
878                 {
879                     /*
880                      * Looks like we've found the one we were looking for
881                      */
882                     capID = i;
883                     break;
884                 }
885             }
886
887             /*
888              * Create a new capability description structure
889              */
890             capStruct = g_new0( SFG_Capability, 1 );
891
892             /*
893              * Fill in the cap's values, as we have parsed it:
894              */
895             capStruct->capability =      capID;
896             capStruct->comparison = comparison;
897             capStruct->value      =      value;
898
899             /*
900              * Add the new capabaility to the caps list
901              */
902             caps = g_list_append( caps, capStruct );
903
904             /*
905              * Clean up the local mess and keep rolling on
906              */
907             g_free( valueString );
908             g_free( capability );
909         }
910     }
911
912     /*
913      * Now that we have converted the string into somewhat more machine-friendly
914      * form, proceed with matching the frame buffer configuration...
915      *
916      * The caps list could be passed to a function that would try finding the closest 
917      * matching pixel format, visual, frame buffer configuration or whatever. It would 
918      * be good to convert the glutInitDisplayMode() to use the same method.
919      */
920 #if 0
921     g_message( "found %i capability preferences", g_list_length( caps ) );
922
923     g_message( "token `%s': cap: %i, com: %i, val: %i",
924         scanner->value.v_identifier,
925         capStruct->capability,
926         capStruct->comparison,
927         capStruct->value
928     );
929 #endif
930
931     /*
932      * Free the capabilities we have parsed
933      */
934     for( i=0; i<(gint) g_list_length( caps ); i++ )
935         g_free( g_list_nth( caps, i )->data );
936
937     /*
938      * Destroy the capabilities list itself
939      */
940     g_list_free( caps );
941
942     /*
943      * Free the lexical scanner now...
944      */
945     g_scanner_destroy( scanner );
946 }
947 #endif
948
949 #define NUM_TOKENS             28
950 static char* Tokens[] =
951 {
952     "alpha", "acca", "acc", "blue", "buffer", "conformant", "depth", "double", "green",
953     "index", "num", "red", "rgba", "rgb", "luminance", "stencil", "single", "stereo", "samples",
954     "slow", "win32pdf", "xvisual", "xstaticgray", "xgrayscale", "xstaticcolor", "xpseudocolor",
955     "xtruecolor", "xdirectcolor"
956 };
957
958 static int TokenLengths[] =
959 {
960          5,      4,     3,      4,        6,           10,       5,        6,       5,
961          5,     3,     3,      4,     3,           9,         7,        6,        6,         7,
962         4,          8,         7,            11,           10,             12,             12,
963              10,             12
964 };
965
966 void FGAPIENTRY glutInitDisplayString( const char* displayMode )
967 {
968   int glut_state_flag = 0 ;
969   /*
970    * Unpack a lot of options from a character string.  The options are delimited by blanks or tabs.
971    */
972   char *token ;
973   int len = strlen ( displayMode ) ;
974   char *buffer = malloc ( (len+1) * sizeof(char) ) ;
975   memcpy ( buffer, displayMode, len ) ;
976   buffer[len] = '\0' ;
977
978   token = strtok ( buffer, " \t" ) ;
979   while ( token )
980   {
981     /*
982      * Process this token
983      */
984     int i ;
985     for ( i = 0; i < NUM_TOKENS; i++ )
986     {
987       if ( strncmp ( token, Tokens[i], TokenLengths[i] ) == 0 ) break ;
988     }
989
990     switch ( i )
991     {
992     case 0 :  /* "alpha":  Alpha color buffer precision in bits */
993       glut_state_flag |= GLUT_ALPHA ;  /* Somebody fix this for me! */
994       break ;
995
996     case 1 :  /* "acca":  Red, green, blue, and alpha accumulation buffer precision in bits */
997       break ;
998
999     case 2 :  /* "acc":  Red, green, and blue accumulation buffer precision in bits with zero bits alpha */
1000       glut_state_flag |= GLUT_ACCUM ;  /* Somebody fix this for me! */
1001       break ;
1002
1003     case 3 :  /* "blue":  Blue color buffer precision in bits */
1004       break ;
1005
1006     case 4 :  /* "buffer":  Number of bits in the color index color buffer */
1007       break ;
1008
1009     case 5 :  /* "conformant":  Boolean indicating if the frame buffer configuration is conformant or not */
1010       break ;
1011
1012     case 6 :  /* "depth":  Number of bits of precsion in the depth buffer */
1013       glut_state_flag |= GLUT_DEPTH ;  /* Somebody fix this for me! */
1014       break ;
1015
1016     case 7 :  /* "double":  Boolean indicating if the color buffer is double buffered */
1017       glut_state_flag |= GLUT_DOUBLE ;
1018       break ;
1019
1020     case 8 :  /* "green":  Green color buffer precision in bits */
1021       break ;
1022
1023     case 9 :  /* "index":  Boolean if the color model is color index or not */
1024       glut_state_flag |= GLUT_INDEX ;
1025       break ;
1026
1027     case 10 :  /* "num":  A special capability  name indicating where the value represents the Nth frame buffer configuration matching the description string */
1028       break ;
1029
1030     case 11 :  /* "red":  Red color buffer precision in bits */
1031       break ;
1032
1033     case 12 :  /* "rgba":  Number of bits of red, green, blue, and alpha in the RGBA color buffer */
1034       glut_state_flag |= GLUT_RGBA ;  /* Somebody fix this for me! */
1035       break ;
1036
1037     case 13 :  /* "rgb":  Number of bits of red, green, and blue in the RGBA color buffer with zero bits alpha */
1038       glut_state_flag |= GLUT_RGB ;  /* Somebody fix this for me! */
1039       break ;
1040
1041     case 14 :  /* "luminance":  Number of bits of red in the RGBA and zero bits of green, blue (alpha not specified) of color buffer precision */
1042       glut_state_flag |= GLUT_LUMINANCE ;  /* Somebody fix this for me! */
1043       break ;
1044
1045     case 15 :  /* "stencil":  Number of bits in the stencil buffer */
1046       glut_state_flag |= GLUT_STENCIL ;  /* Somebody fix this for me! */
1047       break ;
1048
1049     case 16 :  /* "single":  Boolean indicate the color buffer is single buffered */
1050       glut_state_flag |= GLUT_SINGLE ;
1051       break ;
1052
1053     case 17 :  /* "stereo":  Boolean indicating the color buffer supports OpenGL-style stereo */
1054       glut_state_flag |= GLUT_STEREO ;
1055       break ;
1056
1057     case 18 :  /* "samples":  Indicates the number of multisamples to use based on GLX's SGIS_multisample extension (for antialiasing) */
1058       glut_state_flag |= GLUT_MULTISAMPLE ;  /* Somebody fix this for me! */
1059       break ;
1060
1061     case 19 :  /* "slow":  Boolean indicating if the frame buffer configuration is slow or not */
1062       break ;
1063
1064     case 20 :  /* "win32pdf":  matches the Win32 Pixel Format Descriptor by number */
1065 #ifdef TARGET_HOST_WIN32
1066 #endif
1067       break ;
1068
1069     case 21 :  /* "xvisual":  matches the X visual ID by number */
1070 #ifdef TARGET_HOST_UNIX_X11
1071 #endif
1072       break ;
1073
1074     case 22 :  /* "xstaticgray":  boolean indicating if the frame buffer configuration's X visual is of type StaticGray */
1075 #ifdef TARGET_HOST_UNIX_X11
1076 #endif
1077       break ;
1078
1079     case 23 :  /* "xgrayscale":  boolean indicating if the frame buffer configuration's X visual is of type GrayScale */
1080 #ifdef TARGET_HOST_UNIX_X11
1081 #endif
1082       break ;
1083
1084     case 24 :  /* "xstaticcolor":  boolean indicating if the frame buffer configuration's X visual is of type StaticColor */
1085 #ifdef TARGET_HOST_UNIX_X11
1086 #endif
1087       break ;
1088
1089     case 25 :  /* "xpseudocolor":  boolean indicating if the frame buffer configuration's X visual is of type PseudoColor */
1090 #ifdef TARGET_HOST_UNIX_X11
1091 #endif
1092       break ;
1093
1094     case 26 :  /* "xtruecolor":  boolean indicating if the frame buffer configuration's X visual is of type TrueColor */
1095 #ifdef TARGET_HOST_UNIX_X11
1096 #endif
1097       break ;
1098
1099     case 27 :  /* "xdirectcolor":  boolean indicating if the frame buffer configuration's X visual is of type DirectColor */
1100 #ifdef TARGET_HOST_UNIX_X11
1101 #endif
1102       break ;
1103
1104     case 28 :  /* Unrecognized */
1105       printf ( "WARNING - Display string token not recognized:  %s\n", token ) ;
1106       break ;
1107     }
1108
1109     token = strtok ( NULL, " \t" ) ;
1110   }
1111
1112   free ( buffer ) ;
1113
1114   /*
1115    * We will make use of this value when creating a new OpenGL context...
1116    */
1117   fgState.DisplayMode = glut_state_flag;
1118 }
1119
1120 /*** END OF FILE ***/