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