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