Moving that "#if TARGET_HOST_UNIX_X11" to the correct place.
[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 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #define  G_LOG_DOMAIN  "freeglut-gamemode"
33
34 #include "../include/GL/freeglut.h"
35 #include "freeglut_internal.h"
36
37 /*
38  * TODO BEFORE THE STABLE RELEASE:
39  *
40  *  glutGameModeString()    -- missing
41  *  glutEnterGameMode()     -- X11 version
42  *  glutLeaveGameMode()     -- is that correct?
43  *  glutGameModeGet()       -- is that correct?
44  */
45
46
47 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
48
49 /*
50  * Remembers the current visual settings, so that
51  * we can change them and restore later...
52  */
53 void fghRememberState( void )
54 {
55 #if TARGET_HOST_UNIX_X11
56
57     /*
58      * This highly depends on the XFree86 extensions, not approved as X Consortium standards
59      */
60 #   ifdef X_XF86VidModeGetModeLine
61
62
63     /*
64      * Remember the current ViewPort location of the screen to be able to
65      * restore the ViewPort on LeaveGameMode():
66      */
67     XF86VidModeGetViewPort(
68         fgDisplay.Display,
69         fgDisplay.Screen,
70         &fgDisplay.DisplayViewPortX,
71         &fgDisplay.DisplayViewPortY
72     );
73
74     /*
75      * Remember the current pointer location before going fullscreen
76      * for restoring it later:
77      */
78     {
79         Window junk_window;
80         unsigned int mask;
81
82         XQueryPointer(
83             fgDisplay.Display, fgDisplay.RootWindow,
84             &junk_window, &junk_window,
85             &fgDisplay.DisplayPointerX, &fgDisplay.DisplayPointerY,
86             &fgDisplay.DisplayPointerX, &fgDisplay.DisplayPointerY, &mask
87         );
88     }
89
90     /*
91      * Query the current display settings:
92      */
93     fgDisplay.DisplayModeValid = 
94       XF86VidModeGetModeLine(
95         fgDisplay.Display,
96         fgDisplay.Screen,
97         &fgDisplay.DisplayModeClock,
98         &fgDisplay.DisplayMode
99     );
100
101     if (!fgDisplay.DisplayModeValid)
102         fgWarning( "Runtime use of XF86VidModeGetModeLine failed.\n" );
103
104 #   else
105 #       warning fghRememberState: missing XFree86 video mode extensions, game mode will not change screen resolution when activated
106 #   endif
107
108 #elif TARGET_HOST_WIN32
109
110 /*    DEVMODE devMode; */
111
112     /*
113      * Grab the current desktop settings...
114      */
115
116 /* hack to get around my stupid cross-gcc headers */
117 #define FREEGLUT_ENUM_CURRENT_SETTINGS -1
118
119     EnumDisplaySettings( NULL, FREEGLUT_ENUM_CURRENT_SETTINGS, &fgDisplay.DisplayMode );
120
121     /*
122      * Make sure we will be restoring all settings needed
123      */
124     fgDisplay.DisplayMode.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
125
126 #endif
127 }
128
129 /*
130  * Restores the previously remembered visual settings
131  */
132 void fghRestoreState( void )
133 {
134 #if TARGET_HOST_UNIX_X11
135     /*
136      * Restore the remembered pointer position:
137      */
138     XWarpPointer(
139         fgDisplay.Display, None, fgDisplay.RootWindow, 0, 0, 0, 0,
140         fgDisplay.DisplayPointerX, fgDisplay.DisplayPointerY
141     );
142
143     /*
144      * This highly depends on the XFree86 extensions, not approved as X Consortium standards
145      */
146 #   ifdef X_XF86VidModeGetAllModeLines
147
148     if (fgDisplay.DisplayModeValid)
149     {
150         XF86VidModeModeInfo** displayModes;
151         int i, displayModesCount;
152
153         /*
154          * Query for all the display available...
155          */
156         XF86VidModeGetAllModeLines(
157             fgDisplay.Display,
158             fgDisplay.Screen,
159             &displayModesCount,
160             &displayModes
161         );
162
163         /*
164          * Check every of the modes looking for one that matches our demands
165          */
166         for( i=0; i<displayModesCount; i++ )
167         {
168             if( displayModes[ i ]->hdisplay == fgDisplay.DisplayMode.hdisplay &&
169                 displayModes[ i ]->vdisplay == fgDisplay.DisplayMode.vdisplay &&
170                 displayModes[ i ]->dotclock == fgDisplay.DisplayModeClock )
171             {
172                 /*
173                  * OK, this is the display mode we have been looking for...
174                  */
175                 XF86VidModeSwitchToMode(
176                     fgDisplay.Display,
177                     fgDisplay.Screen,
178                     displayModes[ i ]
179                 );
180
181                 /*
182                  * Now we can restore the remembered ViewPort:
183                  */
184                 XF86VidModeSetViewPort(
185                      fgDisplay.Display,
186                      fgDisplay.Screen,
187                      fgDisplay.DisplayViewPortX,
188                      fgDisplay.DisplayViewPortY
189                 );
190
191                 /*
192                  * For the case this would be the last X11 call the application
193                  * calls exit() we've to flush the X11 output queue to have the
194                  * commands sent to the X server before the application exists.
195                  */
196                 XFlush(fgDisplay.Display);
197
198                 return;
199             }
200         }
201     }
202
203 #   else
204 #       warning fghRestoreState: missing XFree86 video mode extensions, game mode will not change screen resolution when activated
205 #   endif
206
207 #elif TARGET_HOST_WIN32
208
209     /*
210      * Restore the previously rememebered desktop display settings
211      */
212     ChangeDisplaySettings( &fgDisplay.DisplayMode, 0 );
213
214 #endif
215 }
216
217 /*
218  * Checks the display mode settings against user's preferences
219  */
220 GLboolean fghCheckDisplayMode( int width, int height, int depth, int refresh )
221 {
222     /*
223      * The desired values should be stored in fgState structure...
224      */
225     return( (width == fgState.GameModeSize.X) && (height == fgState.GameModeSize.Y) &&
226             (depth == fgState.GameModeDepth)  && (refresh == fgState.GameModeRefresh) );
227 }
228
229 /*
230  * Changes the current display mode to match user's settings
231  */
232 GLboolean fghChangeDisplayMode( GLboolean haveToTest )
233 {
234 #if TARGET_HOST_UNIX_X11
235
236     /*
237      * This highly depends on the XFree86 extensions, not approved as X Consortium standards
238      */
239 #   ifdef X_XF86VidModeGetAllModeLines
240
241     /*
242      * This is also used by applcations which check modes by calling
243      * glutGameModeGet(GLUT_GAME_MODE_POSSIBLE), so allow the check:
244      */
245     if (haveToTest || fgDisplay.DisplayModeValid)
246     {
247         XF86VidModeModeInfo** displayModes;
248         int i, displayModesCount;
249
250         /*
251          * Query for all the display available...
252          */
253         XF86VidModeGetAllModeLines(
254             fgDisplay.Display,
255             fgDisplay.Screen,
256             &displayModesCount,
257             &displayModes
258         );
259
260         /*
261          * Check every of the modes looking for one that matches our demands
262          */
263         for( i=0; i<displayModesCount; i++ )
264         {
265             if( fghCheckDisplayMode( displayModes[ i ]->hdisplay, displayModes[ i ]->vdisplay,
266                                      fgState.GameModeDepth, fgState.GameModeRefresh ) )
267             {
268                     if( haveToTest )
269                                 return( TRUE );
270                 /*
271                  * OKi, this is the display mode we have been looking for...
272                  */
273                 XF86VidModeSwitchToMode(
274                     fgDisplay.Display,
275                     fgDisplay.Screen,
276                     displayModes[ i ]
277                 );
278                 /*
279                  * Return successfull...
280                  */
281                 return( TRUE );
282             }
283         }
284     }
285
286     /*
287      * Something must have went wrong
288      */
289     return( FALSE );
290
291 #   else
292 #       warning fghChangeDisplayMode: missing XFree86 video mode extensions, game mode will not change screen resolution when activated
293 #   endif
294
295 #elif TARGET_HOST_WIN32
296
297     unsigned int    displayModes = 0, mode = 0xffffffff;
298     GLboolean success = FALSE;
299 /*    HDC      desktopDC; */
300     DEVMODE  devMode;
301
302     /*
303      * Enumerate the available display modes
304      * Try to get a complete match
305      */
306     while( EnumDisplaySettings( NULL, displayModes, &devMode ) == TRUE )
307     {
308         /*
309          * Does the enumerated display mode match the user's preferences?
310          */
311         if( fghCheckDisplayMode( devMode.dmPelsWidth,  devMode.dmPelsHeight,
312                                  devMode.dmBitsPerPel, devMode.dmDisplayFrequency ) )
313         {
314             /*
315              * OKi, we've found a matching display mode, remember its number and break
316              */
317             mode = displayModes;
318             break;
319         }
320
321         /*
322          * Switch to the next display mode, if any
323          */
324         displayModes++;
325     }
326
327     if ( mode == 0xffffffff )
328     {
329       /* then try without Display Frequency */
330       displayModes = 0;
331
332       /*
333        * Enumerate the available display modes
334        */
335       while( EnumDisplaySettings( NULL, displayModes, &devMode ) == TRUE )
336       {
337         /* then try without Display Frequency */
338
339         if( fghCheckDisplayMode( devMode.dmPelsWidth,  devMode.dmPelsHeight,
340                                  devMode.dmBitsPerPel, fgState.GameModeRefresh))
341         {
342           /*
343            * OKi, we've found a matching display mode, remember its number and break
344            */
345           mode = displayModes;
346           break;
347         }
348         
349         /*
350          * Switch to the next display mode, if any
351          */
352         displayModes++;
353       }
354     }
355
356     /*
357      * Did we find a matching display mode?
358      */
359     if( mode != 0xffffffff )
360     {
361         int retVal = DISP_CHANGE_SUCCESSFUL;
362
363         /*
364          * Mark the values we want to modify in the display change call
365          */
366         devMode.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
367
368         /*
369          * Change the current display mode (possibly in test mode only)
370          */
371         retVal = ChangeDisplaySettings( &devMode, haveToTest ? CDS_TEST : 0 );
372
373         /*
374          * I don't know if it's really needed, but looks nice:
375          */
376         success = (retVal == DISP_CHANGE_SUCCESSFUL) || (retVal == DISP_CHANGE_NOTUPDATED);
377
378         /*
379          * If it was not a test, remember the current screen settings
380          */
381         if( !haveToTest && success )
382         {
383             fgState.GameModeSize.X  = devMode.dmPelsWidth;
384             fgState.GameModeSize.Y  = devMode.dmPelsHeight;
385             fgState.GameModeDepth   = devMode.dmBitsPerPel;
386             fgState.GameModeRefresh = devMode.dmDisplayFrequency;
387         }
388     }
389
390     /*
391      * Otherwise we must have failed somewhere
392      */
393     return( success );
394
395 #endif
396 }
397
398
399 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
400
401 /*
402  * Sets the game mode display string
403  */
404 void FGAPIENTRY glutGameModeString( const char* string )
405 {
406     int width = 640, height = 480, depth = 16, refresh = 72;
407
408     /*
409      * This one seems a bit easier than glutInitDisplayString. The bad thing
410      * about it that I was unable to find the game mode string definition, so
411      * that I assumed it is: "[width]x[height]:[depth]@[refresh rate]", which
412      * appears in all GLUT game mode programs I have seen to date.
413      */
414     if( sscanf( string, "%ix%i:%i@%i", &width, &height, &depth, &refresh ) != 4 )
415         if( sscanf( string, "%ix%i:%i", &width, &height, &depth ) != 3 )
416             if( sscanf( string, "%ix%i@%i", &width, &height, &refresh ) != 3 )
417                 if( sscanf( string, "%ix%i", &width, &height ) != 2 )
418                     if( sscanf( string, ":%i@%i", &depth, &refresh ) != 2 )
419                         if( sscanf( string, ":%i", &depth ) != 1 )
420                             if( sscanf( string, "@%i", &refresh ) != 1 )
421                                 fgWarning( "unable to parse game mode string `%s'", string );
422
423     /*
424      * Hopefully it worked, and if not, we still have the default values
425      */
426     fgState.GameModeSize.X  = width;
427     fgState.GameModeSize.Y  = height;
428     fgState.GameModeDepth   = depth;
429     fgState.GameModeRefresh = refresh;
430 }
431
432 /*
433  * Enters the game mode
434  */
435 int FGAPIENTRY glutEnterGameMode( void )
436 {
437     /*
438      * Check if a game mode window already exists...
439      */
440     if( fgStructure.GameMode != NULL )
441     {
442         /*
443          * ...if so, delete it before proceeding...
444          */
445         fgAddToWindowDestroyList( fgStructure.GameMode, TRUE );
446     }
447     else
448     {
449         /*
450          * ...otherwise remember the current resolution, etc.
451          */
452         fghRememberState();
453     }
454
455     /*
456      * We are ready to change the current screen's resolution now
457      */
458     if( fghChangeDisplayMode( FALSE ) == FALSE )
459     {
460               fgWarning( "failed to change screen settings" );
461         return( FALSE );
462     }
463
464     /*
465      * Finally, have the game mode window created
466      */
467     fgStructure.GameMode = fgCreateWindow( 
468         NULL, "FREEGLUT", 0, 0, fgState.GameModeSize.X, fgState.GameModeSize.Y, TRUE 
469     );
470
471 #if TARGET_HOST_UNIX_X11
472
473     /* Move the window up to the topleft corner */
474     XMoveWindow(fgDisplay.Display, fgStructure.Window->Window.Handle, 0, 0);
475
476     /*
477      * Sync needed to avoid a real race, the Xserver must have really created
478      * the window before we can grab the pointer into it:
479      */
480     XSync(fgDisplay.Display, False);
481
482     /* Move the Pointer to the middle of the fullscreen window */
483     XWarpPointer(
484         fgDisplay.Display,
485         None, 
486         fgDisplay.RootWindow,
487         0, 0, 0, 0,
488         fgState.GameModeSize.X/2, fgState.GameModeSize.Y/2
489     );
490
491     /*
492      * Grab the pointer to confine it into the window after the calls to
493      * XWrapPointer() which ensure that the pointer really enters the window.
494      *
495      * We also need to wait here until XGrabPointer() returns GrabSuccess,
496      * otherwise the new window is not viewable yet and if the next function
497      * (XSetInputFocus) is called with a not yet viewable window, it will exit
498      * the application which we have to aviod, so wait until it's viewable:
499      */
500     while (GrabSuccess =! XGrabPointer(
501             fgDisplay.Display, fgStructure.GameMode->Window.Handle,
502             TRUE, ButtonPressMask|ButtonReleaseMask|ButtonMotionMask
503                 |PointerMotionMask,
504             GrabModeAsync, GrabModeAsync,
505             fgStructure.GameMode->Window.Handle, None, CurrentTime)) {
506         usleep (100);
507     }
508
509     /*
510      * Change input focus to the new window. This will exit the application
511      * if the new window is not viewable yet, see the XGrabPointer loop above.
512      */
513     XSetInputFocus(
514         fgDisplay.Display,
515         fgStructure.GameMode->Window.Handle,
516         RevertToNone,
517         CurrentTime
518     );
519
520 #   ifdef X_XF86VidModeSetViewPort
521
522     if (fgDisplay.DisplayModeValid) {
523         int x, y;
524         Window child;
525
526         /*
527          * Change to viewport to the window topleft edge:
528          */
529         XF86VidModeSetViewPort(fgDisplay.Display, fgDisplay.Screen, 0, 0);
530
531         /*
532          * Final window repositioning: It could be avoided using an undecorated
533          * window using override_redirect, but this * would possily require more
534          * changes and investigation.
535          */
536
537         /* Get the current postion of the drawable area on screen */
538         XTranslateCoordinates(
539             fgDisplay.Display,
540             fgStructure.Window->Window.Handle,
541             fgDisplay.RootWindow,
542             0, 0, &x, &y,
543             &child
544         );
545
546         /* Move the decorataions out of the topleft corner of the display */
547         XMoveWindow(fgDisplay.Display, fgStructure.Window->Window.Handle, -x, -y);
548     }
549
550 #endif
551
552     /*
553      * Grab the keyboard, too
554      */
555     XGrabKeyboard(
556         fgDisplay.Display,
557         fgStructure.GameMode->Window.Handle,
558         FALSE,
559         GrabModeAsync, GrabModeAsync,
560         CurrentTime
561     );
562
563 #endif
564
565     /*
566      * Return successfull
567      */
568     return( TRUE );
569 }
570
571 /*
572  * Leaves the game mode
573  */
574 void FGAPIENTRY glutLeaveGameMode( void )
575 {
576     freeglut_return_if_fail( fgStructure.GameMode != NULL );
577
578     /*
579      * First of all, have the game mode window destroyed
580      */
581     fgAddToWindowDestroyList( fgStructure.GameMode, TRUE );
582
583 #if TARGET_HOST_UNIX_X11
584
585     /*
586      * Ungrab the mouse and keyboard
587      */
588     XUngrabPointer( fgDisplay.Display, CurrentTime );
589     XUngrabKeyboard( fgDisplay.Display, CurrentTime );
590
591 #endif
592
593     /*
594      * Then, have the desktop visual settings restored
595      */
596     fghRestoreState();
597 }
598
599 /*
600  * Returns information concerning the freeglut game mode
601  */
602 int FGAPIENTRY glutGameModeGet( GLenum eWhat )
603 {
604     /*
605      * See why are we bothered
606      */
607     switch( eWhat )
608     {
609     case GLUT_GAME_MODE_ACTIVE:
610         /*
611          * Check if the game mode is currently active
612          */
613         return( fgStructure.GameMode != NULL );
614
615     case GLUT_GAME_MODE_POSSIBLE:
616         /*
617          * Check if the current game mode settings are valid
618          */
619         return( fghChangeDisplayMode( TRUE ) );
620
621     case GLUT_GAME_MODE_WIDTH:
622         /*
623          * The game mode screen width
624          */
625         return( fgState.GameModeSize.X );
626
627     case GLUT_GAME_MODE_HEIGHT:
628         /*
629          * The game mode screen height
630          */
631         return( fgState.GameModeSize.Y );
632
633     case GLUT_GAME_MODE_PIXEL_DEPTH:
634         /*
635          * The game mode pixel depth
636          */
637         return( fgState.GameModeDepth );
638
639     case GLUT_GAME_MODE_REFRESH_RATE:
640         /*
641          * The game mode refresh rate
642          */
643         return( fgState.GameModeRefresh );
644
645     case GLUT_GAME_MODE_DISPLAY_CHANGED:
646         /*
647          * This is true if the game mode has been activated successfully..
648          */
649         return( fgStructure.GameMode != NULL );
650     }
651
652     return( -1 );
653 }
654
655 /*** END OF FILE ***/
656
657
658
659