Fix compilation warning about unused variables and functions
[freeglut] / src / x11 / fg_gamemode_x11.c
1 /*
2  * freeglut_gamemode_x11.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  * Copied for Platform code by Evan Felix <karcaw at gmail.com>
9  * Creation date: Thur Feb 2 2012
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
24  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
25  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27  */
28
29 #include <GL/freeglut.h>
30 #include "../fg_internal.h"
31
32 #ifdef HAVE_X11_EXTENSIONS_XRANDR_H
33 static int xrandr_resize(int xsz, int ysz, int rate, int just_checking)
34 {
35     int event_base, error_base, ver_major, ver_minor, use_rate;
36     XRRScreenConfiguration *xrr_config = 0;
37     Status result = -1;
38
39     /* must check at runtime for the availability of the extension */
40     if(!XRRQueryExtension(fgDisplay.pDisplay.Display, &event_base, &error_base)) {
41         return -1;
42     }
43
44     XRRQueryVersion(fgDisplay.pDisplay.Display, &ver_major, &ver_minor);
45
46     /* we only heed the rate if we CAN actually use it (Xrandr >= 1.1) and
47      * the user actually cares about it (rate > 0)
48      */
49     use_rate = ( rate > 0 ) && ( ( ver_major > 1 ) ||
50                                          ( ( ver_major == 1 ) && ( ver_minor >= 1 ) ) );
51
52     /* this loop is only so that the whole thing will be repeated if someone
53      * else changes video mode between our query of the current information and
54      * the attempt to change it.
55      */
56     do {
57         XRRScreenSize *ssizes;
58         short *rates;
59         Rotation rot;
60         int i, ssizes_count, rates_count, curr, res_idx = -1;
61         Time timestamp, cfg_timestamp;
62
63         if(xrr_config) {
64             XRRFreeScreenConfigInfo(xrr_config);
65         }
66
67         if(!(xrr_config = XRRGetScreenInfo(fgDisplay.pDisplay.Display, fgDisplay.pDisplay.RootWindow))) {
68             fgWarning("XRRGetScreenInfo failed");
69             break;
70         }
71         ssizes = XRRConfigSizes(xrr_config, &ssizes_count);
72         curr = XRRConfigCurrentConfiguration(xrr_config, &rot);
73         timestamp = XRRConfigTimes(xrr_config, &cfg_timestamp);
74
75         /* if either of xsz or ysz are unspecified, use the current values */
76         if(xsz <= 0)
77             xsz = fgState.GameModeSize.X = ssizes[curr].width;
78         if(ysz <= 0)
79             ysz = fgState.GameModeSize.Y = ssizes[curr].height;
80
81
82         if(xsz == ssizes[curr].width && ysz == ssizes[curr].height) {
83             /* no need to switch, we're already in the requested resolution */
84             res_idx = curr;
85         } else {
86             for(i=0; i<ssizes_count; i++) {
87                 if(ssizes[i].width == xsz && ssizes[i].height == ysz) {
88                     res_idx = i;
89                     break;  /* found it */
90                 }
91             }
92         }
93         if(res_idx == -1)
94             break;  /* no matching resolution */
95
96 #if ( RANDR_MAJOR > 1 ) || ( ( RANDR_MAJOR == 1 ) && ( RANDR_MINOR >= 1 ) )
97         if(use_rate) {
98             rate = fgState.GameModeRefresh;
99
100             /* for the selected resolution, let's find out if there is
101              * a matching refresh rate available.
102              */
103             rates = XRRConfigRates(xrr_config, res_idx, &rates_count);
104
105             for(i=0; i<rates_count; i++) {
106                 if(rates[i] == rate) {
107                     break;
108                 }
109             }
110             if(i == rates_count) {
111                 break; /* no matching rate */
112             }
113         }
114 #endif
115
116         if(just_checking) {
117             result = 0;
118             break;
119         }
120
121 #if ( RANDR_MAJOR > 1 ) || ( ( RANDR_MAJOR == 1 ) && ( RANDR_MINOR >= 1 ) )
122         if(use_rate)
123             result = XRRSetScreenConfigAndRate(fgDisplay.pDisplay.Display, xrr_config,
124                     fgDisplay.pDisplay.RootWindow, res_idx, rot, rate, timestamp);
125         else
126 #endif
127             result = XRRSetScreenConfig(fgDisplay.pDisplay.Display, xrr_config,
128                     fgDisplay.pDisplay.RootWindow, res_idx, rot, timestamp);
129
130     } while(result == RRSetConfigInvalidTime);
131
132     if(xrr_config) {
133         XRRFreeScreenConfigInfo(xrr_config);
134     }
135
136     if(result == 0) {
137         return 0;
138     }
139
140     return -1;
141 }
142 #endif  /* HAVE_X11_EXTENSIONS_XRANDR_H */
143
144 /*
145  * Remembers the current visual settings, so that
146  * we can change them and restore later...
147  */
148 void fgPlatformRememberState( void )
149 {
150 #   if defined(HAVE_X11_EXTENSIONS_XRANDR_H) | defined(HAVE_X11_EXTENSIONS_XF86VMODE_H)
151     int event_base, error_base;
152 #   endif
153
154     /*
155      * Remember the current pointer location before going fullscreen
156      * for restoring it later:
157      */
158     Window junk_window;
159     unsigned int junk_mask;
160
161     XQueryPointer(fgDisplay.pDisplay.Display, fgDisplay.pDisplay.RootWindow,
162             &junk_window, &junk_window,
163             &fgDisplay.pDisplay.DisplayPointerX, &fgDisplay.pDisplay.DisplayPointerY,
164             &fgDisplay.pDisplay.DisplayPointerX, &fgDisplay.pDisplay.DisplayPointerY, &junk_mask);
165
166 #   ifdef HAVE_X11_EXTENSIONS_XRANDR_H
167     if(XRRQueryExtension(fgDisplay.pDisplay.Display, &event_base, &error_base)) {
168         XRRScreenConfiguration *xrr_config;
169         XRRScreenSize *ssizes;
170         Rotation rot;
171         int ssize_count, curr;
172
173         if((xrr_config = XRRGetScreenInfo(fgDisplay.pDisplay.Display, fgDisplay.pDisplay.RootWindow))) {
174             ssizes = XRRConfigSizes(xrr_config, &ssize_count);
175             curr = XRRConfigCurrentConfiguration(xrr_config, &rot);
176
177             fgDisplay.pDisplay.prev_xsz = ssizes[curr].width;
178             fgDisplay.pDisplay.prev_ysz = ssizes[curr].height;
179             fgDisplay.pDisplay.prev_refresh = -1;
180
181 #       if ( RANDR_MAJOR > 1 ) || ( ( RANDR_MAJOR == 1 ) && ( RANDR_MINOR >= 1 ) )
182             if(fgState.GameModeRefresh != -1) {
183                 fgDisplay.pDisplay.prev_refresh = XRRConfigCurrentRate(xrr_config);
184             }
185 #       endif
186
187             fgDisplay.pDisplay.prev_size_valid = 1;
188
189             XRRFreeScreenConfigInfo(xrr_config);
190         }
191     }
192 #   endif
193
194     /*
195      * This highly depends on the XFree86 extensions,
196      * not approved as X Consortium standards
197      */
198 #   ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
199     if(!XF86VidModeQueryExtension(fgDisplay.pDisplay.Display, &event_base, &error_base)) {
200         return;
201     }
202
203     /*
204      * Remember the current ViewPort location of the screen to be able to
205      * restore the ViewPort on LeaveGameMode():
206      */
207     if( !XF86VidModeGetViewPort(
208              fgDisplay.pDisplay.Display,
209              fgDisplay.pDisplay.Screen,
210              &fgDisplay.pDisplay.DisplayViewPortX,
211              &fgDisplay.pDisplay.DisplayViewPortY ) )
212         fgWarning( "XF86VidModeGetViewPort failed" );
213
214
215     /* Query the current display settings: */
216     fgDisplay.pDisplay.DisplayModeValid =
217       XF86VidModeGetModeLine(
218         fgDisplay.pDisplay.Display,
219         fgDisplay.pDisplay.Screen,
220         &fgDisplay.pDisplay.DisplayModeClock,
221         &fgDisplay.pDisplay.DisplayMode
222     );
223
224     if( !fgDisplay.pDisplay.DisplayModeValid )
225         fgWarning( "XF86VidModeGetModeLine failed" );
226 #   endif
227
228 }
229
230 /*
231  * Restores the previously remembered visual settings
232  */
233 void fgPlatformRestoreState( void )
234 {
235     /* Restore the remembered pointer position: */
236     XWarpPointer(
237         fgDisplay.pDisplay.Display, None, fgDisplay.pDisplay.RootWindow, 0, 0, 0, 0,
238         fgDisplay.pDisplay.DisplayPointerX, fgDisplay.pDisplay.DisplayPointerY
239     );
240
241
242 #   ifdef HAVE_X11_EXTENSIONS_XRANDR_H
243     if(fgDisplay.pDisplay.prev_size_valid) {
244         if(xrandr_resize(fgDisplay.pDisplay.prev_xsz, fgDisplay.pDisplay.prev_ysz, fgDisplay.pDisplay.prev_refresh, 0) != -1) {
245             fgDisplay.pDisplay.prev_size_valid = 0;
246 #       ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
247             fgDisplay.pDisplay.DisplayModeValid = 0;
248 #       endif
249             return;
250         }
251     }
252 #   endif
253
254
255
256 #   ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
257     /*
258      * This highly depends on the XFree86 extensions,
259      * not approved as X Consortium standards
260      */
261
262     if( fgDisplay.pDisplay.DisplayModeValid )
263     {
264         XF86VidModeModeInfo** displayModes;
265         int i, displayModesCount;
266
267         if( !XF86VidModeGetAllModeLines(
268                  fgDisplay.pDisplay.Display,
269                  fgDisplay.pDisplay.Screen,
270                  &displayModesCount,
271                  &displayModes ) )
272         {
273             fgWarning( "XF86VidModeGetAllModeLines failed" );
274             return;
275         }
276
277
278         /*
279          * Check every of the modes looking for one that matches our demands.
280          * If we find one, switch to it and restore the remembered viewport.
281          */
282         for( i = 0; i < displayModesCount; i++ )
283         {
284             if(displayModes[ i ]->hdisplay == fgDisplay.pDisplay.DisplayMode.hdisplay &&
285                displayModes[ i ]->vdisplay == fgDisplay.pDisplay.DisplayMode.vdisplay &&
286                displayModes[ i ]->dotclock == fgDisplay.pDisplay.DisplayModeClock )
287             {
288                 if( !XF86VidModeSwitchToMode(
289                          fgDisplay.pDisplay.Display,
290                          fgDisplay.pDisplay.Screen,
291                          displayModes[ i ] ) )
292                 {
293                     fgWarning( "XF86VidModeSwitchToMode failed" );
294                     break;
295                 }
296
297                 if( !XF86VidModeSetViewPort(
298                          fgDisplay.pDisplay.Display,
299                          fgDisplay.pDisplay.Screen,
300                          fgDisplay.pDisplay.DisplayViewPortX,
301                          fgDisplay.pDisplay.DisplayViewPortY ) )
302                     fgWarning( "XF86VidModeSetViewPort failed" );
303
304
305                 /*
306                  * For the case this would be the last X11 call the application
307                  * calls exit() we've to flush the X11 output queue to have the
308                  * commands sent to the X server before the application exits.
309                  */
310                 XFlush( fgDisplay.pDisplay.Display );
311
312                 fgDisplay.pDisplay.DisplayModeValid = 0;
313 #       ifdef HAVE_X11_EXTENSIONS_XRANDR_H
314                 fgDisplay.pDisplay.prev_size_valid = 0;
315 #       endif
316
317                 break;
318             }
319         }
320         XFree( displayModes );
321     }
322
323 #   endif
324
325 }
326
327 #ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
328
329 /*
330  * Checks a single display mode settings against user's preferences.
331  */
332 static GLboolean fghCheckDisplayMode( int width, int height, int depth, int refresh )
333 {
334     /* The desired values should be stored in fgState structure... */
335     return ( width == fgState.GameModeSize.X ) &&
336            ( height == fgState.GameModeSize.Y ) &&
337            ( depth == fgState.GameModeDepth ) &&
338            ( refresh == fgState.GameModeRefresh );
339 }
340
341 /*
342  * Checks all display modes settings against user's preferences.
343  * Returns the mode number found or -1 if none could be found.
344  */
345 static int fghCheckDisplayModes( GLboolean exactMatch, int displayModesCount, XF86VidModeModeInfo** displayModes )
346 {
347     int i;
348     for( i = 0; i < displayModesCount; i++ )
349     {
350         /* Compute the displays refresh rate, dotclock comes in kHz. */
351         int refresh = ( displayModes[ i ]->dotclock * 1000 ) /
352                       ( displayModes[ i ]->htotal * displayModes[ i ]->vtotal );
353
354         if( fghCheckDisplayMode( displayModes[ i ]->hdisplay,
355                                  displayModes[ i ]->vdisplay,
356                                  fgState.GameModeDepth,
357                                  ( exactMatch ? refresh : fgState.GameModeRefresh ) ) ) {
358             if (!exactMatch)
359             {
360                 /* Update the chosen refresh rate, otherwise a
361                  * glutGameModeGet(GLUT_GAME_MODE_REFRESH_RATE) would not
362                  * return the right values
363                  */
364                 fgState.GameModeRefresh = refresh;
365             }
366
367             return i;
368         }
369     }
370     return -1;
371 }
372
373 #endif
374
375 /*
376  * Changes the current display mode to match user's settings
377  */
378 GLboolean fgPlatformChangeDisplayMode( GLboolean haveToTest )
379 {
380     GLboolean success = GL_FALSE;
381     /* first try to use XRandR, then fallback to XF86VidMode */
382 #   ifdef HAVE_X11_EXTENSIONS_XRANDR_H
383     if(xrandr_resize(fgState.GameModeSize.X, fgState.GameModeSize.Y,
384                 fgState.GameModeRefresh, haveToTest) != -1) {
385         return GL_TRUE;
386     }
387 #   endif
388
389
390     /*
391      * This highly depends on the XFree86 extensions,
392      * not approved as X Consortium standards
393      */
394 #   ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
395
396     /*
397      * This is also used by applications which check modes by calling
398      * glutGameModeGet(GLUT_GAME_MODE_POSSIBLE), so allow the check:
399      */
400     if( haveToTest || fgDisplay.pDisplay.DisplayModeValid )
401     {
402         XF86VidModeModeInfo** displayModes;
403         int i, displayModesCount;
404
405         /* If we don't have a valid modeline in the display structure, which
406          * can happen if this is called from glutGameModeGet instead of
407          * glutEnterGameMode, then we need to query the current mode, to make
408          * unspecified settings to default to their current values.
409          */
410         if(!fgDisplay.pDisplay.DisplayModeValid) {
411             if(!XF86VidModeGetModeLine(fgDisplay.pDisplay.Display, fgDisplay.pDisplay.Screen,
412                     &fgDisplay.pDisplay.DisplayModeClock, &fgDisplay.pDisplay.DisplayMode)) {
413                 return success;
414             }
415         }
416
417         if (fgState.GameModeSize.X == -1)
418         {
419             fgState.GameModeSize.X = fgDisplay.pDisplay.DisplayMode.hdisplay;
420         }
421         if (fgState.GameModeSize.Y == -1)
422         {
423             fgState.GameModeSize.Y = fgDisplay.pDisplay.DisplayMode.vdisplay;
424         }
425         if (fgState.GameModeDepth == -1)
426         {
427             /* can't get color depth from this, nor can we change it, do nothing
428              * TODO: get with XGetVisualInfo()? but then how to set?
429              */
430         }
431         if (fgState.GameModeRefresh == -1)
432         {
433             /* Compute the displays refresh rate, dotclock comes in kHz. */
434             int refresh = ( fgDisplay.pDisplay.DisplayModeClock * 1000 ) /
435                 ( fgDisplay.pDisplay.DisplayMode.htotal * fgDisplay.pDisplay.DisplayMode.vtotal );
436
437             fgState.GameModeRefresh = refresh;
438         }
439
440         /* query all possible display modes */
441         if( !XF86VidModeGetAllModeLines(
442                  fgDisplay.pDisplay.Display,
443                  fgDisplay.pDisplay.Screen,
444                  &displayModesCount,
445                  &displayModes ) )
446         {
447             fgWarning( "XF86VidModeGetAllModeLines failed" );
448             return success;
449         }
450
451
452         /*
453          * Check every of the modes looking for one that matches our demands,
454          * ignoring the refresh rate if no exact match could be found.
455          */
456         i = fghCheckDisplayModes( GL_TRUE, displayModesCount, displayModes );
457         if( i < 0 ) {
458             i = fghCheckDisplayModes( GL_FALSE, displayModesCount, displayModes );
459         }
460         success = ( i < 0 ) ? GL_FALSE : GL_TRUE;
461
462         if( !haveToTest && success ) {
463             if( !XF86VidModeSwitchToMode(
464                      fgDisplay.pDisplay.Display,
465                      fgDisplay.pDisplay.Screen,
466                      displayModes[ i ] ) )
467                 fgWarning( "XF86VidModeSwitchToMode failed" );
468         }
469
470         XFree( displayModes );
471     }
472
473 #   endif
474
475     return success;
476 }
477
478
479 void fgPlatformEnterGameMode( void ) 
480 {
481
482     /*
483      * Sync needed to avoid a real race, the Xserver must have really created
484      * the window before we can grab the pointer into it:
485      */
486     XSync( fgDisplay.pDisplay.Display, False );
487     /*
488      * Grab the pointer to confine it into the window after the calls to
489      * XWrapPointer() which ensure that the pointer really enters the window.
490      *
491      * We also need to wait here until XGrabPointer() returns GrabSuccess,
492      * otherwise the new window is not viewable yet and if the next function
493      * (XSetInputFocus) is called with a not yet viewable window, it will exit
494      * the application which we have to aviod, so wait until it's viewable:
495      */
496     while( GrabSuccess != XGrabPointer(
497                fgDisplay.pDisplay.Display, fgStructure.GameModeWindow->Window.Handle,
498                TRUE,
499                ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
500                | PointerMotionMask,
501                GrabModeAsync, GrabModeAsync,
502                fgStructure.GameModeWindow->Window.Handle, None, CurrentTime) )
503         usleep( 100 );
504     /*
505      * Change input focus to the new window. This will exit the application
506      * if the new window is not viewable yet, see the XGrabPointer loop above.
507      */
508     XSetInputFocus(
509         fgDisplay.pDisplay.Display,
510         fgStructure.GameModeWindow->Window.Handle,
511         RevertToNone,
512         CurrentTime
513     );
514
515     /* Move the Pointer to the middle of the fullscreen window */
516     XWarpPointer(
517         fgDisplay.pDisplay.Display,
518         None,
519         fgDisplay.pDisplay.RootWindow,
520         0, 0, 0, 0,
521         fgState.GameModeSize.X/2, fgState.GameModeSize.Y/2
522     );
523
524 #   ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
525
526     if( fgDisplay.pDisplay.DisplayModeValid )
527     {
528         int x, y;
529         Window child;
530
531         /* Change to viewport to the window topleft edge: */
532         if( !XF86VidModeSetViewPort( fgDisplay.pDisplay.Display, fgDisplay.pDisplay.Screen, 0, 0 ) )
533             fgWarning( "XF86VidModeSetViewPort failed" );
534
535         /*
536          * Final window repositioning: It could be avoided using an undecorated
537          * window using override_redirect, but this * would possily require
538          * more changes and investigation.
539          */
540
541         /* Get the current postion of the drawable area on screen */
542         XTranslateCoordinates(
543             fgDisplay.pDisplay.Display,
544             fgStructure.CurrentWindow->Window.Handle,
545             fgDisplay.pDisplay.RootWindow,
546             0, 0, &x, &y,
547             &child
548         );
549
550         /* Move the decorataions out of the topleft corner of the display */
551         XMoveWindow( fgDisplay.pDisplay.Display, fgStructure.CurrentWindow->Window.Handle,
552                      -x, -y);
553     }
554
555 #endif
556
557     /* Grab the keyboard, too */
558     XGrabKeyboard(
559         fgDisplay.pDisplay.Display,
560         fgStructure.GameModeWindow->Window.Handle,
561         FALSE,
562         GrabModeAsync, GrabModeAsync,
563         CurrentTime
564     );
565
566 }
567
568 void fgPlatformLeaveGameMode( void ) 
569 {
570     XUngrabPointer( fgDisplay.pDisplay.Display, CurrentTime );
571     XUngrabKeyboard( fgDisplay.pDisplay.Display, CurrentTime );
572 }
573