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