4 * Various freeglut initialization functions.
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
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:
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
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.
32 #define G_LOG_DOMAIN "freeglut-init"
34 #include "../include/GL/freeglut.h"
35 #include "../include/GL/freeglut_internal.h"
38 * TODO BEFORE THE STABLE RELEASE:
40 * fgDeinitialize() -- Win32's OK, X11 needs the OS-specific deinitialization done
41 * glutInitDisplayString() -- display mode string parsing
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?
47 /* -- GLOBAL VARIABLES ----------------------------------------------------- */
50 * A structure pointed by g_pDisplay holds all information
51 * regarding the display, screen, root window etc.
53 SFG_Display fgDisplay;
56 * The settings for the current freeglut session
61 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
64 * A call to this function should initialize all the display stuff...
66 void fgInitialize( const char* displayName )
68 #if TARGET_HOST_UNIX_X11
70 * Have the display created
72 fgDisplay.Display = XOpenDisplay( displayName );
74 if( fgDisplay.Display == NULL )
77 * Failed to open a display. That's no good.
79 fgError( "failed to open display '%s'", XDisplayName( displayName ) );
83 * Check for the OpenGL GLX extension availability:
85 if( !glXQueryExtension( fgDisplay.Display, NULL, NULL ) )
88 * GLX extensions have not been found...
90 fgError( "OpenGL GLX extension not supported by display '%s'", XDisplayName( displayName ) );
94 * Grab the default screen for the display we have just opened
96 fgDisplay.Screen = DefaultScreen( fgDisplay.Display );
99 * The same applying to the root window
101 fgDisplay.RootWindow = RootWindow(
107 * Grab the logical screen's geometry
109 fgDisplay.ScreenWidth = DisplayWidth(
114 fgDisplay.ScreenHeight = DisplayHeight(
120 * Grab the physical screen's geometry
122 fgDisplay.ScreenWidthMM = DisplayWidthMM(
127 fgDisplay.ScreenHeightMM = DisplayHeightMM(
133 * The display's connection number
135 fgDisplay.Connection = ConnectionNumber( fgDisplay.Display );
138 * Create the window deletion atom
140 fgDisplay.DeleteWindow = XInternAtom(
146 #elif TARGET_HOST_WIN32
152 * What we need to do is to initialize the fgDisplay global structure here...
154 fgDisplay.Instance = GetModuleHandle( NULL );
157 * Check if the freeglut window class has been registered before...
159 atom = GetClassInfo( fgDisplay.Instance, "FREEGLUT", &wc );
162 * ...nope, it has not, and we have to do it right now:
169 * Make sure the unitialized fields are reset to zero
171 ZeroMemory( &wc, sizeof(WNDCLASS) );
174 * Each of the windows should have it's own device context...
177 wc.lpfnWndProc = fgWindowProc;
180 wc.hInstance = fgDisplay.Instance;
181 wc.hIcon = LoadIcon( NULL, IDI_WINLOGO );
182 wc.hCursor = LoadCursor( NULL, IDC_ARROW );
183 wc.hbrBackground = NULL;
184 wc.lpszMenuName = NULL;
185 wc.lpszClassName = "FREEGLUT";
188 * Register the window class
190 retval = RegisterClass( &wc );
191 assert( retval != FALSE );
195 * The screen dimensions can be obtained via GetSystemMetrics() calls
197 fgDisplay.ScreenWidth = GetSystemMetrics( SM_CXSCREEN );
198 fgDisplay.ScreenHeight = GetSystemMetrics( SM_CYSCREEN );
202 * Checking the display's size in millimeters isn't too hard, too:
204 HWND desktop = GetDesktopWindow();
205 HDC context = GetDC( desktop );
208 * Grab the appropriate values now (HORZSIZE and VERTSIZE respectably):
210 fgDisplay.ScreenWidthMM = GetDeviceCaps( context, HORZSIZE );
211 fgDisplay.ScreenHeightMM = GetDeviceCaps( context, VERTSIZE );
214 * Whoops, forgot to release the device context :)
216 ReleaseDC( desktop, context );
222 * Have the joystick device initialized now
228 * Perform the freeglut deinitialization...
230 void fgDeinitialize( void )
235 * Check if initialization has been performed before
237 if( !fgState.Time.Set )
239 fgWarning( "fgDeinitialize(): fgState.Timer is null => no valid initialization has been performed" );
244 * Perform the freeglut structure deinitialization
246 fgDestroyStructure();
249 * Delete all the timers and their storage list
251 while ( (timer = fgState.Timers.First) != NULL )
253 fgListRemove(&fgState.Timers, &timer->Node);
258 * Deinitialize the joystick device
262 #if TARGET_HOST_UNIX_X11
265 * Make sure all X-client data we have created will be destroyed on display closing
267 XSetCloseDownMode( fgDisplay.Display, DestroyAll );
270 * Close the display connection, destroying all windows we have created so far
272 XCloseDisplay( fgDisplay.Display );
278 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
281 * Perform initialization. This usually happens on the program startup
282 * and restarting after glutMainLoop termination...
284 void FGAPIENTRY glutInit( int* pargc, char** argv )
286 char* geometrySettings = NULL;
287 char* displayName = NULL;
288 int i, j, argc = *pargc;
291 * Do not allow multiple initialization of the library
293 if( fgState.Time.Set )
296 * We can't have multiple initialization performed
298 fgError( "illegal glutInit() reinitialization attemp" );
302 * Have the internal freeglut structure initialized now
307 * Fill in the default values that have not been passed in yet.
309 if( fgState.Position.Use == FALSE )
311 fgState.Position.X = -1;
312 fgState.Position.Y = -1;
315 if( fgState.Size.Use == FALSE )
317 fgState.Size.X = 300;
318 fgState.Size.Y = 300;
319 fgState.Size.Use = TRUE;
323 * Some more settings to come
325 fgState.ForceDirectContext = FALSE;
326 fgState.TryDirectContext = TRUE;
327 fgState.ForceIconic = FALSE;
328 fgState.GLDebugSwitch = FALSE;
329 fgState.XSyncSwitch = FALSE;
332 * Assume we want to ignore the automatic key repeat
334 fgState.IgnoreKeyRepeat = TRUE;
337 * The default display mode to be used
339 fgState.DisplayMode = GLUT_RGBA | GLUT_SINGLE | GLUT_DEPTH;
342 * Set the default game mode settings
344 fgState.GameModeSize.X = 640;
345 fgState.GameModeSize.Y = 480;
346 fgState.GameModeDepth = 16;
347 fgState.GameModeRefresh = 72;
350 * Remember the function's call time
353 gettimeofday(&fgState.Time.Value, NULL);
355 fgState.Time.Value = timeGetTime();
357 fgState.Time.Set = TRUE;
360 * Grab the environment variable indicating the X display to use.
361 * This is harmless under Win32, so let's let it stay here...
363 displayName = strdup( getenv( "DISPLAY" ) );
366 * Have the program arguments parsed.
368 for( i=1; i<argc; i++ )
371 * The X display name settings
373 if( strcmp( argv[ i ], "-display" ) == 0 )
376 * Check for possible lack of the next argument
379 fgError( "-display parameter must be followed by display name" );
382 * Release the previous display name (the one from app's environment)
387 * Make a working copy of the name for us to use
389 displayName = strdup( argv[ i ] );
392 * Have both arguments removed
394 argv[ i - 1 ] = NULL;
400 * The geometry settings
402 else if( strcmp( argv[ i ], "-geometry" ) == 0 )
405 * Again, check if there is at least one more argument
408 fgError( "-geometry parameter must be followed by window geometry settings" );
411 * Otherwise make a duplicate of the geometry settings...
413 geometrySettings = strdup( argv[ i ] );
416 * Have both arguments removed
418 argv[ i - 1 ] = NULL;
424 * The direct/indirect OpenGL contexts settings
426 else if( strcmp( argv[ i ], "-direct" ) == 0)
429 * We try to force direct rendering...
431 if( fgState.TryDirectContext == FALSE )
432 fgError( "parameters ambiguity, -direct and -indirect cannot be both specified" );
434 fgState.ForceDirectContext = TRUE;
438 else if( strcmp( argv[ i ], "-indirect" ) == 0 )
441 * We try to force indirect rendering...
443 if( fgState.ForceDirectContext == TRUE )
444 fgError( "parameters ambiguity, -direct and -indirect cannot be both specified" );
446 fgState.TryDirectContext = FALSE;
452 * The '-iconic' parameter makes all new top-level
453 * windows created in iconified state...
455 else if( strcmp( argv[ i ], "-iconic" ) == 0 )
457 fgState.ForceIconic = TRUE;
463 * The '-gldebug' option activates some OpenGL state debugging features
465 else if( strcmp( argv[ i ], "-gldebug" ) == 0 )
467 fgState.GLDebugSwitch = TRUE;
473 * The '-sync' option activates X protocol synchronization (for debugging purposes)
475 else if( strcmp( argv[ i ], "-sync" ) == 0 )
477 fgState.XSyncSwitch = TRUE;
484 * Have the arguments list compacted now
486 for( i=1; i<argc; i++ )
488 if( argv[ i ] == NULL )
490 for( j=i; j<argc; j++ )
492 if( argv[ j ] != NULL )
494 argv[ i ] = argv[ j ];
502 * Have the display created now. As I am too lazy to implement
503 * the program arguments parsing, we will have the DISPLAY
504 * environment variable used for opening the X display:
506 fgInitialize( displayName );
508 #if TARGET_HOST_UNIX_X11
510 * We can process the default window geometry settings safely now
512 * WARNING: have this rewritten to be portable. That won't be hard.
514 if( geometrySettings != NULL )
520 * Have the settings parsed now. This is easy.
521 * We will use the XParseGeometry function.
523 result = XParseGeometry( geometrySettings, &x, &y, &w, &h );
526 * Check what we have been supplied with...
528 if( (result & WidthValue) && (w >= 0) )
531 if( (result & HeightValue) && (h >= 0) )
534 if( result & XValue )
536 if( result & XNegative )
537 fgState.Position.X = fgDisplay.ScreenWidth + x - fgState.Size.X;
539 fgState.Position.X = x;
542 if( result & YValue )
544 if( result & YNegative )
545 fgState.Position.Y = fgDisplay.ScreenHeight + y - fgState.Size.Y;
547 fgState.Position.Y = y;
551 * Free the geometry settings string
553 free( geometrySettings );
558 * Check for the minus one settings for both position and size...
560 if( fgState.Position.X < 0 || fgState.Position.Y < 0 )
561 fgState.Position.Use = FALSE;
563 if( fgState.Size.X < 0 || fgState.Size.Y < 0 )
564 fgState.Size.Use = FALSE;
567 * Do not forget about releasing the display name string
573 * Sets the default initial window position for new windows
575 void FGAPIENTRY glutInitWindowPosition( int x, int y )
578 * The settings can be disables when both coordinates are negative
580 if( (x >= 0) && (y >= 0) )
583 * We want to specify the initial position of each of the windows
585 fgState.Position.X = x;
586 fgState.Position.Y = y;
587 fgState.Position.Use = TRUE;
592 * The initial position of each of the windows is specified by the wm
594 fgState.Position.X = -1;
595 fgState.Position.Y = -1;
596 fgState.Position.Use = FALSE;
601 * Sets the default initial window size for new windows
603 void FGAPIENTRY glutInitWindowSize( int width, int height )
606 * The settings can be disables when both values are negative
608 if( (width >= 0) && (height >= 0) )
611 * We want to specify the initial size of each of the windows
613 fgState.Size.X = width;
614 fgState.Size.Y = height;
615 fgState.Size.Use = TRUE;
620 * The initial size of each of the windows is specified by the wm
624 fgState.Size.Use = FALSE;
629 * Sets the default display mode for all new windows
631 void FGAPIENTRY glutInitDisplayMode( int displayMode )
634 * We will make use of this value when creating a new OpenGL context...
636 fgState.DisplayMode = displayMode;
640 /* -- INIT DISPLAY STRING PARSING ------------------------------------------ */
642 #if 0 /* FIXME: CJP */
644 * There is a discrete number of comparison operators we can encounter:
646 * comparison ::= "=" | "!=" | "<" | ">" | "<=" | ">=" | "~"
648 #define FG_NONE 0x0000
649 #define FG_EQUAL 0x0001
650 #define FG_NOT_EQUAL 0x0002
651 #define FG_LESS 0x0003
652 #define FG_MORE 0x0004
653 #define FG_LESS_OR_EQUAL 0x0005
654 #define FG_MORE_OR_EQUAL 0x0006
655 #define FG_CLOSEST 0x0007
658 * The caller can feed us with a number of capability tokens:
660 * capability ::= "alpha" | "acca" | "acc" | "blue" | "buffer" | "conformant" | "depth" | "double" |
661 * "green" | "index" | "num" | "red" | "rgba" | "rgb" | "luminance" | "stencil" |
662 * "single" | "stereo" | "samples" | "slow" | "win32pdf" | "xvisual" | "xstaticgray" |
663 * "xgrayscale" | "xstaticcolor" | "xpseudocolor" | "xtruecolor" | "xdirectcolor"
665 static gchar* g_Tokens[] =
667 "none", "alpha", "acca", "acc", "blue", "buffer", "conformant", "depth", "double", "green",
668 "index", "num", "red", "rgba", "rgb", "luminance", "stencil", "single", "stereo", "samples",
669 "slow", "win32pdf", "xvisual", "xstaticgray", "xgrayscale", "xstaticcolor", "xpseudocolor",
670 "xtruecolor", "xdirectcolor", NULL
674 * The structure to hold the parsed display string tokens
676 typedef struct tagSFG_Capability SFG_Capability;
677 struct tagSFG_Capability
679 gint capability; /* the capability token enumerator */
680 gint comparison; /* the comparison operator used */
681 gint value; /* the value we're comparing to */
685 * The scanner configuration for the init display string
687 static GScannerConfig fgInitDisplayStringScannerConfig =
689 ( " \t\r\n" ) /* cset_skip_characters */,
694 ) /* cset_identifier_first */,
702 ) /* cset_identifier_nth */,
703 ( "#\n" ) /* cpair_comment_single */,
704 FALSE /* case_sensitive */,
705 TRUE /* skip_comment_multi */,
706 TRUE /* skip_comment_single */,
707 TRUE /* scan_comment_multi */,
708 TRUE /* scan_identifier */,
709 FALSE /* scan_identifier_1char */,
710 FALSE /* scan_identifier_NULL */,
711 TRUE /* scan_symbols */,
712 FALSE /* scan_binary */,
713 TRUE /* scan_octal */,
714 TRUE /* scan_float */,
716 FALSE /* scan_hex_dollar */,
717 TRUE /* scan_string_sq */,
718 TRUE /* scan_string_dq */,
719 TRUE /* numbers_2_int */,
720 FALSE /* int_2_float */,
721 FALSE /* identifier_2_string */,
722 TRUE /* char_2_token */,
723 FALSE /* symbol_2_token */,
724 FALSE /* scope_0_fallback */,
728 * Sets the default display mode for all new windows using a string
730 void FGAPIENTRY glutInitDisplayString( char* displayMode )
733 * display_string ::= (switch)
734 * switch ::= capability [comparison value]
735 * comparison ::= "=" | "!=" | "<" | ">" | "<=" | ">=" | "~"
736 * capability ::= "alpha" | "acca" | "acc" | "blue" | "buffer" | "conformant" |
737 * "depth" | "double" | "green" | "index" | "num" | "red" | "rgba" |
738 * "rgb" | "luminance" | "stencil" | "single" | "stereo" |
739 * "samples" | "slow" | "win32pdf" | "xvisual" | "xstaticgray" |
740 * "xgrayscale" | "xstaticcolor" | "xpseudocolor" |
741 * "xtruecolor" | "xdirectcolor"
742 * value ::= 0..9 [value]
744 * The display string grammar. This should be EBNF, but I couldn't find the definitions so, to
745 * clarify: (expr) means 0 or more times the expression, [expr] means 0 or 1 times expr.
747 * Create a new GLib lexical analyzer to process the display mode string
749 GScanner* scanner = g_scanner_new( &fgInitDisplayStringScannerConfig );
754 * Fail if the display mode string is empty or the scanner failed to initialize
756 freeglut_return_if_fail( (scanner != NULL) && (strlen( displayMode ) > 0) );
759 * Set the scanner's input name (for debugging)
761 scanner->input_name = "glutInitDisplayString";
764 * Start the lexical analysis of the extensions string
766 g_scanner_input_text( scanner, displayMode, strlen( displayMode ) );
769 * While there are any more tokens to be checked...
771 while( !g_scanner_eof( scanner ) )
774 * Actually we're expecting only string tokens
776 GTokenType tokenType = g_scanner_get_next_token( scanner );
779 * We are looking for identifiers
781 if( tokenType == G_TOKEN_IDENTIFIER )
783 gchar* capability = NULL; /* the capability identifier string (always present) */
784 gint capID = 0; /* the capability identifier value (from g_Tokens) */
785 gint comparison = 0; /* the comparison operator value, see definitions */
786 gchar* valueString = NULL; /* if the previous one is present, this is needed */
787 gint value = 0; /* the integer value converted using a strtol call */
788 SFG_Capability* capStruct; /* the capability description structure */
791 * OK. The general rule of thumb that we always should be getting a capability identifier
792 * string (see the grammar description). If it is followed by a comparison identifier, then
793 * there must follow an integer value we're comparing the capability to...
795 * Have the current token analyzed with that in mind...
797 for( i=0; i<(gint) strlen( scanner->value.v_identifier ); i++ )
799 gchar c = scanner->value.v_identifier[ i ];
801 if( (c == '=') || (c == '!') || (c == '<') || (c == '>') || (c == '~') )
806 * Here we go with the length of the capability identifier string.
807 * In the worst of cases, it is as long as the token identifier.
809 capability = g_strndup( scanner->value.v_identifier, i );
812 * OK. Is there a chance for comparison and value identifiers to follow?
813 * Note: checking against i+1 as this handles two cases: single character
814 * comparison operator and first of value's digits, which must always be
815 * there, or the two-character comparison operators.
817 if( (i + 1) < (gint) strlen( scanner->value.v_identifier ) )
820 * Yeah, indeed, it is the i-th character to start the identifier, then.
822 gchar c1 = scanner->value.v_identifier[ i + 0 ];
823 gchar c2 = scanner->value.v_identifier[ i + 1 ];
825 if( (c1 == '=') ) { i += 1; comparison = FG_EQUAL; } else
826 if( (c1 == '!') && (c2 == '=') ) { i += 2; comparison = FG_NOT_EQUAL; } else
827 if( (c1 == '<') && (c2 == '=') ) { i += 2; comparison = FG_LESS_OR_EQUAL; } else
828 if( (c1 == '>') && (c2 == '=') ) { i += 2; comparison = FG_MORE_OR_EQUAL; } else
829 if( (c1 == '<') ) { i += 1; comparison = FG_LESS; } else
830 if( (c1 == '>') ) { i += 1; comparison = FG_MORE; } else
831 if( (c1 == '~') ) { i += 1; comparison = FG_CLOSEST; } else
832 g_warning( "invalid comparison operator in token `%s'", scanner->value.v_identifier );
836 * Grab the value string that must follow the comparison operator...
838 if( comparison != FG_NONE && i < (gint) strlen( scanner->value.v_identifier ) )
839 valueString = g_strdup( scanner->value.v_identifier + i );
842 * If there was a value string, convert it to integer...
844 if( comparison != FG_NONE && strlen( valueString ) > 0 )
845 value = strtol( valueString, NULL, 0 );
848 * Now we need to match the capability string and it's ID
850 for( i=0; g_Tokens[ i ]!=NULL; i++ )
852 if( strcmp( capability, g_Tokens[ i ] ) == 0 )
855 * Looks like we've found the one we were looking for
863 * Create a new capability description structure
865 capStruct = g_new0( SFG_Capability, 1 );
868 * Fill in the cap's values, as we have parsed it:
870 capStruct->capability = capID;
871 capStruct->comparison = comparison;
872 capStruct->value = value;
875 * Add the new capabaility to the caps list
877 caps = g_list_append( caps, capStruct );
880 * Clean up the local mess and keep rolling on
882 g_free( valueString );
883 g_free( capability );
888 * Now that we have converted the string into somewhat more machine-friendly
889 * form, proceed with matching the frame buffer configuration...
891 * The caps list could be passed to a function that would try finding the closest
892 * matching pixel format, visual, frame buffer configuration or whatever. It would
893 * be good to convert the glutInitDisplayMode() to use the same method.
896 g_message( "found %i capability preferences", g_list_length( caps ) );
898 g_message( "token `%s': cap: %i, com: %i, val: %i",
899 scanner->value.v_identifier,
900 capStruct->capability,
901 capStruct->comparison,
907 * Free the capabilities we have parsed
909 for( i=0; i<(gint) g_list_length( caps ); i++ )
910 g_free( g_list_nth( caps, i )->data );
913 * Destroy the capabilities list itself
918 * Free the lexical scanner now...
920 g_scanner_destroy( scanner );
924 void FGAPIENTRY glutInitDisplayString( char* displayMode )
928 /*** END OF FILE ***/