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