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