Added the ugly ATEXIT_HACK from GLUT 3.7, making freeglut binary compatible with...
[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 #define FREEGLUT_BUILDING_LIB
29 #include <GL/freeglut.h>
30 #include "freeglut_internal.h"
31
32 #if TARGET_HOST_POSIX_X11
33 #include <limits.h>  /* LONG_MAX */
34 #endif
35
36 /*
37  * TODO BEFORE THE STABLE RELEASE:
38  *
39  *  fgDeinitialize()        -- Win32's OK, X11 needs the OS-specific
40  *                             deinitialization done
41  *  glutInitDisplayString() -- display mode string parsing
42  *
43  * Wouldn't it be cool to use gettext() for error messages? I just love
44  * bash saying  "nie znaleziono pliku" instead of "file not found" :)
45  * Is gettext easily portable?
46  */
47
48 /* -- GLOBAL VARIABLES ----------------------------------------------------- */
49
50 /*
51  * A structure pointed by g_pDisplay holds all information
52  * regarding the display, screen, root window etc.
53  */
54 SFG_Display fgDisplay;
55
56 /*
57  * The settings for the current freeglut session
58  */
59 SFG_State fgState = { { -1, -1, GL_FALSE },  /* Position */
60                       { 300, 300, GL_TRUE }, /* Size */
61                       GLUT_RGBA | GLUT_SINGLE | GLUT_DEPTH,  /* DisplayMode */
62                       GL_FALSE,              /* Initialised */
63                       GLUT_TRY_DIRECT_CONTEXT,  /* DirectContext */
64                       GL_FALSE,              /* ForceIconic */
65                       GL_FALSE,              /* UseCurrentContext */
66                       GL_FALSE,              /* GLDebugSwitch */
67                       GL_FALSE,              /* XSyncSwitch */
68                       GLUT_KEY_REPEAT_ON,    /* KeyRepeat */
69                       INVALID_MODIFIERS,     /* Modifiers */
70                       0,                     /* FPSInterval */
71                       0,                     /* SwapCount */
72                       0,                     /* SwapTime */
73                       0,                     /* Time */
74                       { NULL, NULL },         /* Timers */
75                       { NULL, NULL },         /* FreeTimers */
76                       NULL,                   /* IdleCallback */
77                       0,                      /* ActiveMenus */
78                       NULL,                   /* MenuStateCallback */
79                       NULL,                   /* MenuStatusCallback */
80                       { 640, 480, GL_TRUE },  /* GameModeSize */
81                       16,                     /* GameModeDepth */
82                       72,                     /* GameModeRefresh */
83                       GLUT_ACTION_EXIT,       /* ActionOnWindowClose */
84                       GLUT_EXEC_STATE_INIT,   /* ExecState */
85                       NULL,                   /* ProgramName */
86                       GL_FALSE,               /* JoysticksInitialised */
87                       GL_FALSE,               /* InputDevsInitialised */
88                       1,                      /* AuxiliaryBufferNumber */
89                       4,                      /* SampleNumber */
90                       1,                      /* MajorVersion */
91                       0,                      /* MajorVersion */
92                       0                       /* ContextFlags */
93 };
94
95
96 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
97
98 #if TARGET_HOST_POSIX_X11
99
100 /* Return the atom associated with "name". */
101 static Atom fghGetAtom(const char * name)
102 {
103   return XInternAtom(fgDisplay.Display, name, False);
104 }
105
106 /*
107  * Check if "property" is set on "window".  The property's values are returned
108  * through "data".  If the property is set and is of type "type", return the
109  * number of elements in "data".  Return zero otherwise.  In both cases, use
110  * "Xfree()" to free "data".
111  */
112 static int fghGetWindowProperty(Window window,
113                                 Atom property,
114                                 Atom type,
115                                 unsigned char ** data)
116 {
117   /*
118    * Caller always has to use "Xfree()" to free "data", since
119    * "XGetWindowProperty() always allocates one extra byte in prop_return
120    * [i.e. "data"] (even if the property is zero length) [..]".
121    */
122
123   int status;  /*  Returned by "XGetWindowProperty". */
124
125   Atom          type_returned;
126   int           temp_format;             /*  Not used. */
127   unsigned long number_of_elements;
128   unsigned long temp_bytes_after;        /*  Not used. */
129
130
131   status = XGetWindowProperty(fgDisplay.Display,
132                               window,
133                               property,
134                               0,
135                               LONG_MAX,
136                               False,
137                               type,
138                               &type_returned,
139                               &temp_format,
140                               &number_of_elements,
141                               &temp_bytes_after,
142                               data);
143
144   FREEGLUT_INTERNAL_ERROR_EXIT(status == Success,
145                                "XGetWindowProperty failled",
146                                "fghGetWindowProperty");
147
148   if (type_returned != type)
149     {
150       number_of_elements = 0;
151     }
152
153   return number_of_elements;
154 }
155
156 /*  Check if the window manager is NET WM compliant. */
157 static int fghNetWMSupported(void)
158 {
159   Atom wm_check;
160   Window ** window_ptr_1;
161
162   int number_of_windows;
163   int net_wm_supported;
164
165
166   net_wm_supported = 0;
167
168   wm_check = fghGetAtom("_NET_SUPPORTING_WM_CHECK");
169   window_ptr_1 = malloc(sizeof(Window *));
170
171   /*
172    * Check that the window manager has set this property on the root window.
173    * The property must be the ID of a child window.
174    */
175   number_of_windows = fghGetWindowProperty(fgDisplay.RootWindow,
176                                            wm_check,
177                                            XA_WINDOW,
178                                            (unsigned char **) window_ptr_1);
179   if (number_of_windows == 1)
180     {
181       Window ** window_ptr_2;
182
183       window_ptr_2 = malloc(sizeof(Window *));
184
185       /* Check that the window has the same property set to the same value. */
186       number_of_windows = fghGetWindowProperty(**window_ptr_1,
187                                                wm_check,
188                                                XA_WINDOW,
189                                                (unsigned char **) window_ptr_2);
190       if ((number_of_windows == 1) && (**window_ptr_1 == **window_ptr_2))
191       {
192         /* NET WM compliant */
193         net_wm_supported = 1;
194       }
195
196       XFree(*window_ptr_2);
197       free(window_ptr_2);
198     }
199
200         XFree(*window_ptr_1);
201         free(window_ptr_1);
202
203         return net_wm_supported;
204 }
205
206 /*  Check if "hint" is present in "property" for "window". */
207 int fgHintPresent(Window window, Atom property, Atom hint)
208 {
209   Atom ** atoms_ptr;
210   int number_of_atoms;
211   int supported;
212   int i;
213
214   supported = 0;
215
216   atoms_ptr = malloc(sizeof(Atom *));
217   number_of_atoms = fghGetWindowProperty(window,
218                                          property,
219                                          XA_ATOM,
220                                          (unsigned char **) atoms_ptr);
221   for (i = 0; i < number_of_atoms; i++)
222     {
223       if ((*atoms_ptr)[i] == hint)
224       {
225           supported = 1;
226           break;
227       }
228     }
229
230   return supported;
231 }
232
233 #endif /*  TARGET_HOST_POSIX_X11  */
234
235
236 /*
237  * A call to this function should initialize all the display stuff...
238  */
239 static void fghInitialize( const char* displayName )
240 {
241 #if TARGET_HOST_POSIX_X11
242     fgDisplay.Display = XOpenDisplay( displayName );
243
244     if( fgDisplay.Display == NULL )
245         fgError( "failed to open display '%s'", XDisplayName( displayName ) );
246
247     if( !glXQueryExtension( fgDisplay.Display, NULL, NULL ) )
248         fgError( "OpenGL GLX extension not supported by display '%s'",
249             XDisplayName( displayName ) );
250
251     fgDisplay.Screen = DefaultScreen( fgDisplay.Display );
252     fgDisplay.RootWindow = RootWindow(
253         fgDisplay.Display,
254         fgDisplay.Screen
255     );
256
257     fgDisplay.ScreenWidth  = DisplayWidth(
258         fgDisplay.Display,
259         fgDisplay.Screen
260     );
261     fgDisplay.ScreenHeight = DisplayHeight(
262         fgDisplay.Display,
263         fgDisplay.Screen
264     );
265
266     fgDisplay.ScreenWidthMM = DisplayWidthMM(
267         fgDisplay.Display,
268         fgDisplay.Screen
269     );
270     fgDisplay.ScreenHeightMM = DisplayHeightMM(
271         fgDisplay.Display,
272         fgDisplay.Screen
273     );
274
275     fgDisplay.Connection = ConnectionNumber( fgDisplay.Display );
276
277     /* Create the window deletion atom */
278     fgDisplay.DeleteWindow = fghGetAtom("WM_DELETE_WINDOW");
279
280     /* Create the state and full screen atoms */
281     fgDisplay.State           = None;
282     fgDisplay.StateFullScreen = None;
283
284     if (fghNetWMSupported())
285     {
286       const Atom supported = fghGetAtom("_NET_SUPPORTED");
287       const Atom state     = fghGetAtom("_NET_WM_STATE");
288       
289       /* Check if the state hint is supported. */
290       if (fgHintPresent(fgDisplay.RootWindow, supported, state))
291       {
292         const Atom full_screen = fghGetAtom("_NET_WM_STATE_FULLSCREEN");
293         
294         fgDisplay.State = state;
295         
296         /* Check if the window manager supports full screen. */
297         /**  Check "_NET_WM_ALLOWED_ACTIONS" on our window instead? **/
298         if (fgHintPresent(fgDisplay.RootWindow, supported, full_screen))
299         {
300           fgDisplay.StateFullScreen = full_screen;
301         }
302       }
303     }
304
305 #elif TARGET_HOST_MS_WINDOWS
306
307     WNDCLASS wc;
308     ATOM atom;
309
310     /* What we need to do is to initialize the fgDisplay global structure here. */
311     fgDisplay.Instance = GetModuleHandle( NULL );
312
313     atom = GetClassInfo( fgDisplay.Instance, _T("FREEGLUT"), &wc );
314
315     if( atom == 0 )
316     {
317         ZeroMemory( &wc, sizeof(WNDCLASS) );
318
319         /*
320          * Each of the windows should have its own device context, and we
321          * want redraw events during Vertical and Horizontal Resizes by
322          * the user.
323          *
324          * XXX Old code had "| CS_DBCLCKS" commented out.  Plans for the
325          * XXX future?  Dead-end idea?
326          */
327         wc.lpfnWndProc    = fgWindowProc;
328         wc.cbClsExtra     = 0;
329         wc.cbWndExtra     = 0;
330         wc.hInstance      = fgDisplay.Instance;
331         wc.hIcon          = LoadIcon( fgDisplay.Instance, _T("GLUT_ICON") );
332
333 #if defined(_WIN32_WCE)
334         wc.style          = CS_HREDRAW | CS_VREDRAW;
335 #else
336         wc.style          = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
337         if (!wc.hIcon)
338           wc.hIcon        = LoadIcon( NULL, IDI_WINLOGO );
339 #endif
340
341         wc.hCursor        = LoadCursor( NULL, IDC_ARROW );
342         wc.hbrBackground  = NULL;
343         wc.lpszMenuName   = NULL;
344         wc.lpszClassName  = _T("FREEGLUT");
345
346         /* Register the window class */
347         atom = RegisterClass( &wc );
348         FREEGLUT_INTERNAL_ERROR_EXIT ( atom, "Window Class Not Registered", "fghInitialize" );
349     }
350
351     /* The screen dimensions can be obtained via GetSystemMetrics() calls */
352     fgDisplay.ScreenWidth  = GetSystemMetrics( SM_CXSCREEN );
353     fgDisplay.ScreenHeight = GetSystemMetrics( SM_CYSCREEN );
354
355     {
356         HWND desktop = GetDesktopWindow( );
357         HDC  context = GetDC( desktop );
358
359         fgDisplay.ScreenWidthMM  = GetDeviceCaps( context, HORZSIZE );
360         fgDisplay.ScreenHeightMM = GetDeviceCaps( context, VERTSIZE );
361
362         ReleaseDC( desktop, context );
363     }
364
365     /* Set the timer granularity to 1 ms */
366     timeBeginPeriod ( 1 );
367
368 #endif
369
370     fgState.Initialised = GL_TRUE;
371
372     /* InputDevice uses GlutTimerFunc(), so fgState.Initialised must be TRUE */
373     fgInitialiseInputDevices();
374 }
375
376 /*
377  * Perform the freeglut deinitialization...
378  */
379 void fgDeinitialize( void )
380 {
381     SFG_Timer *timer;
382
383     if( !fgState.Initialised )
384     {
385         fgWarning( "fgDeinitialize(): "
386                    "no valid initialization has been performed" );
387         return;
388     }
389
390     /* If there was a menu created, destroy the rendering context */
391     if( fgStructure.MenuContext )
392     {
393 #if TARGET_HOST_POSIX_X11
394         /* Note that the MVisualInfo is not owned by the MenuContext! */
395         glXDestroyContext( fgDisplay.Display, fgStructure.MenuContext->MContext );
396 #endif
397         free( fgStructure.MenuContext );
398         fgStructure.MenuContext = NULL;
399     }
400
401     fgDestroyStructure( );
402
403     while( ( timer = fgState.Timers.First) )
404     {
405         fgListRemove( &fgState.Timers, &timer->Node );
406         free( timer );
407     }
408
409     while( ( timer = fgState.FreeTimers.First) )
410     {
411         fgListRemove( &fgState.FreeTimers, &timer->Node );
412         free( timer );
413     }
414
415 #if !defined(_WIN32_WCE)
416     if ( fgState.JoysticksInitialised )
417         fgJoystickClose( );
418
419     if ( fgState.InputDevsInitialised )
420         fgInputDeviceClose( );
421 #endif /* !defined(_WIN32_WCE) */
422     fgState.JoysticksInitialised = GL_FALSE;
423     fgState.InputDevsInitialised = GL_FALSE;
424
425     fgState.MajorVersion = 1;
426     fgState.MinorVersion = 0;
427     fgState.ContextFlags = 0;
428
429     fgState.Initialised = GL_FALSE;
430
431     fgState.Position.X = -1;
432     fgState.Position.Y = -1;
433     fgState.Position.Use = GL_FALSE;
434
435     fgState.Size.X = 300;
436     fgState.Size.Y = 300;
437     fgState.Size.Use = GL_TRUE;
438
439     fgState.DisplayMode = GLUT_RGBA | GLUT_SINGLE | GLUT_DEPTH;
440
441     fgState.DirectContext  = GLUT_TRY_DIRECT_CONTEXT;
442     fgState.ForceIconic         = GL_FALSE;
443     fgState.UseCurrentContext   = GL_FALSE;
444     fgState.GLDebugSwitch       = GL_FALSE;
445     fgState.XSyncSwitch         = GL_FALSE;
446     fgState.ActionOnWindowClose = GLUT_ACTION_EXIT;
447     fgState.ExecState           = GLUT_EXEC_STATE_INIT;
448
449     fgState.KeyRepeat       = GLUT_KEY_REPEAT_ON;
450     fgState.Modifiers       = INVALID_MODIFIERS;
451
452     fgState.GameModeSize.X  = 640;
453     fgState.GameModeSize.Y  = 480;
454     fgState.GameModeDepth   =  16;
455     fgState.GameModeRefresh =  72;
456
457     fgListInit( &fgState.Timers );
458     fgListInit( &fgState.FreeTimers );
459
460     fgState.IdleCallback = NULL;
461     fgState.MenuStateCallback = ( FGCBMenuState )NULL;
462     fgState.MenuStatusCallback = ( FGCBMenuStatus )NULL;
463
464     fgState.SwapCount   = 0;
465     fgState.SwapTime    = 0;
466     fgState.FPSInterval = 0;
467
468     if( fgState.ProgramName )
469     {
470         free( fgState.ProgramName );
471         fgState.ProgramName = NULL;
472     }
473
474 #if TARGET_HOST_POSIX_X11
475
476     /*
477      * Make sure all X-client data we have created will be destroyed on
478      * display closing
479      */
480     XSetCloseDownMode( fgDisplay.Display, DestroyAll );
481
482     /*
483      * Close the display connection, destroying all windows we have
484      * created so far
485      */
486     XCloseDisplay( fgDisplay.Display );
487
488 #elif TARGET_HOST_MS_WINDOWS
489
490     /* Reset the timer granularity */
491     timeEndPeriod ( 1 );
492
493 #endif
494
495     fgState.Initialised = GL_FALSE;
496 }
497
498 /*
499  * Everything inside the following #ifndef is copied from the X sources.
500  */
501
502 #if TARGET_HOST_MS_WINDOWS
503
504 /*
505
506 Copyright 1985, 1986, 1987,1998  The Open Group
507
508 Permission to use, copy, modify, distribute, and sell this software and its
509 documentation for any purpose is hereby granted without fee, provided that
510 the above copyright notice appear in all copies and that both that
511 copyright notice and this permission notice appear in supporting
512 documentation.
513
514 The above copyright notice and this permission notice shall be included
515 in all copies or substantial portions of the Software.
516
517 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
518 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
519 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
520 IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
521 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
522 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
523 OTHER DEALINGS IN THE SOFTWARE.
524
525 Except as contained in this notice, the name of The Open Group shall
526 not be used in advertising or otherwise to promote the sale, use or
527 other dealings in this Software without prior written authorization
528 from The Open Group.
529
530 */
531
532 #define NoValue         0x0000
533 #define XValue          0x0001
534 #define YValue          0x0002
535 #define WidthValue      0x0004
536 #define HeightValue     0x0008
537 #define AllValues       0x000F
538 #define XNegative       0x0010
539 #define YNegative       0x0020
540
541 /*
542  *    XParseGeometry parses strings of the form
543  *   "=<width>x<height>{+-}<xoffset>{+-}<yoffset>", where
544  *   width, height, xoffset, and yoffset are unsigned integers.
545  *   Example:  "=80x24+300-49"
546  *   The equal sign is optional.
547  *   It returns a bitmask that indicates which of the four values
548  *   were actually found in the string.  For each value found,
549  *   the corresponding argument is updated;  for each value
550  *   not found, the corresponding argument is left unchanged.
551  */
552
553 static int
554 ReadInteger(char *string, char **NextString)
555 {
556     register int Result = 0;
557     int Sign = 1;
558
559     if (*string == '+')
560         string++;
561     else if (*string == '-')
562     {
563         string++;
564         Sign = -1;
565     }
566     for (; (*string >= '0') && (*string <= '9'); string++)
567     {
568         Result = (Result * 10) + (*string - '0');
569     }
570     *NextString = string;
571     if (Sign >= 0)
572         return Result;
573     else
574         return -Result;
575 }
576
577 static int XParseGeometry (
578     const char *string,
579     int *x,
580     int *y,
581     unsigned int *width,    /* RETURN */
582     unsigned int *height)    /* RETURN */
583 {
584     int mask = NoValue;
585     register char *strind;
586     unsigned int tempWidth = 0, tempHeight = 0;
587     int tempX = 0, tempY = 0;
588     char *nextCharacter;
589
590     if ( (string == NULL) || (*string == '\0'))
591       return mask;
592     if (*string == '=')
593         string++;  /* ignore possible '=' at beg of geometry spec */
594
595     strind = (char *)string;
596     if (*strind != '+' && *strind != '-' && *strind != 'x') {
597         tempWidth = ReadInteger(strind, &nextCharacter);
598         if (strind == nextCharacter)
599             return 0;
600         strind = nextCharacter;
601         mask |= WidthValue;
602     }
603
604     if (*strind == 'x' || *strind == 'X') {
605         strind++;
606         tempHeight = ReadInteger(strind, &nextCharacter);
607         if (strind == nextCharacter)
608             return 0;
609         strind = nextCharacter;
610         mask |= HeightValue;
611     }
612
613     if ((*strind == '+') || (*strind == '-')) {
614         if (*strind == '-') {
615             strind++;
616             tempX = -ReadInteger(strind, &nextCharacter);
617             if (strind == nextCharacter)
618                 return 0;
619             strind = nextCharacter;
620             mask |= XNegative;
621         }
622         else
623         {
624             strind++;
625             tempX = ReadInteger(strind, &nextCharacter);
626             if (strind == nextCharacter)
627                 return 0;
628             strind = nextCharacter;
629         }
630         mask |= XValue;
631         if ((*strind == '+') || (*strind == '-')) {
632             if (*strind == '-') {
633                 strind++;
634                 tempY = -ReadInteger(strind, &nextCharacter);
635                 if (strind == nextCharacter)
636                     return 0;
637                 strind = nextCharacter;
638                 mask |= YNegative;
639             }
640             else
641             {
642                 strind++;
643                 tempY = ReadInteger(strind, &nextCharacter);
644                 if (strind == nextCharacter)
645                     return 0;
646                 strind = nextCharacter;
647             }
648             mask |= YValue;
649         }
650     }
651
652     /* If strind isn't at the end of the string the it's an invalid
653        geometry specification. */
654
655     if (*strind != '\0') return 0;
656
657     if (mask & XValue)
658         *x = tempX;
659     if (mask & YValue)
660         *y = tempY;
661     if (mask & WidthValue)
662         *width = tempWidth;
663     if (mask & HeightValue)
664         *height = tempHeight;
665     return mask;
666 }
667 #endif
668
669 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
670
671 /*
672  * Perform initialization. This usually happens on the program startup
673  * and restarting after glutMainLoop termination...
674  */
675 void FGAPIENTRY glutInit( int* pargc, char** argv )
676 {
677     char* displayName = NULL;
678     char* geometry = NULL;
679     int i, j, argc = *pargc;
680
681     /* will return true for VC8 (VC2005) and higher */
682 #if TARGET_HOST_MS_WINDOWS && ( _MSC_VER >= 1400 )
683     size_t sLen;
684     errno_t err;
685 #endif
686
687     if( fgState.Initialised )
688         fgError( "illegal glutInit() reinitialization attempt" );
689
690     if (pargc && *pargc && argv && *argv && **argv)
691     {
692         fgState.ProgramName = strdup (*argv);
693
694         if( !fgState.ProgramName )
695             fgError ("Could not allocate space for the program's name.");
696     }
697
698     fgCreateStructure( );
699
700     /* Get start time */
701     fgState.Time = fgSystemTime();
702
703     /* check if GLUT_FPS env var is set */
704 #ifndef _WIN32_WCE
705     {
706     /* will return true for VC8 (VC2005) and higher */
707 #if TARGET_HOST_MS_WINDOWS && ( _MSC_VER >= 1400 )
708         char* fps = NULL;
709         err = _dupenv_s( &fps, &sLen, "GLUT_FPS" );
710         if (err)
711             fgError("Error getting GLUT_FPS environment variable"); 
712 #else
713         const char *fps = getenv( "GLUT_FPS" );
714 #endif
715         if( fps )
716         {
717             int interval;
718             sscanf( fps, "%d", &interval );
719
720             if( interval <= 0 )
721                 fgState.FPSInterval = 5000;  /* 5000 millisecond default */
722             else
723                 fgState.FPSInterval = interval;
724         }
725     /* will return true for VC8 (VC2005) and higher */
726 #if TARGET_HOST_MS_WINDOWS && ( _MSC_VER >= 1400 )
727         free ( fps );  fps = NULL;  /* dupenv_s allocates a string that we must free */
728 #endif
729     }
730
731     /* will return true for VC8 (VC2005) and higher */
732 #if TARGET_HOST_MS_WINDOWS && ( _MSC_VER >= 1400 )
733     err = _dupenv_s( &displayName, &sLen, "DISPLAY" );
734     if (err)
735         fgError("Error getting DISPLAY environment variable");
736 #else
737     displayName = getenv( "DISPLAY" );
738 #endif
739
740     for( i = 1; i < argc; i++ )
741     {
742         if( strcmp( argv[ i ], "-display" ) == 0 )
743         {
744             if( ++i >= argc )
745                 fgError( "-display parameter must be followed by display name" );
746
747             displayName = argv[ i ];
748
749             argv[ i - 1 ] = NULL;
750             argv[ i     ] = NULL;
751             ( *pargc ) -= 2;
752         }
753         else if( strcmp( argv[ i ], "-geometry" ) == 0 )
754         {
755             if( ++i >= argc )
756                 fgError( "-geometry parameter must be followed by window "
757                          "geometry settings" );
758
759             geometry = argv[ i ];
760
761             argv[ i - 1 ] = NULL;
762             argv[ i     ] = NULL;
763             ( *pargc ) -= 2;
764         }
765         else if( strcmp( argv[ i ], "-direct" ) == 0)
766         {
767             if( fgState.DirectContext == GLUT_FORCE_INDIRECT_CONTEXT )
768                 fgError( "parameters ambiguity, -direct and -indirect "
769                     "cannot be both specified" );
770
771             fgState.DirectContext = GLUT_FORCE_DIRECT_CONTEXT;
772             argv[ i ] = NULL;
773             ( *pargc )--;
774         }
775         else if( strcmp( argv[ i ], "-indirect" ) == 0 )
776         {
777             if( fgState.DirectContext == GLUT_FORCE_DIRECT_CONTEXT )
778                 fgError( "parameters ambiguity, -direct and -indirect "
779                     "cannot be both specified" );
780
781             fgState.DirectContext = GLUT_FORCE_INDIRECT_CONTEXT;
782             argv[ i ] = NULL;
783             (*pargc)--;
784         }
785         else if( strcmp( argv[ i ], "-iconic" ) == 0 )
786         {
787             fgState.ForceIconic = GL_TRUE;
788             argv[ i ] = NULL;
789             ( *pargc )--;
790         }
791         else if( strcmp( argv[ i ], "-gldebug" ) == 0 )
792         {
793             fgState.GLDebugSwitch = GL_TRUE;
794             argv[ i ] = NULL;
795             ( *pargc )--;
796         }
797         else if( strcmp( argv[ i ], "-sync" ) == 0 )
798         {
799             fgState.XSyncSwitch = GL_TRUE;
800             argv[ i ] = NULL;
801             ( *pargc )--;
802         }
803     }
804
805     /* Compact {argv}. */
806     for( i = j = 1; i < *pargc; i++, j++ )
807     {
808         /* Guaranteed to end because there are "*pargc" arguments left */
809         while ( argv[ j ] == NULL )
810             j++;
811         if ( i != j )
812             argv[ i ] = argv[ j ];
813     }
814
815 #endif /* _WIN32_WCE */
816
817     /*
818      * Have the display created now. If there wasn't a "-display"
819      * in the program arguments, we will use the DISPLAY environment
820      * variable for opening the X display (see code above):
821      */
822     fghInitialize( displayName );
823     /* will return true for VC8 (VC2005) and higher */
824 #if TARGET_HOST_MS_WINDOWS && ( _MSC_VER >= 1400 )
825     free ( displayName );  displayName = NULL;  /* dupenv_s allocates a string that we must free */
826 #endif
827
828     /*
829      * Geometry parsing deffered until here because we may need the screen
830      * size.
831      */
832
833     if (geometry )
834     {
835         unsigned int parsedWidth, parsedHeight;
836         int mask = XParseGeometry( geometry,
837                                    &fgState.Position.X, &fgState.Position.Y,
838                                    &parsedWidth, &parsedHeight );
839         /* TODO: Check for overflow? */
840         fgState.Size.X = parsedWidth;
841         fgState.Size.Y = parsedHeight;
842
843         if( (mask & (WidthValue|HeightValue)) == (WidthValue|HeightValue) )
844             fgState.Size.Use = GL_TRUE;
845
846         if( mask & XNegative )
847             fgState.Position.X += fgDisplay.ScreenWidth - fgState.Size.X;
848
849         if( mask & YNegative )
850             fgState.Position.Y += fgDisplay.ScreenHeight - fgState.Size.Y;
851
852         if( (mask & (XValue|YValue)) == (XValue|YValue) )
853             fgState.Position.Use = GL_TRUE;
854     }
855 }
856
857 #ifdef _WIN32
858 void (__cdecl *__glutExitFunc)( int retval ) = NULL;
859
860 void FGAPIENTRY __glutInitWithExit( int *argcp, char **argv, void (__cdecl *exitfunc)(int) )
861 {
862   __glutExitFunc = exitfunc;
863   glutInit(argcp, argv);
864 }
865 #endif
866
867 /*
868  * Undoes all the "glutInit" stuff
869  */
870 void FGAPIENTRY glutExit ( void )
871 {
872   fgDeinitialize ();
873 }
874
875 /*
876  * Sets the default initial window position for new windows
877  */
878 void FGAPIENTRY glutInitWindowPosition( int x, int y )
879 {
880     fgState.Position.X = x;
881     fgState.Position.Y = y;
882
883     if( ( x >= 0 ) && ( y >= 0 ) )
884         fgState.Position.Use = GL_TRUE;
885     else
886         fgState.Position.Use = GL_FALSE;
887 }
888
889 /*
890  * Sets the default initial window size for new windows
891  */
892 void FGAPIENTRY glutInitWindowSize( int width, int height )
893 {
894     fgState.Size.X = width;
895     fgState.Size.Y = height;
896
897     if( ( width > 0 ) && ( height > 0 ) )
898         fgState.Size.Use = GL_TRUE;
899     else
900         fgState.Size.Use = GL_FALSE;
901 }
902
903 /*
904  * Sets the default display mode for all new windows
905  */
906 void FGAPIENTRY glutInitDisplayMode( unsigned int displayMode )
907 {
908     /* We will make use of this value when creating a new OpenGL context... */
909     fgState.DisplayMode = displayMode;
910 }
911
912
913 /* -- INIT DISPLAY STRING PARSING ------------------------------------------ */
914
915 static char* Tokens[] =
916 {
917     "alpha", "acca", "acc", "blue", "buffer", "conformant", "depth", "double",
918     "green", "index", "num", "red", "rgba", "rgb", "luminance", "stencil",
919     "single", "stereo", "samples", "slow", "win32pdf", "win32pfd", "xvisual",
920     "xstaticgray", "xgrayscale", "xstaticcolor", "xpseudocolor",
921     "xtruecolor", "xdirectcolor",
922     "xstaticgrey", "xgreyscale", "xstaticcolour", "xpseudocolour",
923     "xtruecolour", "xdirectcolour", "borderless", "aux"
924 };
925 #define NUM_TOKENS             (sizeof(Tokens) / sizeof(*Tokens))
926
927 void FGAPIENTRY glutInitDisplayString( const char* displayMode )
928 {
929     int glut_state_flag = 0 ;
930     /*
931      * Unpack a lot of options from a character string.  The options are
932      * delimited by blanks or tabs.
933      */
934     char *token ;
935     /* will return true for VC8 (VC2005) and higher */
936 #if TARGET_HOST_MS_WINDOWS && ( _MSC_VER >= 1400 )
937     char *next_token = NULL;
938 #endif
939     size_t len = strlen ( displayMode );
940     char *buffer = (char *)malloc ( (len+1) * sizeof(char) );
941     memcpy ( buffer, displayMode, len );
942     buffer[len] = '\0';
943
944     /* will return true for VC8 (VC2005) and higher */
945 #if TARGET_HOST_MS_WINDOWS && ( _MSC_VER >= 1400 )
946     token = strtok_s ( buffer, " \t", &next_token );
947 #else
948     token = strtok ( buffer, " \t" );
949 #endif
950     while ( token )
951     {
952         /* Process this token */
953         int i ;
954
955         /* Temporary fix:  Ignore any length specifications and at least
956          * process the basic token
957          * TODO:  Fix this permanently
958          */
959         size_t cleanlength = strcspn ( token, "=<>~!" );
960
961         for ( i = 0; i < NUM_TOKENS; i++ )
962         {
963             if ( strncmp ( token, Tokens[i], cleanlength ) == 0 ) break ;
964         }
965
966         switch ( i )
967         {
968         case 0 :  /* "alpha":  Alpha color buffer precision in bits */
969             glut_state_flag |= GLUT_ALPHA ;  /* Somebody fix this for me! */
970             break ;
971
972         case 1 :  /* "acca":  Red, green, blue, and alpha accumulation buffer
973                      precision in bits */
974             break ;
975
976         case 2 :  /* "acc":  Red, green, and blue accumulation buffer precision
977                      in bits with zero bits alpha */
978             glut_state_flag |= GLUT_ACCUM ;  /* Somebody fix this for me! */
979             break ;
980
981         case 3 :  /* "blue":  Blue color buffer precision in bits */
982             break ;
983
984         case 4 :  /* "buffer":  Number of bits in the color index color buffer
985                    */
986             break ;
987
988         case 5 :  /* "conformant":  Boolean indicating if the frame buffer
989                      configuration is conformant or not */
990             break ;
991
992         case 6 : /* "depth":  Number of bits of precsion in the depth buffer */
993             glut_state_flag |= GLUT_DEPTH ;  /* Somebody fix this for me! */
994             break ;
995
996         case 7 :  /* "double":  Boolean indicating if the color buffer is
997                      double buffered */
998             glut_state_flag |= GLUT_DOUBLE ;
999             break ;
1000
1001         case 8 :  /* "green":  Green color buffer precision in bits */
1002             break ;
1003
1004         case 9 :  /* "index":  Boolean if the color model is color index or not
1005                    */
1006             glut_state_flag |= GLUT_INDEX ;
1007             break ;
1008
1009         case 10 :  /* "num":  A special capability  name indicating where the
1010                       value represents the Nth frame buffer configuration
1011                       matching the description string */
1012             break ;
1013
1014         case 11 :  /* "red":  Red color buffer precision in bits */
1015             break ;
1016
1017         case 12 :  /* "rgba":  Number of bits of red, green, blue, and alpha in
1018                       the RGBA color buffer */
1019             glut_state_flag |= GLUT_RGBA ;  /* Somebody fix this for me! */
1020             break ;
1021
1022         case 13 :  /* "rgb":  Number of bits of red, green, and blue in the
1023                       RGBA color buffer with zero bits alpha */
1024             glut_state_flag |= GLUT_RGB ;  /* Somebody fix this for me! */
1025             break ;
1026
1027         case 14 :  /* "luminance":  Number of bits of red in the RGBA and zero
1028                       bits of green, blue (alpha not specified) of color buffer
1029                       precision */
1030             glut_state_flag |= GLUT_LUMINANCE ; /* Somebody fix this for me! */
1031             break ;
1032
1033         case 15 :  /* "stencil":  Number of bits in the stencil buffer */
1034             glut_state_flag |= GLUT_STENCIL;  /* Somebody fix this for me! */
1035             break ;
1036
1037         case 16 :  /* "single":  Boolean indicate the color buffer is single
1038                       buffered */
1039             glut_state_flag |= GLUT_SINGLE ;
1040             break ;
1041
1042         case 17 :  /* "stereo":  Boolean indicating the color buffer supports
1043                       OpenGL-style stereo */
1044             glut_state_flag |= GLUT_STEREO ;
1045             break ;
1046
1047         case 18 :  /* "samples":  Indicates the number of multisamples to use
1048                       based on GLX's SGIS_multisample extension (for
1049                       antialiasing) */
1050             glut_state_flag |= GLUT_MULTISAMPLE ; /*Somebody fix this for me!*/
1051             break ;
1052
1053         case 19 :  /* "slow":  Boolean indicating if the frame buffer
1054                       configuration is slow or not */
1055             break ;
1056
1057         case 20 :  /* "win32pdf": (incorrect spelling but was there before */
1058         case 21 :  /* "win32pfd":  matches the Win32 Pixel Format Descriptor by
1059                       number */
1060 #if TARGET_HOST_MS_WINDOWS
1061 #endif
1062             break ;
1063
1064         case 22 :  /* "xvisual":  matches the X visual ID by number */
1065 #if TARGET_HOST_POSIX_X11
1066 #endif
1067             break ;
1068
1069         case 23 :  /* "xstaticgray": */
1070         case 29 :  /* "xstaticgrey":  boolean indicating if the frame buffer
1071                       configuration's X visual is of type StaticGray */
1072 #if TARGET_HOST_POSIX_X11
1073 #endif
1074             break ;
1075
1076         case 24 :  /* "xgrayscale": */
1077         case 30 :  /* "xgreyscale":  boolean indicating if the frame buffer
1078                       configuration's X visual is of type GrayScale */
1079 #if TARGET_HOST_POSIX_X11
1080 #endif
1081             break ;
1082
1083         case 25 :  /* "xstaticcolor": */
1084         case 31 :  /* "xstaticcolour":  boolean indicating if the frame buffer
1085                       configuration's X visual is of type StaticColor */
1086 #if TARGET_HOST_POSIX_X11
1087 #endif
1088             break ;
1089
1090         case 26 :  /* "xpseudocolor": */
1091         case 32 :  /* "xpseudocolour":  boolean indicating if the frame buffer
1092                       configuration's X visual is of type PseudoColor */
1093 #if TARGET_HOST_POSIX_X11
1094 #endif
1095             break ;
1096
1097         case 27 :  /* "xtruecolor": */
1098         case 33 :  /* "xtruecolour":  boolean indicating if the frame buffer
1099                       configuration's X visual is of type TrueColor */
1100 #if TARGET_HOST_POSIX_X11
1101 #endif
1102             break ;
1103
1104         case 28 :  /* "xdirectcolor": */
1105         case 34 :  /* "xdirectcolour":  boolean indicating if the frame buffer
1106                       configuration's X visual is of type DirectColor */
1107 #if TARGET_HOST_POSIX_X11
1108 #endif
1109             break ;
1110
1111         case 35 :  /* "borderless":  windows should not have borders */
1112 #if TARGET_HOST_POSIX_X11
1113 #endif
1114             break ;
1115
1116         case 36 :  /* "aux":  some number of aux buffers */
1117             glut_state_flag |= GLUT_AUX;
1118             break ;
1119
1120         case 37 :  /* Unrecognized */
1121             fgWarning ( "WARNING - Display string token not recognized:  %s",
1122                         token );
1123             break ;
1124         }
1125
1126     /* will return true for VC8 (VC2005) and higher */
1127 #if TARGET_HOST_MS_WINDOWS && ( _MSC_VER >= 1400 )
1128         token = strtok_s ( NULL, " \t", &next_token );
1129 #else
1130         token = strtok ( NULL, " \t" );
1131 #endif
1132     }
1133
1134     free ( buffer );
1135
1136     /* We will make use of this value when creating a new OpenGL context... */
1137     fgState.DisplayMode = glut_state_flag;
1138 }
1139
1140 /* -- SETTING OPENGL 3.0 CONTEXT CREATION PARAMETERS ---------------------- */
1141
1142 void FGAPIENTRY glutInitContextVersion( int majorVersion, int minorVersion )
1143 {
1144     /* We will make use of these valuse when creating a new OpenGL context... */
1145     fgState.MajorVersion = majorVersion;
1146     fgState.MinorVersion = minorVersion;
1147 }
1148
1149
1150 void FGAPIENTRY glutInitContextFlags( int flags )
1151 {
1152     /* We will make use of this value when creating a new OpenGL context... */
1153     fgState.ContextFlags = flags;
1154 }
1155
1156 /*** END OF FILE ***/