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