Fixing a 'gamemode at_exit' bug courtesy of Diederick Niehorster per e-mail dated...
[freeglut] / src / freeglut_gamemode.c
1 /*
2  * freeglut_gamemode.c
3  *
4  * The game mode handling code.
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 16 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 #include <GL/freeglut.h>
29 #include "freeglut_internal.h"
30
31 /*
32  * TODO BEFORE THE STABLE RELEASE:
33  *
34  *  glutGameModeString()    -- missing
35  *  glutEnterGameMode()     -- X11 version
36  *  glutLeaveGameMode()     -- is that correct?
37  *  glutGameModeGet()       -- is that correct?
38  */
39
40
41 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
42
43 #if TARGET_HOST_POSIX_X11
44 static int xrandr_resize(int xsz, int ysz, int rate, int just_checking)
45 {
46 #ifdef HAVE_X11_EXTENSIONS_XRANDR_H
47     int event_base, error_base, ver_major, ver_minor, use_rate;
48     XRRScreenConfiguration *xrr_config = 0;
49     Status result = -1;
50
51     /* must check at runtime for the availability of the extension */
52     if(!XRRQueryExtension(fgDisplay.Display, &event_base, &error_base)) {
53         return -1;
54     }
55
56     XRRQueryVersion(fgDisplay.Display, &ver_major, &ver_minor);
57
58     /* we only heed the rate if we CAN actually use it (Xrandr >= 1.1) and
59      * the user actually cares about it (rate > 0)
60      */
61     use_rate = rate > 0 && ver_major >= 1 && ver_minor >= 1;
62
63     /* this loop is only so that the whole thing will be repeated if someone
64      * else changes video mode between our query the current information and
65      * the attempt to change it.
66      */
67     do {
68         XRRScreenSize *ssizes;
69         short *rates;
70         Rotation rot;
71         int i, ssizes_count, rates_count, curr, res_idx = -1;
72         Time timestamp, cfg_timestamp;
73
74         if(xrr_config) {
75             XRRFreeScreenConfigInfo(xrr_config);
76         }
77
78         if(!(xrr_config = XRRGetScreenInfo(fgDisplay.Display, fgDisplay.RootWindow))) {
79             fgWarning("XRRGetScreenInfo failed");
80             break;
81         }
82         ssizes = XRRConfigSizes(xrr_config, &ssizes_count);
83         curr = XRRConfigCurrentConfiguration(xrr_config, &rot);
84         timestamp = XRRConfigTimes(xrr_config, &cfg_timestamp);
85
86         /* if either of xsz or ysz are unspecified, use the current values */
87         if(xsz <= 0)
88             xsz = fgState.GameModeSize.X = ssizes[curr].width;
89         if(ysz <= 0)
90             ysz = fgState.GameModeSize.Y = ssizes[curr].height;
91
92
93         if(xsz == ssizes[curr].width && ysz == ssizes[curr].height) {
94             /* no need to switch, we're already in the requested resolution */
95             res_idx = curr;
96         } else {
97             for(i=0; i<ssizes_count; i++) {
98                 if(ssizes[i].width == xsz && ssizes[i].height == ysz) {
99                     res_idx = i;
100                     break;  /* found it */
101                 }
102             }
103         }
104         if(res_idx == -1)
105             break;  /* no matching resolution */
106
107 #if RANDR_MAJOR >= 1 && RANDR_MINOR >= 1
108         if(rate <= 0) {
109             fgState.GameModeRefresh = XRRConfigCurrentRate(xrr_config);
110         }
111
112         if(use_rate) {
113             rate = fgState.GameModeRefresh;
114
115             /* for the selected resolution, let's find out if there is
116              * a matching refresh rate available.
117              */
118             rates = XRRConfigRates(xrr_config, res_idx, &rates_count);
119
120             for(i=0; i<rates_count; i++) {
121                 if(rates[i] == rate) {
122                     break;
123                 }
124             }
125             if(i == rates_count) {
126                 break; /* no matching rate */
127             }
128         }
129 #endif
130
131         if(just_checking) {
132             result = 0;
133             break;
134         }
135
136 #if RANDR_MAJOR >= 1 && RANDR_MINOR >= 1
137         if(use_rate)
138             result = XRRSetScreenConfigAndRate(fgDisplay.Display, xrr_config,
139                     fgDisplay.RootWindow, res_idx, rot, rate, timestamp);
140         else
141 #endif
142             result = XRRSetScreenConfig(fgDisplay.Display, xrr_config,
143                     fgDisplay.RootWindow, res_idx, rot, timestamp);
144
145     } while(result == RRSetConfigInvalidTime);
146
147     if(xrr_config) {
148         XRRFreeScreenConfigInfo(xrr_config);
149     }
150
151     if(result == 0) {
152         return 0;
153     }
154
155 #endif  /* HAVE_X11_EXTENSIONS_XRANDR_H */
156     return -1;
157 }
158 #endif  /* TARGET_HOST_POSIX_X11 */
159
160 /*
161  * Remembers the current visual settings, so that
162  * we can change them and restore later...
163  */
164 static void fghRememberState( void )
165 {
166 #if TARGET_HOST_POSIX_X11
167     int event_base, error_base;
168
169     /*
170      * Remember the current pointer location before going fullscreen
171      * for restoring it later:
172      */
173     Window junk_window;
174     unsigned int junk_mask;
175
176     XQueryPointer(fgDisplay.Display, fgDisplay.RootWindow,
177             &junk_window, &junk_window,
178             &fgDisplay.DisplayPointerX, &fgDisplay.DisplayPointerY,
179             &fgDisplay.DisplayPointerX, &fgDisplay.DisplayPointerY, &junk_mask);
180
181 #   ifdef HAVE_X11_EXTENSIONS_XRANDR_H
182     if(XRRQueryExtension(fgDisplay.Display, &event_base, &error_base)) {
183         XRRScreenConfiguration *xrr_config;
184         XRRScreenSize *ssizes;
185         Rotation rot;
186         int ssize_count, curr;
187
188         if((xrr_config = XRRGetScreenInfo(fgDisplay.Display, fgDisplay.RootWindow))) {
189             ssizes = XRRConfigSizes(xrr_config, &ssize_count);
190             curr = XRRConfigCurrentConfiguration(xrr_config, &rot);
191
192             fgDisplay.prev_xsz = ssizes[curr].width;
193             fgDisplay.prev_ysz = ssizes[curr].height;
194             fgDisplay.prev_refresh = -1;
195
196 #       if RANDR_MAJOR >= 1 && RANDR_MINOR >= 1
197             if(fgState.GameModeRefresh != -1) {
198                 fgDisplay.prev_refresh = XRRConfigCurrentRate(xrr_config);
199             }
200 #       endif
201
202             fgDisplay.prev_size_valid = 1;
203
204             XRRFreeScreenConfigInfo(xrr_config);
205         }
206     }
207 #   endif
208
209     /*
210      * This highly depends on the XFree86 extensions,
211      * not approved as X Consortium standards
212      */
213 #   ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
214     if(!XF86VidModeQueryExtension(fgDisplay.Display, &event_base, &error_base)) {
215         return;
216     }
217
218     /*
219      * Remember the current ViewPort location of the screen to be able to
220      * restore the ViewPort on LeaveGameMode():
221      */
222     if( !XF86VidModeGetViewPort(
223              fgDisplay.Display,
224              fgDisplay.Screen,
225              &fgDisplay.DisplayViewPortX,
226              &fgDisplay.DisplayViewPortY ) )
227         fgWarning( "XF86VidModeGetViewPort failed" );
228
229
230     /* Query the current display settings: */
231     fgDisplay.DisplayModeValid =
232       XF86VidModeGetModeLine(
233         fgDisplay.Display,
234         fgDisplay.Screen,
235         &fgDisplay.DisplayModeClock,
236         &fgDisplay.DisplayMode
237     );
238
239     if( !fgDisplay.DisplayModeValid )
240         fgWarning( "XF86VidModeGetModeLine failed" );
241 #   endif
242
243 #elif TARGET_HOST_MS_WINDOWS
244
245 /*    DEVMODE devMode; */
246
247     /* Grab the current desktop settings... */
248
249 /* hack to get around my stupid cross-gcc headers */
250 #define FREEGLUT_ENUM_CURRENT_SETTINGS -1
251
252     EnumDisplaySettings( fgDisplay.DisplayName, FREEGLUT_ENUM_CURRENT_SETTINGS,
253                          &fgDisplay.DisplayMode );
254
255     /* Make sure we will be restoring all settings needed */
256     fgDisplay.DisplayMode.dmFields |=
257         DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
258
259 #endif
260 }
261
262 /*
263  * Restores the previously remembered visual settings
264  */
265 static void fghRestoreState( void )
266 {
267 #if TARGET_HOST_POSIX_X11
268     /* Restore the remembered pointer position: */
269     XWarpPointer(
270         fgDisplay.Display, None, fgDisplay.RootWindow, 0, 0, 0, 0,
271         fgDisplay.DisplayPointerX, fgDisplay.DisplayPointerY
272     );
273
274
275 #   ifdef HAVE_X11_EXTENSIONS_XRANDR_H
276     if(fgDisplay.prev_size_valid) {
277         if(xrandr_resize(fgDisplay.prev_xsz, fgDisplay.prev_ysz, fgDisplay.prev_refresh, 0) != -1) {
278             fgDisplay.prev_size_valid = 0;
279 #       ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
280             fgDisplay.DisplayModeValid = 0;
281 #       endif
282             return;
283         }
284     }
285 #   endif
286
287
288
289 #   ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
290     /*
291      * This highly depends on the XFree86 extensions,
292      * not approved as X Consortium standards
293      */
294
295     if( fgDisplay.DisplayModeValid )
296     {
297         XF86VidModeModeInfo** displayModes;
298         int i, displayModesCount;
299
300         if( !XF86VidModeGetAllModeLines(
301                  fgDisplay.Display,
302                  fgDisplay.Screen,
303                  &displayModesCount,
304                  &displayModes ) )
305         {
306             fgWarning( "XF86VidModeGetAllModeLines failed" );
307             return;
308         }
309
310
311         /*
312          * Check every of the modes looking for one that matches our demands.
313          * If we find one, switch to it and restore the remembered viewport.
314          */
315         for( i = 0; i < displayModesCount; i++ )
316         {
317             if(displayModes[ i ]->hdisplay == fgDisplay.DisplayMode.hdisplay &&
318                displayModes[ i ]->vdisplay == fgDisplay.DisplayMode.vdisplay &&
319                displayModes[ i ]->dotclock == fgDisplay.DisplayModeClock )
320             {
321                 if( !XF86VidModeSwitchToMode(
322                          fgDisplay.Display,
323                          fgDisplay.Screen,
324                          displayModes[ i ] ) )
325                 {
326                     fgWarning( "XF86VidModeSwitchToMode failed" );
327                     break;
328                 }
329
330                 if( !XF86VidModeSetViewPort(
331                          fgDisplay.Display,
332                          fgDisplay.Screen,
333                          fgDisplay.DisplayViewPortX,
334                          fgDisplay.DisplayViewPortY ) )
335                     fgWarning( "XF86VidModeSetViewPort failed" );
336
337
338                 /*
339                  * For the case this would be the last X11 call the application
340                  * calls exit() we've to flush the X11 output queue to have the
341                  * commands sent to the X server before the application exits.
342                  */
343                 XFlush( fgDisplay.Display );
344
345                 fgDisplay.DisplayModeValid = 0;
346 #       ifdef HAVE_X11_EXTENSIONS_XRANDR_H
347                 fgDisplay.prev_size_valid = 0;
348 #       endif
349
350                 break;
351             }
352         }
353         XFree( displayModes );
354     }
355
356 #   endif
357
358 #elif TARGET_HOST_MS_WINDOWS
359
360     /* Restore the previously remembered desktop display settings */
361     ChangeDisplaySettingsEx( fgDisplay.DisplayName,&fgDisplay.DisplayMode, 0,0,0 );
362
363 #endif
364 }
365
366 #if TARGET_HOST_POSIX_X11
367 #ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
368
369 /*
370  * Checks a single display mode settings against user's preferences.
371  */
372 static GLboolean fghCheckDisplayMode( int width, int height, int depth, int refresh )
373 {
374     /* The desired values should be stored in fgState structure... */
375     return ( width == fgState.GameModeSize.X ) &&
376            ( height == fgState.GameModeSize.Y ) &&
377            ( depth == fgState.GameModeDepth ) &&
378            ( refresh == fgState.GameModeRefresh );
379 }
380
381 /*
382  * Checks all display modes settings against user's preferences.
383  * Returns the mode number found or -1 if none could be found.
384  */
385 static int fghCheckDisplayModes( GLboolean exactMatch, int displayModesCount, XF86VidModeModeInfo** displayModes )
386 {
387     int i;
388     for( i = 0; i < displayModesCount; i++ )
389     {
390         /* Compute the displays refresh rate, dotclock comes in kHz. */
391         int refresh = ( displayModes[ i ]->dotclock * 1000 ) /
392                       ( displayModes[ i ]->htotal * displayModes[ i ]->vtotal );
393
394         if( fghCheckDisplayMode( displayModes[ i ]->hdisplay,
395                                  displayModes[ i ]->vdisplay,
396                                  fgState.GameModeDepth,
397                                  ( exactMatch ? refresh : fgState.GameModeRefresh ) ) ) {
398             if (!exactMatch)
399             {
400                 /* Update the chosen refresh rate, otherwise a
401                  * glutGameModeGet(GLUT_GAME_MODE_REFRESH_RATE) would not
402                  * return the right values
403                  */
404                 fgState.GameModeRefresh = refresh;
405             }
406
407             return i;
408         }
409     }
410     return -1;
411 }
412
413 #endif
414 #endif
415
416 /*
417  * Changes the current display mode to match user's settings
418  */
419 static GLboolean fghChangeDisplayMode( GLboolean haveToTest )
420 {
421     GLboolean success = GL_FALSE;
422 #if TARGET_HOST_POSIX_X11
423
424     /* first try to use XRandR, then fallback to XF86VidMode */
425 #   ifdef HAVE_X11_EXTENSIONS_XRANDR_H
426     if(xrandr_resize(fgState.GameModeSize.X, fgState.GameModeSize.Y,
427                 fgState.GameModeRefresh, haveToTest) != -1) {
428         return GL_TRUE;
429     }
430 #   endif
431
432
433     /*
434      * This highly depends on the XFree86 extensions,
435      * not approved as X Consortium standards
436      */
437 #   ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
438
439     /*
440      * This is also used by applications which check modes by calling
441      * glutGameModeGet(GLUT_GAME_MODE_POSSIBLE), so allow the check:
442      */
443     if( haveToTest || fgDisplay.DisplayModeValid )
444     {
445         XF86VidModeModeInfo** displayModes;
446         int i, displayModesCount;
447
448         /* If we don't have a valid modeline in the display structure, which
449          * can happen if this is called from glutGameModeGet instead of
450          * glutEnterGameMode, then we need to query the current mode, to make
451          * unspecified settings to default to their current values.
452          */
453         if(!fgDisplay.DisplayModeValid) {
454             if(!XF86VidModeGetModeLine(fgDisplay.Display, fgDisplay.Screen,
455                     &fgDisplay.DisplayModeClock, &fgDisplay.DisplayMode)) {
456                 return success;
457             }
458         }
459
460         if (fgState.GameModeSize.X == -1)
461         {
462             fgState.GameModeSize.X = fgDisplay.DisplayMode.hdisplay;
463         }
464         if (fgState.GameModeSize.Y == -1)
465         {
466             fgState.GameModeSize.Y = fgDisplay.DisplayMode.vdisplay;
467         }
468         if (fgState.GameModeDepth == -1)
469         {
470             /* can't get color depth from this, nor can we change it, do nothing
471              * TODO: get with XGetVisualInfo()? but then how to set?
472              */
473         }
474         if (fgState.GameModeRefresh == -1)
475         {
476             /* Compute the displays refresh rate, dotclock comes in kHz. */
477             int refresh = ( fgDisplay.DisplayModeClock * 1000 ) /
478                 ( fgDisplay.DisplayMode.htotal * fgDisplay.DisplayMode.vtotal );
479
480             fgState.GameModeRefresh = refresh;
481         }
482
483         /* query all possible display modes */
484         if( !XF86VidModeGetAllModeLines(
485                  fgDisplay.Display,
486                  fgDisplay.Screen,
487                  &displayModesCount,
488                  &displayModes ) )
489         {
490             fgWarning( "XF86VidModeGetAllModeLines failed" );
491             return success;
492         }
493
494
495         /*
496          * Check every of the modes looking for one that matches our demands,
497          * ignoring the refresh rate if no exact match could be found.
498          */
499         i = fghCheckDisplayModes( GL_TRUE, displayModesCount, displayModes );
500         if( i < 0 ) {
501             i = fghCheckDisplayModes( GL_FALSE, displayModesCount, displayModes );
502         }
503         success = ( i < 0 ) ? GL_FALSE : GL_TRUE;
504
505         if( !haveToTest && success ) {
506             if( !XF86VidModeSwitchToMode(
507                      fgDisplay.Display,
508                      fgDisplay.Screen,
509                      displayModes[ i ] ) )
510                 fgWarning( "XF86VidModeSwitchToMode failed" );
511         }
512
513         XFree( displayModes );
514     }
515
516 #   endif
517
518
519 #elif TARGET_HOST_MS_WINDOWS
520
521     DEVMODE  devMode;
522     char *fggmstr = NULL;
523     char displayMode[300];
524
525     success = GL_FALSE;
526
527     EnumDisplaySettings( fgDisplay.DisplayName, -1, &devMode ); 
528     devMode.dmFields = 0;
529
530     if (fgState.GameModeSize.X!=-1)
531     {
532         devMode.dmPelsWidth  = fgState.GameModeSize.X;
533         devMode.dmFields |= DM_PELSWIDTH;
534     }
535     if (fgState.GameModeSize.Y!=-1)
536     {
537         devMode.dmPelsHeight  = fgState.GameModeSize.Y;
538         devMode.dmFields |= DM_PELSHEIGHT;
539     }
540     if (fgState.GameModeDepth!=-1)
541     {
542         devMode.dmBitsPerPel  = fgState.GameModeDepth;
543         devMode.dmFields |= DM_BITSPERPEL;
544     }
545     if (fgState.GameModeRefresh!=-1)
546     {
547         devMode.dmDisplayFrequency  = fgState.GameModeRefresh;
548         devMode.dmFields |= DM_DISPLAYFREQUENCY;
549     }
550
551     switch ( ChangeDisplaySettingsEx(fgDisplay.DisplayName, &devMode, NULL, haveToTest ? CDS_TEST : CDS_FULLSCREEN , NULL) )
552     {
553     case DISP_CHANGE_SUCCESSFUL:
554         success = GL_TRUE;
555
556         if (!haveToTest)
557         {
558             /* update vars in case if windows switched to proper mode */
559             EnumDisplaySettings( fgDisplay.DisplayName, FREEGLUT_ENUM_CURRENT_SETTINGS, &devMode );
560             fgState.GameModeSize.X  = devMode.dmPelsWidth;        
561             fgState.GameModeSize.Y  = devMode.dmPelsHeight;
562             fgState.GameModeDepth   = devMode.dmBitsPerPel;
563             fgState.GameModeRefresh = devMode.dmDisplayFrequency;
564         }
565                 break;
566     case DISP_CHANGE_RESTART:
567         fggmstr = "The computer must be restarted for the graphics mode to work.";
568         break;
569     case DISP_CHANGE_BADFLAGS:
570         fggmstr = "An invalid set of flags was passed in.";
571         break;
572     case DISP_CHANGE_BADPARAM:
573         fggmstr = "An invalid parameter was passed in. This can include an invalid flag or combination of flags.";
574         break;
575     case DISP_CHANGE_FAILED:
576         fggmstr = "The display driver failed the specified graphics mode.";
577         break;
578     case DISP_CHANGE_BADMODE:
579         fggmstr = "The graphics mode is not supported.";
580         break;
581     default:
582         fggmstr = "Unknown error in graphics mode???"; /* dunno if it is possible,MSDN does not mention any other error */
583         break;
584     }
585
586     if ( !success )
587     {
588         /* I'd rather get info whats going on in my program than wonder about */
589         /* magic happenings behind my back, its lib for devels at last ;) */
590         
591         /* append display mode to error to make things more informative */
592         sprintf(displayMode,"%s Problem with requested mode: %ix%i:%i@%i", fggmstr, devMode.dmPelsWidth, devMode.dmPelsHeight, devMode.dmBitsPerPel, devMode.dmDisplayFrequency);
593         fgWarning(displayMode);
594     }
595 #endif
596
597     return success;
598 }
599
600
601 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
602
603 /*
604  * Sets the game mode display string
605  */
606 void FGAPIENTRY glutGameModeString( const char* string )
607 {
608     int width = -1, height = -1, depth = -1, refresh = -1;
609
610     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutGameModeString" );
611
612     /*
613      * This one seems a bit easier than glutInitDisplayString. The bad thing
614      * about it that I was unable to find the game mode string definition, so
615      * that I assumed it is: "[width]x[height]:[depth]@[refresh rate]", which
616      * appears in all GLUT game mode programs I have seen to date.
617      */
618     if( sscanf( string, "%ix%i:%i@%i", &width, &height, &depth, &refresh ) !=
619         4 )
620         if( sscanf( string, "%ix%i:%i", &width, &height, &depth ) != 3 )
621             if( sscanf( string, "%ix%i@%i", &width, &height, &refresh ) != 3 )
622                 if( sscanf( string, "%ix%i", &width, &height ) != 2 )
623                     if( sscanf( string, ":%i@%i", &depth, &refresh ) != 2 )
624                         if( sscanf( string, ":%i", &depth ) != 1 )
625                             if( sscanf( string, "@%i", &refresh ) != 1 )
626                                 fgWarning(
627                                     "unable to parse game mode string `%s'",
628                                     string
629                                 );
630
631     /* All values not specified are now set to -1, which means those
632      * aspects of the current display mode are not changed in
633      * fghChangeDisplayMode() above.
634      */
635     fgState.GameModeSize.X  = width;
636     fgState.GameModeSize.Y  = height;
637     fgState.GameModeDepth   = depth;
638     fgState.GameModeRefresh = refresh;
639 }
640
641
642
643 /*
644  * Enters the game mode
645  */
646 int FGAPIENTRY glutEnterGameMode( void )
647 {
648     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutEnterGameMode" );
649
650     if( fgStructure.GameModeWindow )
651         fgAddToWindowDestroyList( fgStructure.GameModeWindow );
652     else
653         fghRememberState( );
654
655     if( ! fghChangeDisplayMode( GL_FALSE ) )
656     {
657         fgWarning( "failed to change screen settings" );
658         return 0;
659     }
660
661     fgStructure.GameModeWindow = fgCreateWindow(
662         NULL, "FREEGLUT", GL_TRUE, 0, 0,
663         GL_TRUE, fgState.GameModeSize.X, fgState.GameModeSize.Y,
664         GL_TRUE, GL_FALSE
665     );
666
667     fgStructure.GameModeWindow->State.Width  = fgState.GameModeSize.X;
668     fgStructure.GameModeWindow->State.Height = fgState.GameModeSize.Y;
669     fgStructure.GameModeWindow->State.NeedToResize = GL_TRUE;
670
671 #if TARGET_HOST_POSIX_X11
672
673     /*
674      * Sync needed to avoid a real race, the Xserver must have really created
675      * the window before we can grab the pointer into it:
676      */
677     XSync( fgDisplay.Display, False );
678     /*
679      * Grab the pointer to confine it into the window after the calls to
680      * XWrapPointer() which ensure that the pointer really enters the window.
681      *
682      * We also need to wait here until XGrabPointer() returns GrabSuccess,
683      * otherwise the new window is not viewable yet and if the next function
684      * (XSetInputFocus) is called with a not yet viewable window, it will exit
685      * the application which we have to aviod, so wait until it's viewable:
686      */
687     while( GrabSuccess != XGrabPointer(
688                fgDisplay.Display, fgStructure.GameModeWindow->Window.Handle,
689                TRUE,
690                ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
691                | PointerMotionMask,
692                GrabModeAsync, GrabModeAsync,
693                fgStructure.GameModeWindow->Window.Handle, None, CurrentTime) )
694         usleep( 100 );
695     /*
696      * Change input focus to the new window. This will exit the application
697      * if the new window is not viewable yet, see the XGrabPointer loop above.
698      */
699     XSetInputFocus(
700         fgDisplay.Display,
701         fgStructure.GameModeWindow->Window.Handle,
702         RevertToNone,
703         CurrentTime
704     );
705
706     /* Move the Pointer to the middle of the fullscreen window */
707     XWarpPointer(
708         fgDisplay.Display,
709         None,
710         fgDisplay.RootWindow,
711         0, 0, 0, 0,
712         fgState.GameModeSize.X/2, fgState.GameModeSize.Y/2
713     );
714
715 #   ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
716
717     if( fgDisplay.DisplayModeValid )
718     {
719         int x, y;
720         Window child;
721
722         /* Change to viewport to the window topleft edge: */
723         if( !XF86VidModeSetViewPort( fgDisplay.Display, fgDisplay.Screen, 0, 0 ) )
724             fgWarning( "XF86VidModeSetViewPort failed" );
725
726         /*
727          * Final window repositioning: It could be avoided using an undecorated
728          * window using override_redirect, but this * would possily require
729          * more changes and investigation.
730          */
731
732         /* Get the current postion of the drawable area on screen */
733         XTranslateCoordinates(
734             fgDisplay.Display,
735             fgStructure.CurrentWindow->Window.Handle,
736             fgDisplay.RootWindow,
737             0, 0, &x, &y,
738             &child
739         );
740
741         /* Move the decorataions out of the topleft corner of the display */
742         XMoveWindow( fgDisplay.Display, fgStructure.CurrentWindow->Window.Handle,
743                      -x, -y);
744     }
745
746 #endif
747
748     /* Grab the keyboard, too */
749     XGrabKeyboard(
750         fgDisplay.Display,
751         fgStructure.GameModeWindow->Window.Handle,
752         FALSE,
753         GrabModeAsync, GrabModeAsync,
754         CurrentTime
755     );
756
757 #endif
758
759     return fgStructure.GameModeWindow->ID;
760 }
761
762 /*
763  * Leaves the game mode
764  */
765 void FGAPIENTRY glutLeaveGameMode( void )
766 {
767     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveGameMode" );
768
769     freeglut_return_if_fail( fgStructure.GameModeWindow );
770
771     fgAddToWindowDestroyList( fgStructure.GameModeWindow );
772     fgStructure.GameModeWindow = NULL;
773
774 #if TARGET_HOST_POSIX_X11
775
776     XUngrabPointer( fgDisplay.Display, CurrentTime );
777     XUngrabKeyboard( fgDisplay.Display, CurrentTime );
778
779 #endif
780
781     fghRestoreState();
782 }
783
784 /*
785  * Returns information concerning the freeglut game mode
786  */
787 int FGAPIENTRY glutGameModeGet( GLenum eWhat )
788 {
789     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutGameModeGet" );
790
791     switch( eWhat )
792     {
793     case GLUT_GAME_MODE_ACTIVE:
794         return !!fgStructure.GameModeWindow;
795
796     case GLUT_GAME_MODE_POSSIBLE:
797         return fghChangeDisplayMode( GL_TRUE );
798
799     case GLUT_GAME_MODE_WIDTH:
800         return fgState.GameModeSize.X;
801
802     case GLUT_GAME_MODE_HEIGHT:
803         return fgState.GameModeSize.Y;
804
805     case GLUT_GAME_MODE_PIXEL_DEPTH:
806         return fgState.GameModeDepth;
807
808     case GLUT_GAME_MODE_REFRESH_RATE:
809         return fgState.GameModeRefresh;
810
811     case GLUT_GAME_MODE_DISPLAY_CHANGED:
812         /*
813          * This is true if the game mode has been activated successfully..
814          */
815         return !!fgStructure.GameModeWindow;
816     }
817
818     fgWarning( "Unknown gamemode get: %d", eWhat );
819     return -1;
820 }
821
822 /*** END OF FILE ***/