de6cbb42ccc69424b87368c9dfaea9c90d134cf4
[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      * Query the current display settings:
64      */
65     fgDisplay.DisplayModeValid = 
66       XF86VidModeGetModeLine(
67         fgDisplay.Display,
68         fgDisplay.Screen,
69         &fgDisplay.DisplayModeClock,
70         &fgDisplay.DisplayMode
71     );
72
73     if (!fgDisplay.DisplayModeValid)
74         fgWarning( "Runtime use of XF86VidModeGetModeLine failed.\n" );
75
76 #   else
77 #       warning fghRememberState: missing XFree86 video mode extensions, game mode will not change screen resolution when activated
78 #   endif
79
80 #elif TARGET_HOST_WIN32
81
82 /*    DEVMODE devMode; */
83
84     /*
85      * Grab the current desktop settings...
86      */
87
88 /* hack to get around my stupid cross-gcc headers */
89 #define FREEGLUT_ENUM_CURRENT_SETTINGS -1
90
91     EnumDisplaySettings( NULL, FREEGLUT_ENUM_CURRENT_SETTINGS, &fgDisplay.DisplayMode );
92
93     /*
94      * Make sure we will be restoring all settings needed
95      */
96     fgDisplay.DisplayMode.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
97
98 #endif
99 }
100
101 /*
102  * Restores the previously remembered visual settings
103  */
104 void fghRestoreState( void )
105 {
106 #if TARGET_HOST_UNIX_X11
107
108     /*
109      * This highly depends on the XFree86 extensions, not approved as X Consortium standards
110      */
111 #   ifdef X_XF86VidModeGetAllModeLines
112
113     if (fgDisplay.DisplayModeValid)
114     {
115         XF86VidModeModeInfo** displayModes;
116         int i, displayModesCount;
117
118         /*
119          * Query for all the display available...
120          */
121         XF86VidModeGetAllModeLines(
122             fgDisplay.Display,
123             fgDisplay.Screen,
124             &displayModesCount,
125             &displayModes
126         );
127
128         /*
129          * Check every of the modes looking for one that matches our demands
130          */
131         for( i=0; i<displayModesCount; i++ )
132         {
133             if( displayModes[ i ]->hdisplay == fgDisplay.DisplayMode.hdisplay &&
134                 displayModes[ i ]->vdisplay == fgDisplay.DisplayMode.vdisplay &&
135                 displayModes[ i ]->dotclock == fgDisplay.DisplayModeClock )
136             {
137                 /*
138                  * OKi, this is the display mode we have been looking for...
139                  */
140                 XF86VidModeSwitchToMode(
141                     fgDisplay.Display,
142                     fgDisplay.Screen,
143                     displayModes[ i ]
144                 );
145
146                     /*
147                      * In case this will be the last X11 call we do before exit,
148                      * we've to flush the X11 output queue to be sure the command
149                      * is really brought onto it's way to the X server.
150                      * The application should not do this because it
151                      * would not be platform independent then.
152                      */
153                     XFlush(fgDisplay.Display);
154
155                 return;
156             }
157         }
158     }
159
160 #   else
161 #       warning fghRestoreState: missing XFree86 video mode extensions, game mode will not change screen resolution when activated
162 #   endif
163
164 #elif TARGET_HOST_WIN32
165
166     /*
167      * Restore the previously rememebered desktop display settings
168      */
169     ChangeDisplaySettings( &fgDisplay.DisplayMode, 0 );
170
171 #endif
172 }
173
174 /*
175  * Checks the display mode settings against user's preferences
176  */
177 GLboolean fghCheckDisplayMode( int width, int height, int depth, int refresh )
178 {
179     /*
180      * The desired values should be stored in fgState structure...
181      */
182     return( (width == fgState.GameModeSize.X) && (height == fgState.GameModeSize.Y) &&
183             (depth == fgState.GameModeDepth)  && (refresh == fgState.GameModeRefresh) );
184 }
185
186 /*
187  * Changes the current display mode to match user's settings
188  */
189 GLboolean fghChangeDisplayMode( GLboolean haveToTest )
190 {
191 #if TARGET_HOST_UNIX_X11
192
193     /*
194      * This highly depends on the XFree86 extensions, not approved as X Consortium standards
195      */
196 #   ifdef X_XF86VidModeGetAllModeLines
197
198     if (fgDisplay.DisplayModeValid)
199     {
200         XF86VidModeModeInfo** displayModes;
201         int i, displayModesCount;
202
203         /*
204          * Query for all the display available...
205          */
206         XF86VidModeGetAllModeLines(
207             fgDisplay.Display,
208             fgDisplay.Screen,
209             &displayModesCount,
210             &displayModes
211         );
212
213         /*
214          * Check every of the modes looking for one that matches our demands
215          */
216         for( i=0; i<displayModesCount; i++ )
217         {
218             if( fghCheckDisplayMode( displayModes[ i ]->hdisplay, displayModes[ i ]->vdisplay,
219                                      fgState.GameModeDepth, fgState.GameModeRefresh ) )
220             {
221                     if( haveToTest )
222                                 return( TRUE );
223                 /*
224                  * OKi, this is the display mode we have been looking for...
225                  */
226                 XF86VidModeSwitchToMode(
227                     fgDisplay.Display,
228                     fgDisplay.Screen,
229                     displayModes[ i ]
230                 );
231
232                 /*
233                  * Set the viewport's origin to (0,0) (the game mode window's top-left corner)
234                  */
235                 XF86VidModeSetViewPort(
236                     fgDisplay.Display,
237                     fgDisplay.Screen,
238                     0,
239                     0
240                 );
241
242                 /*
243                  * Return successfull...
244                  */
245                 return( TRUE );
246             }
247         }
248     }
249
250     /*
251      * Something must have went wrong
252      */
253     return( FALSE );
254
255 #   else
256 #       warning fghChangeDisplayMode: missing XFree86 video mode extensions, game mode will not change screen resolution when activated
257 #   endif
258
259 #elif TARGET_HOST_WIN32
260
261     unsigned int    displayModes = 0, mode = 0xffffffff;
262     GLboolean success = FALSE;
263 /*    HDC      desktopDC; */
264     DEVMODE  devMode;
265
266     /*
267      * Enumerate the available display modes
268      * Try to get a complete match
269      */
270     while( EnumDisplaySettings( NULL, displayModes, &devMode ) == TRUE )
271     {
272         /*
273          * Does the enumerated display mode match the user's preferences?
274          */
275         if( fghCheckDisplayMode( devMode.dmPelsWidth,  devMode.dmPelsHeight,
276                                  devMode.dmBitsPerPel, devMode.dmDisplayFrequency ) )
277         {
278             /*
279              * OKi, we've found a matching display mode, remember its number and break
280              */
281             mode = displayModes;
282             break;
283         }
284
285         /*
286          * Switch to the next display mode, if any
287          */
288         displayModes++;
289     }
290
291     if ( mode == 0xffffffff )
292     {
293       /* then try without Display Frequency */
294       displayModes = 0;
295
296       /*
297        * Enumerate the available display modes
298        */
299       while( EnumDisplaySettings( NULL, displayModes, &devMode ) == TRUE )
300       {
301         /* then try without Display Frequency */
302
303         if( fghCheckDisplayMode( devMode.dmPelsWidth,  devMode.dmPelsHeight,
304                                  devMode.dmBitsPerPel, fgState.GameModeRefresh))
305         {
306           /*
307            * OKi, we've found a matching display mode, remember its number and break
308            */
309           mode = displayModes;
310           break;
311         }
312         
313         /*
314          * Switch to the next display mode, if any
315          */
316         displayModes++;
317       }
318     }
319
320     /*
321      * Did we find a matching display mode?
322      */
323     if( mode != 0xffffffff )
324     {
325         int retVal = DISP_CHANGE_SUCCESSFUL;
326
327         /*
328          * Mark the values we want to modify in the display change call
329          */
330         devMode.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
331
332         /*
333          * Change the current display mode (possibly in test mode only)
334          */
335         retVal = ChangeDisplaySettings( &devMode, haveToTest ? CDS_TEST : 0 );
336
337         /*
338          * I don't know if it's really needed, but looks nice:
339          */
340         success = (retVal == DISP_CHANGE_SUCCESSFUL) || (retVal == DISP_CHANGE_NOTUPDATED);
341
342         /*
343          * If it was not a test, remember the current screen settings
344          */
345         if( !haveToTest && success )
346         {
347             fgState.GameModeSize.X  = devMode.dmPelsWidth;
348             fgState.GameModeSize.Y  = devMode.dmPelsHeight;
349             fgState.GameModeDepth   = devMode.dmBitsPerPel;
350             fgState.GameModeRefresh = devMode.dmDisplayFrequency;
351         }
352     }
353
354     /*
355      * Otherwise we must have failed somewhere
356      */
357     return( success );
358
359 #endif
360 }
361
362
363 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
364
365 /*
366  * Sets the game mode display string
367  */
368 void FGAPIENTRY glutGameModeString( const char* string )
369 {
370     int width = 640, height = 480, depth = 16, refresh = 72;
371
372     /*
373      * This one seems a bit easier than glutInitDisplayString. The bad thing
374      * about it that I was unable to find the game mode string definition, so
375      * that I assumed it is: "[width]x[height]:[depth]@[refresh rate]", which
376      * appears in all GLUT game mode programs I have seen to date.
377      */
378     if( sscanf( string, "%ix%i:%i@%i", &width, &height, &depth, &refresh ) != 4 )
379         if( sscanf( string, "%ix%i:%i", &width, &height, &depth ) != 3 )
380             if( sscanf( string, "%ix%i@%i", &width, &height, &refresh ) != 3 )
381                 if( sscanf( string, "%ix%i", &width, &height ) != 2 )
382                     if( sscanf( string, ":%i@%i", &depth, &refresh ) != 2 )
383                         if( sscanf( string, ":%i", &depth ) != 1 )
384                             if( sscanf( string, "@%i", &refresh ) != 1 )
385                                 fgWarning( "unable to parse game mode string `%s'", string );
386
387     /*
388      * Hopefully it worked, and if not, we still have the default values
389      */
390     fgState.GameModeSize.X  = width;
391     fgState.GameModeSize.Y  = height;
392     fgState.GameModeDepth   = depth;
393     fgState.GameModeRefresh = refresh;
394 }
395
396 /*
397  * Enters the game mode
398  */
399 int FGAPIENTRY glutEnterGameMode( void )
400 {
401     /*
402      * Check if a game mode window already exists...
403      */
404     if( fgStructure.GameMode != NULL )
405     {
406         /*
407          * ...if so, delete it before proceeding...
408          */
409         fgAddToWindowDestroyList( fgStructure.GameMode, TRUE );
410     }
411     else
412     {
413         /*
414          * ...otherwise remember the current resolution, etc.
415          */
416         fghRememberState();
417     }
418
419     /*
420      * We are ready to change the current screen's resolution now
421      */
422     if( fghChangeDisplayMode( FALSE ) == FALSE )
423     {
424               fgWarning( "failed to change screen settings" );
425         return( FALSE );
426     }
427
428     /*
429      * Finally, have the game mode window created
430      */
431     fgStructure.GameMode = fgCreateWindow( 
432         NULL, "FREEGLUT", 0, 0, fgState.GameModeSize.X, fgState.GameModeSize.Y, TRUE 
433     );
434
435 #if TARGET_HOST_UNIX_X11
436
437     /*
438      * Move the mouse pointer over the game mode window
439      */
440     XSetInputFocus(
441         fgDisplay.Display,
442         fgStructure.GameMode->Window.Handle,
443         RevertToNone,
444         CurrentTime
445     );
446
447     /*
448      * Confine the mouse pointer to the window's client area
449      */
450     XGrabPointer(
451         fgDisplay.Display,
452         fgStructure.GameMode->Window.Handle,
453         TRUE,
454         ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|PointerMotionMask,
455         GrabModeAsync, GrabModeAsync,
456         fgStructure.GameMode->Window.Handle,
457         None,
458         CurrentTime
459     );
460
461     /*
462      * Grab the keyboard, too
463      */
464     XGrabKeyboard(
465         fgDisplay.Display,
466         fgStructure.GameMode->Window.Handle,
467         FALSE,
468         GrabModeAsync, GrabModeAsync,
469         CurrentTime
470     );
471
472 #endif
473
474     /*
475      * Return successfull
476      */
477     return( TRUE );
478 }
479
480 /*
481  * Leaves the game mode
482  */
483 void FGAPIENTRY glutLeaveGameMode( void )
484 {
485     freeglut_return_if_fail( fgStructure.GameMode != NULL );
486
487     /*
488      * First of all, have the game mode window destroyed
489      */
490     fgAddToWindowDestroyList( fgStructure.GameMode, TRUE );
491
492 #if TARGET_HOST_UNIX_X11
493
494     /*
495      * Ungrab the mouse and keyboard
496      */
497     XUngrabPointer( fgDisplay.Display, CurrentTime );
498     XUngrabKeyboard( fgDisplay.Display, CurrentTime );
499
500 #endif
501
502     /*
503      * Then, have the desktop visual settings restored
504      */
505     fghRestoreState();
506 }
507
508 /*
509  * Returns information concerning the freeglut game mode
510  */
511 int FGAPIENTRY glutGameModeGet( GLenum eWhat )
512 {
513     /*
514      * See why are we bothered
515      */
516     switch( eWhat )
517     {
518     case GLUT_GAME_MODE_ACTIVE:
519         /*
520          * Check if the game mode is currently active
521          */
522         return( fgStructure.GameMode != NULL );
523
524     case GLUT_GAME_MODE_POSSIBLE:
525         /*
526          * Check if the current game mode settings are valid
527          */
528         return( fghChangeDisplayMode( TRUE ) );
529
530     case GLUT_GAME_MODE_WIDTH:
531         /*
532          * The game mode screen width
533          */
534         return( fgState.GameModeSize.X );
535
536     case GLUT_GAME_MODE_HEIGHT:
537         /*
538          * The game mode screen height
539          */
540         return( fgState.GameModeSize.Y );
541
542     case GLUT_GAME_MODE_PIXEL_DEPTH:
543         /*
544          * The game mode pixel depth
545          */
546         return( fgState.GameModeDepth );
547
548     case GLUT_GAME_MODE_REFRESH_RATE:
549         /*
550          * The game mode refresh rate
551          */
552         return( fgState.GameModeRefresh );
553
554     case GLUT_GAME_MODE_DISPLAY_CHANGED:
555         /*
556          * This is true if the game mode has been activated successfully..
557          */
558         return( fgStructure.GameMode != NULL );
559     }
560
561     return( -1 );
562 }
563
564 /*** END OF FILE ***/
565
566
567
568