Ticket #6266: og_gamemode.c

File og_gamemode.c, 21.2 KB (added by michaelvoliveira, 14 years ago)

OpenGLUT gamemode functions

Line 
1/*!
2 \file og_gamemode.c
3 \brief The game mode handling code.
4*/
5/*
6 * Portions copyright (C) 2004, the OpenGLUT project contributors.
7 * OpenGLUT branched from freeglut in February, 2004.
8 *
9 * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
10 * Written by Pawel W. Olszta, <olszta@sourceforge.net>
11 * Creation date: Thu Dec 16 1999
12 *
13 * Permission is hereby granted, free of charge, to any person obtaining a
14 * copy of this software and associated documentation files (the "Software"),
15 * to deal in the Software without restriction, including without limitation
16 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 * and/or sell copies of the Software, and to permit persons to whom the
18 * Software is furnished to do so, subject to the following conditions:
19 *
20 * The above copyright notice and this permission notice shall be included
21 * in all copies or substantial portions of the Software.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
27 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
28 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 */
30
31#ifdef HAVE_CONFIG_H
32#include "config.h"
33#endif
34
35#include <GL/openglut.h>
36#include "og_internal.h"
37
38/* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
39
40/*
41 * Remembers the current visual settings, so that
42 * we can change them and restore later...
43 */
44static void oghRememberState( void )
45{
46#if TARGET_HOST_UNIX_X11
47
48 /*
49 * This highly depends on the XFree86 extensions,
50 * not approved as X Consortium standards
51 */
52# ifdef X_XF86VidModeGetModeLine
53
54
55 /*
56 * Remember the current ViewPort location of the screen to be able to
57 * restore the ViewPort on LeaveGameMode():
58 */
59 XF86VidModeGetViewPort(
60 ogDisplay.Display,
61 ogDisplay.Screen,
62 &ogDisplay.DisplayViewPortX,
63 &ogDisplay.DisplayViewPortY
64 );
65
66 /*
67 * Remember the current pointer location before going fullscreen
68 * for restoring it later:
69 */
70 {
71 Window junk_window;
72 unsigned int mask;
73
74 XQueryPointer(
75 ogDisplay.Display, ogDisplay.RootWindow,
76 &junk_window, &junk_window,
77 &ogDisplay.DisplayPointerX, &ogDisplay.DisplayPointerY,
78 &ogDisplay.DisplayPointerX, &ogDisplay.DisplayPointerY, &mask
79 );
80 }
81
82 /*
83 * Query the current display settings:
84 */
85 ogDisplay.DisplayModeValid =
86 XF86VidModeGetModeLine(
87 ogDisplay.Display,
88 ogDisplay.Screen,
89 &ogDisplay.DisplayModeClock,
90 &ogDisplay.DisplayMode
91 );
92
93 if( !ogDisplay.DisplayModeValid )
94 ogWarning( "Runtime use of XF86VidModeGetModeLine failed." );
95
96# else
97 /*
98 * XXX warning oghRememberState: missing XFree86 video mode extensions,
99 * XXX game mode will not change screen resolution when activated
100 */
101# endif
102
103#elif TARGET_HOST_WIN32 || TARGET_HOST_WINCE
104
105 /* DEVMODE devMode; */
106
107 /*
108 * Grab the current desktop settings...
109 */
110
111 /* hack to get around my stupid cross-gcc headers */
112# define FREEGLUT_ENUM_CURRENT_SETTINGS -1
113
114 EnumDisplaySettings( NULL, FREEGLUT_ENUM_CURRENT_SETTINGS,
115 &ogDisplay.DisplayMode );
116
117 /*
118 * Make sure we will be restoring all settings needed
119 */
120 ogDisplay.DisplayMode.dmFields |=
121 DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
122
123#endif
124}
125
126/*
127 * Restores the previously remembered visual settings
128 */
129static void oghRestoreState( void )
130{
131#if TARGET_HOST_UNIX_X11
132
133# ifdef X_XF86VidModeGetAllModeLines
134 /*
135 * Restore the remembered pointer position:
136 */
137 XWarpPointer(
138 ogDisplay.Display, None, ogDisplay.RootWindow, 0, 0, 0, 0,
139 ogDisplay.DisplayPointerX, ogDisplay.DisplayPointerY
140 );
141
142 /*
143 * This highly depends on the XFree86 extensions,
144 * not approved as X Consortium standards
145 */
146
147 if( ogDisplay.DisplayModeValid )
148 {
149 XF86VidModeModeInfo** displayModes;
150 int i, displayModesCount;
151
152 XF86VidModeGetAllModeLines(
153 ogDisplay.Display,
154 ogDisplay.Screen,
155 &displayModesCount,
156 &displayModes
157 );
158
159 /*
160 * Check every of the modes looking for one that matches our demands.
161 * If we find one, switch to it and restore the remembered viewport.
162 */
163 for( i = 0; i < displayModesCount; i++ )
164 if(displayModes[ i ]->hdisplay == ogDisplay.DisplayMode.hdisplay &&
165 displayModes[ i ]->vdisplay == ogDisplay.DisplayMode.vdisplay &&
166 displayModes[ i ]->dotclock == ogDisplay.DisplayModeClock )
167 {
168 XF86VidModeSwitchToMode(
169 ogDisplay.Display,
170 ogDisplay.Screen,
171 displayModes[ i ]
172 );
173 XF86VidModeSetViewPort(
174 ogDisplay.Display,
175 ogDisplay.Screen,
176 ogDisplay.DisplayViewPortX,
177 ogDisplay.DisplayViewPortY
178 );
179
180 /*
181 * For the case this would be the last X11 call the application
182 * calls exit() we've to flush the X11 output queue to have the
183 * commands sent to the X server before the application exits.
184 */
185 XFlush( ogDisplay.Display );
186
187 return;
188 }
189 }
190
191# else
192 /*
193 * XXX warning oghRestoreState: missing XFree86 video mode extensions,
194 * XXX game mode will not change screen resolution when activated
195 */
196# endif
197
198#elif TARGET_HOST_WIN32 || TARGET_HOST_WINCE
199
200 /*
201 * Restore the previously rememebered desktop display settings
202 */
203 ChangeDisplaySettings( &ogDisplay.DisplayMode, 0 );
204
205#endif
206}
207
208/*
209 * On UNIX_X11, the following function is only referenced if we have
210 * the {X_F86VidModeGetAllModeLins} symbol defined from XFree86.
211 * Since it is a {static} function, we must protect it by #ifdef
212 * to protect against warnings.
213 */
214#if defined( X_XF86VidModeGetAllModeLines ) || !TARGET_HOST_UNIX_X11
215/*
216 * Checks the display mode settings against user's preferences
217 */
218static GLboolean oghCheckDisplayMode(
219 int width, int height, int depth, int refresh
220)
221{
222 /*
223 * The desired values should be stored in ogState structure...
224 */
225 return ( width == ogState.GameModeSize.X ) &&
226 ( height == ogState.GameModeSize.Y ) &&
227 ( depth == ogState.GameModeDepth ) &&
228 (refresh == ogState.GameModeRefresh );
229}
230#endif
231
232/*
233 * Changes the current display mode to match user's settings
234 */
235static GLboolean oghChangeDisplayMode( GLboolean haveToTest )
236{
237#if TARGET_HOST_UNIX_X11
238
239 /*
240 * This highly depends on the XFree86 extensions,
241 * not approved as X Consortium standards
242 */
243# ifdef X_XF86VidModeGetAllModeLines
244
245 /*
246 * This is also used by applcations which check modes by calling
247 * glutGameModeGet(GLUT_GAME_MODE_POSSIBLE), so allow the check:
248 */
249 if( haveToTest || ogDisplay.DisplayModeValid )
250 {
251 XF86VidModeModeInfo** displayModes;
252 int i, displayModesCount;
253
254 XF86VidModeGetAllModeLines(
255 ogDisplay.Display,
256 ogDisplay.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 if( oghCheckDisplayMode(
266 displayModes[ i ]->hdisplay,
267 displayModes[ i ]->vdisplay,
268 ogState.GameModeDepth,
269 ogState.GameModeRefresh
270 ) )
271 {
272 if( haveToTest )
273 return GL_TRUE;
274 /*
275 * OK, this is the display mode we have been looking for...
276 */
277 XF86VidModeSwitchToMode(
278 ogDisplay.Display,
279 ogDisplay.Screen,
280 displayModes[ i ]
281 );
282 return GL_TRUE;
283 }
284 }
285
286 /*
287 * Something must have went wrong
288 */
289 return GL_FALSE;
290
291# else
292 /*
293 * XXX warning oghChangeDisplayMode: missing XFree86 video mode extensions,
294 * XXX game mode will not change screen resolution when activated
295 * XXX
296 * XXX For compatibility with Solaris, et al, we simply lie and
297 * XXX claim to succeed. (^& This lets us use game mode for
298 * XXX focus-grabbing.
299 */
300 return GL_TRUE;
301# endif
302
303#elif TARGET_HOST_WIN32 || TARGET_HOST_WINCE
304
305 unsigned int displayModes = 0, mode = 0xffffffff;
306 GLboolean success = GL_FALSE;
307 /* HDC desktopDC; */
308 DEVMODE devMode;
309
310 /*
311 * Enumerate the available display modes
312 * Try to get a complete match
313 */
314 while( EnumDisplaySettings( NULL, displayModes, &devMode ) )
315 {
316 /*
317 * Does the enumerated display mode match the user's preferences?
318 */
319 if( oghCheckDisplayMode(
320 devMode.dmPelsWidth, devMode.dmPelsHeight,
321 devMode.dmBitsPerPel, devMode.dmDisplayFrequency
322 ) )
323 {
324 mode = displayModes;
325 break;
326 }
327 displayModes++;
328 }
329
330 if( mode == 0xffffffff )
331 {
332 /* then try without Display Frequency */
333 displayModes = 0;
334
335 /*
336 * Enumerate the available display modes
337 */
338 while( EnumDisplaySettings( NULL, displayModes, &devMode ) )
339 {
340 /* then try without Display Frequency */
341 if( oghCheckDisplayMode( devMode.dmPelsWidth,
342 devMode.dmPelsHeight,
343 devMode.dmBitsPerPel,
344 ogState.GameModeRefresh ) )
345 {
346 mode = displayModes;
347 break;
348 }
349 displayModes++;
350 }
351 }
352
353 /*
354 * Did we find a matching display mode?
355 */
356 if( mode != 0xffffffff )
357 {
358 int retVal = DISP_CHANGE_SUCCESSFUL;
359
360 /*
361 * Mark the values we want to modify in the display change call
362 */
363 devMode.dmFields |=
364 DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
365
366 retVal = ChangeDisplaySettings( &devMode, haveToTest ? CDS_TEST : 0 );
367
368 /*
369 * I don't know if it's really needed, but looks nice:
370 */
371 success = (retVal == DISP_CHANGE_SUCCESSFUL) ||
372 (retVal == DISP_CHANGE_NOTUPDATED);
373
374 if( !haveToTest && success )
375 {
376 ogState.GameModeSize.X = devMode.dmPelsWidth;
377 ogState.GameModeSize.Y = devMode.dmPelsHeight;
378 ogState.GameModeDepth = devMode.dmBitsPerPel;
379 ogState.GameModeRefresh = devMode.dmDisplayFrequency;
380 }
381 }
382
383 return success;
384
385#endif
386}
387
388
389/* -- INTERFACE FUNCTIONS -------------------------------------------------- */
390
391/*!
392 \fn
393 \brief Set the game mode display string
394 \ingroup gamemode
395 \param string A configuration parameter as a string.
396
397 Sets the gamemode status according to an undocumented string.
398
399 Glancing at old GLUT 3.7, the freeglut codebase that we
400 inherited does not implement more than a single
401 GLUT gamemode ``criteria''. It may not even do that
402 much correctly.
403
404 In principle, this code lets you set the video dimensions,
405 rendering depth, and video refresh rate. In practice,
406 the combination that you request may be unsupportable
407 and the target host may even refuse to honor any such
408 changes.
409
410 If you use this function but do not set all options,
411 the following defaults may be substituted for some
412 values:
413
414 - \a width 640
415 - \a height 480
416 - \a depth 16
417 - \a refresh 72
418
419 \todo Documentation
420 \see glutGameModeString(), glutEnterGameMode(), glutLeaveGameMode(),
421 glutGameModeGet()
422*/
423void OGAPIENTRY glutGameModeString( const char *string )
424{
425 int width = 640, height = 480, depth = 16, refresh = 72;
426
427 /*
428 * This one seems a bit easier than glutInitDisplayString. The bad thing
429 * about it that I was unable to find the game mode string definition, so
430 * that I assumed it is: "[width]x[height]:[depth]@[refresh rate]", which
431 * appears in all GLUT game mode programs I have seen to date.
432 */
433 if( sscanf( string, "%ix%i:%i@%i", &width, &height, &depth, &refresh ) !=
434 4 )
435 if( sscanf( string, "%ix%i:%i", &width, &height, &depth ) != 3 )
436 if( sscanf( string, "%ix%i@%i", &width, &height, &refresh ) != 3 )
437 if( sscanf( string, "%ix%i", &width, &height ) != 2 )
438 if( sscanf( string, ":%i@%i", &depth, &refresh ) != 2 )
439 if( sscanf( string, ":%i", &depth ) != 1 )
440 if( sscanf( string, "@%i", &refresh ) != 1 )
441 ogWarning(
442 "Unable to parse game mode string `%s'.",
443 string
444 );
445
446 /*
447 * Hopefully it worked, and if not, we still have the default values
448 */
449 ogState.GameModeSize.X = width;
450 ogState.GameModeSize.Y = height;
451 ogState.GameModeDepth = depth;
452 ogState.GameModeRefresh = refresh;
453}
454
455/*!
456 \fn
457 \brief Enter game mode
458 \ingroup gamemode
459
460 Any combination of the following may apply:
461
462 - Resolution change, possibly to the requested
463 resolution, possibly to a "nearest match".
464 - Refresh frequency change.
465 - A window with a drawable area equal to the requested
466 screen resolution will be opened.
467 - The mouse may be restricted to operate within
468 your window.
469
470 \note Varies in behavior; X users can disable the resolution
471 change by an OpenGLUT compile-time option.
472 \todo Documentation
473 \todo OpenGLUT may be unable to restore the original settings
474 (this has been observed on WIN32).
475
476 \see glutGameModeString(), glutEnterGameMode(), glutLeaveGameMode(),
477 glutGameModeGet()
478*/
479int OGAPIENTRY glutEnterGameMode( void )
480{
481 if( ogStructure.GameMode )
482 ogAddToWindowDestroyList( ogStructure.GameMode );
483 else
484 oghRememberState( );
485
486 if( ! oghChangeDisplayMode( GL_FALSE ) )
487 {
488 ogWarning( "Failed to change screen settings." );
489 return FALSE;
490 }
491
492 ogStructure.GameMode = ogCreateWindow(
493 NULL, "OpenGLUT", 0, 0,
494 ogState.GameModeSize.X, ogState.GameModeSize.Y, OG_CW_GAMEMODE
495 );
496
497 ogStructure.GameMode->State.IsGameMode = GL_TRUE;
498 ogStructure.GameMode->State.NeedToResize = GL_TRUE;
499 ogStructure.GameMode->State.Width = ogState.GameModeSize.X;
500 ogStructure.GameMode->State.Height = ogState.GameModeSize.Y;
501
502#if TARGET_HOST_UNIX_X11
503
504 /* Move the window up to the topleft corner */
505 XMoveWindow( ogDisplay.Display, ogStructure.Window->Window.Handle, 0, 0 );
506
507 /*
508 * Sync needed to avoid a real race, the Xserver must have really created
509 * the window before we can grab the pointer into it:
510 */
511 XSync( ogDisplay.Display, False );
512
513 /* Move the Pointer to the middle of the fullscreen window */
514 XWarpPointer(
515 ogDisplay.Display,
516 None,
517 ogDisplay.RootWindow,
518 0, 0, 0, 0,
519 ogState.GameModeSize.X/2, ogState.GameModeSize.Y/2
520 );
521
522 /*
523 * Grab the pointer to confine it into the window after the calls to
524 * XWrapPointer() which ensure that the pointer really enters the window.
525 *
526 * We also need to wait here until XGrabPointer() returns GrabSuccess,
527 * otherwise the new window is not viewable yet and if the next function
528 * (XSetInputFocus) is called with a not yet viewable window, it will exit
529 * the application which we have to aviod, so wait until it's viewable:
530 */
531 while( GrabSuccess != XGrabPointer(
532 ogDisplay.Display, ogStructure.GameMode->Window.Handle,
533 TRUE,
534 ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
535 | PointerMotionMask,
536 GrabModeAsync, GrabModeAsync,
537 ogStructure.GameMode->Window.Handle, None, CurrentTime
538 ) )
539 usleep( 100 );
540
541 /*
542 * Change input focus to the new window. This will exit the application
543 * if the new window is not viewable yet, see the XGrabPointer loop above.
544 */
545 XSetInputFocus(
546 ogDisplay.Display,
547 ogStructure.GameMode->Window.Handle,
548 RevertToNone,
549 CurrentTime
550 );
551
552# ifdef X_XF86VidModeSetViewPort
553
554 if( ogDisplay.DisplayModeValid )
555 {
556 int x, y;
557 Window child;
558
559 /* Change to viewport to the window topleft edge: */
560 XF86VidModeSetViewPort( ogDisplay.Display, ogDisplay.Screen, 0, 0 );
561
562 /*
563 * Final window repositioning: It could be avoided using an undecorated
564 * window using override_redirect, but this * would possily require
565 * more changes and investigation.
566 */
567
568 /* Get the current postion of the drawable area on screen */
569 XTranslateCoordinates(
570 ogDisplay.Display,
571 ogStructure.Window->Window.Handle,
572 ogDisplay.RootWindow,
573 0, 0, &x, &y,
574 &child
575 );
576
577 /* Move the decorataions out of the topleft corner of the display */
578 XMoveWindow( ogDisplay.Display, ogStructure.Window->Window.Handle,
579 -x, -y);
580 }
581
582#endif
583
584 /* Grab the keyboard, too */
585 XGrabKeyboard(
586 ogDisplay.Display,
587 ogStructure.GameMode->Window.Handle,
588 FALSE,
589 GrabModeAsync, GrabModeAsync,
590 CurrentTime
591 );
592
593#endif
594
595 return TRUE;
596}
597
598/*!
599 \fn
600 \brief Leave game mode, returning to normal desktop mode
601 \ingroup gamemode
602
603 \todo Documentation
604 \see glutGameModeString(), glutEnterGameMode(), glutLeaveGameMode(),
605 glutGameModeGet()
606*/
607
608void OGAPIENTRY glutLeaveGameMode( void )
609{
610 if( ogStructure.GameMode )
611 {
612 ogStructure.GameMode->State.IsGameMode = GL_FALSE;
613 ogAddToWindowDestroyList( ogStructure.GameMode );
614 ogStructure.GameMode = NULL;
615#if TARGET_HOST_UNIX_X11
616 XUngrabPointer( ogDisplay.Display, CurrentTime );
617 XUngrabKeyboard( ogDisplay.Display, CurrentTime );
618#endif
619 oghRestoreState();
620 }
621}
622
623/*!
624 \fn
625 \brief Return the value of a game mode parameter
626 \ingroup gamemode
627 \param pname The parameter value to be returned
628
629 \a pname is one of:
630
631 - \a GLUT_GAME_MODE_ACTIVE \n
632 Return non-zero if we are presently in gamemode.
633
634 - \a GLUT_GAME_MODE_POSSIBLE \n
635 Return whether the requested gamemode settings are viable.
636 (May also actually change the mode?) Does not necessarily
637 tell you whether entering gamemode will have any effect.
638
639 - \a GLUT_GAME_MODE_WIDTH \n
640 Return the game mode width. (in pixels)
641
642 - \a GLUT_GAME_MODE_HEIGHT \n
643 Return the game mode height. (in pixels)
644
645 - \a GLUT_GAME_MODE_PIXEL_DEPTH \n
646 Return the game mode pixel depth. (in bits)
647
648 - \a GLUT_GAME_MODE_REFRESH_RATE \n
649 Return the game mode vertical refresh frequency. (in Hz)
650
651 - \a GLUT_GAME_MODE_DISPLAY_CHANGED \n
652 Return non-zero if we are presently in gamemode.
653 (Same as \a GLUT_GAME_MODE_ACTIVE.)
654
655 If \a pname is unknown, a warning is printed and a value of
656 -1 is returned.
657
658 \todo Documentation
659 \todo OpenGLUT intrepretation of GLUT_GAME_MODE_DISPLAY_CHANGED
660 \see glutGameModeString(), glutEnterGameMode(), glutLeaveGameMode(),
661 glutGameModeGet()
662*/
663
664int OGAPIENTRY glutGameModeGet( GLenum pname )
665{
666 int ret = -1;
667
668 switch( pname )
669 {
670 case GLUT_GAME_MODE_ACTIVE:
671 ret = !!ogStructure.GameMode;
672 break;
673
674 case GLUT_GAME_MODE_POSSIBLE:
675 ret = oghChangeDisplayMode( GL_TRUE );
676 break;
677
678 case GLUT_GAME_MODE_WIDTH:
679 ret = ogState.GameModeSize.X;
680 break;
681
682 case GLUT_GAME_MODE_HEIGHT:
683 ret = ogState.GameModeSize.Y;
684 break;
685
686 case GLUT_GAME_MODE_PIXEL_DEPTH:
687 ret = ogState.GameModeDepth;
688 break;
689
690 case GLUT_GAME_MODE_REFRESH_RATE:
691 ret = ogState.GameModeRefresh;
692 break;
693
694 case GLUT_GAME_MODE_DISPLAY_CHANGED:
695 /* This is true if the game mode has been activated successfully. */
696 ret = !!ogStructure.GameMode;
697 break;
698
699 default:
700 ogWarning( "Unknown gamemode get: %d", pname );
701 break;
702 }
703
704 return ret;
705}