/*************************************************************************** file : fg_gm.cpp created : Sat Mar 8 14:40:50 CET 2003 copyright : (C) 2003 by Eric Espié email : eric.espie@torcs.org version : $Id: fg_gm.cpp,v 1.5 2004/11/15 20:21:28 torcs Exp $ ***************************************************************************/ /* * The freeglut library private include file. * * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved. * Written by Pawel W. Olszta, * Creation date: Thu Dec 2 1999 * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** @file Missing X11 gamemode in glut, taken from freeglut @version $Id: fg_gm.cpp,v 1.5 2004/11/15 20:21:28 torcs Exp $ */ /* 2004/10/03 Bernhard Wymann: Added Randr support, bugfixes. */ #include #include #include #include #include #include #include #include "fg_gm.h" #ifndef WIN32 #include #include #include #include #ifdef USE_RANDR_EXT #include #else // USE_RANDR_EXT #include #endif // USE_RANDR_EXT static int fgInitDone = 0; typedef struct tagSFG_Display SFG_Display; struct tagSFG_Display { Display* display; // The display we are being run in. int Screen; // The screen we are about to use. Window RootWindow; //The screen's root window. int ScreenWidth; // The screen's width in pixels. int ScreenHeight; // The screen's height in pixels. int error_base; // Base error number of the extension. int event_base; // Base event number of the extension. #ifdef USE_RANDR_EXT Rotation rotation; // The rotation mode. short rate; // Display refresh rate. #else //USE_RANDR_EXT XF86VidModeModeLine DisplayMode; // Current screen's display settings. int DisplayModeClock; // The display mode's refresh rate. #endif //USE_RANDR_EXT }; /* * A helper structure holding two ints and a boolean */ typedef struct tagSFG_XYUse SFG_XYUse; struct tagSFG_XYUse { int X, Y; /* The two integers... */ }; /* * This structure holds different freeglut settings */ typedef struct tagSFG_State SFG_State; struct tagSFG_State { SFG_XYUse GameModeSize; // The game mode screen's dimensions. int GameModeDepth; // The pixel depth for game mode. int GameModeRefresh; // The refresh rate for game mode. }; /* * A structure pointed by g_pDisplay holds all information * regarding the display, screen, root window etc. */ static SFG_Display fgDisplay; /* * The settings for the current freeglut session */ static SFG_State fgState; /* * A call to this function should initialize all the display stuff... */ static void fgInitialize(void) { char* displayName; displayName = getenv("DISPLAY"); if (!displayName) { displayName = ":0.0"; } // Have the display created. fgDisplay.display = XOpenDisplay(displayName); if(fgDisplay.display == NULL) { // Failed to open a display. That's no good. GfOut( "failed to open display '%s'", XDisplayName( displayName ) ); } // Grab the default screen for the display we have just opened. fgDisplay.Screen = DefaultScreen(fgDisplay.display); // The same applying to the root window fgDisplay.RootWindow = RootWindow(fgDisplay.display, fgDisplay.Screen); // Grab the logical screen's geometry. fgDisplay.ScreenWidth = DisplayWidth(fgDisplay.display, fgDisplay.Screen); fgDisplay.ScreenHeight = DisplayHeight(fgDisplay.display, fgDisplay.Screen); #ifdef USE_RANDR_EXT int major, minor; XRRQueryVersion(fgDisplay.display, &major, &minor); printf("Randr version: %d.%d\n", major, minor); XRRQueryExtension(fgDisplay.display, &fgDisplay.event_base, &fgDisplay.error_base); #else //USE_RANDR_EXT int major, minor; XF86VidModeQueryVersion(fgDisplay.display, &major, &minor); printf("Xxf86vm version: %d.%d\n", major, minor); XF86VidModeQueryExtension(fgDisplay.display, &fgDisplay.event_base, &fgDisplay.error_base); #endif //USE_RANDR_EXT } /* * Remembers the current visual settings, so that * we can change them and restore later... */ static void fghRememberState( void ) { // This highly depends on the XFree86 extensions, not approved as X Consortium standards. if (!fgInitDone) { fgInitialize(); fgInitDone = 1; // Query the current display settings. #ifdef USE_RANDR_EXT XRRScreenConfiguration *screenconfig = XRRGetScreenInfo(fgDisplay.display, fgDisplay.RootWindow); if (screenconfig != NULL) { SizeID size = XRRConfigCurrentConfiguration (screenconfig, &fgDisplay.rotation); int nsize; XRRScreenSize *sizes = XRRConfigSizes(screenconfig, &nsize); fgDisplay.ScreenWidth = sizes[size].width; fgDisplay.ScreenHeight = sizes[size].height; fgDisplay.rate = XRRConfigCurrentRate(screenconfig); //printf("x: %d, y: %d, rate: %d\n", fgDisplay.ScreenWidth, fgDisplay.ScreenHeight, fgDisplay.rate); XRRFreeScreenConfigInfo(screenconfig); } else { // Hmm, what to do here, exit? } #else //USE_RANDR_EXT XF86VidModeGetModeLine( fgDisplay.display, fgDisplay.Screen, &fgDisplay.DisplayModeClock, &fgDisplay.DisplayMode ); #endif //USE_RANDR_EXT } } /* * Restores the previously remembered visual settings */ static void fghRestoreState( void ) { #ifdef USE_RANDR_EXT XRRScreenConfiguration *screenconfig = XRRGetScreenInfo(fgDisplay.display, fgDisplay.RootWindow); if (screenconfig != NULL) { int nsize; XRRScreenSize *sizes = XRRConfigSizes(screenconfig, &nsize); // Search for the display resolution ID for the old video mode. SizeID size; for (size = 0; size < nsize; size++) { if (sizes[size].width == fgDisplay.ScreenWidth && sizes[size].height == fgDisplay.ScreenHeight) break; } XSelectInput(fgDisplay.display, fgDisplay.RootWindow, StructureNotifyMask); XRRSelectInput(fgDisplay.display, fgDisplay.RootWindow, RRScreenChangeNotifyMask); Status status = RRSetConfigFailed; status = XRRSetScreenConfigAndRate( fgDisplay.display, screenconfig, fgDisplay.RootWindow, size, fgDisplay.rotation, fgDisplay.rate, CurrentTime ); if (status == RRSetConfigSuccess) { XEvent ev; do { XNextEvent(fgDisplay.display, &ev); XRRUpdateConfiguration(&ev); } while ((ev.type != ConfigureNotify) && ((ev.type - fgDisplay.event_base) != RRScreenChangeNotify)); XSync(fgDisplay.display, True); } XRRFreeScreenConfigInfo(screenconfig); } else { // Hmm, what to do here, exit? } #else //USE_RANDR_EXT /* * This highly depends on the XFree86 extensions, not approved as X Consortium standards */ XF86VidModeModeInfo** displayModes; int i, displayModesCount; /* * Query for all the display available... */ XF86VidModeGetAllModeLines( fgDisplay.display, fgDisplay.Screen, &displayModesCount, &displayModes ); /* * Check every of the modes looking for one that matches our demands */ for( i=0; ihdisplay == fgDisplay.DisplayMode.hdisplay && displayModes[ i ]->vdisplay == fgDisplay.DisplayMode.vdisplay && displayModes[ i ]->dotclock == (unsigned int)fgDisplay.DisplayModeClock ) { /* * OKi, this is the display mode we have been looking for... */ XSync(fgDisplay.display, True); XF86VidModeSwitchToMode(fgDisplay.display, fgDisplay.Screen, displayModes[ i ]); // FIXME: usleep: Hack to avoid screen corruption (does not work always). Do you know a clean solution? usleep(100000); XSync(fgDisplay.display, False); XF86VidModeSetViewPort(fgDisplay.display, fgDisplay.Screen, 0, 0); // FIXME: usleep: Hack to avoid screen corruption (does not work always). Do you know a clean solution? usleep(100000); XSync(fgDisplay.display, False); XWarpPointer( fgDisplay.display, None, fgDisplay.RootWindow, 0, 0, 0, 0, fgDisplay.ScreenWidth/2, fgDisplay.ScreenHeight/2 ); XFree(displayModes); if (fgDisplay.DisplayMode.privsize > 0) { // Cannot compile with c++! --> private keyword. //XFree(fgDisplay.DisplayMode.private); } XFlush(fgDisplay.display); return; } } XFree(displayModes); XFlush(fgDisplay.display); #endif //USE_RANDR_EXT } #ifndef USE_RANDR_EXT /* * Checks the display mode settings against user's preferences */ static int fghCheckDisplayMode( int width, int height, int depth, int refresh ) { /* * The desired values should be stored in fgState structure... */ return( (width == fgState.GameModeSize.X) && (height == fgState.GameModeSize.Y) && (depth == fgState.GameModeDepth) && (refresh == fgState.GameModeRefresh) ); } #endif //USE_RANDR_EXT /* * Changes the current display mode to match user's settings */ static int fghChangeDisplayMode( int /* haveToTest */ ) { #ifdef USE_RANDR_EXT XRRScreenConfiguration *screenconfig = XRRGetScreenInfo(fgDisplay.display, fgDisplay.RootWindow); if (screenconfig != NULL) { int nsize; XRRScreenSize *sizes = XRRConfigSizes(screenconfig, &nsize); // Search for the requested display resolution. SizeID size; for (size = 0; size < nsize; size++) { if (sizes[size].width == fgState.GameModeSize.X && sizes[size].height == fgState.GameModeSize.Y) { break; } } if (size >= nsize) { // We did not find a matching resolution. XRRFreeScreenConfigInfo(screenconfig); return FALSE; } else { // Ok, resolution is ok, now check the refresh rate. int nrate; short *rates = XRRConfigRates(screenconfig, size, &nrate); int rate; for (rate = 0; rate < nrate; rate++) { if (rates[rate] == fgState.GameModeRefresh) { break; } } if (rate >= nrate) { // We did not find a matching refresh rate. XRRFreeScreenConfigInfo(screenconfig); return FALSE; } else { // We have everything, try to switch. XSelectInput(fgDisplay.display, fgDisplay.RootWindow, StructureNotifyMask); XRRSelectInput(fgDisplay.display, fgDisplay.RootWindow, RRScreenChangeNotifyMask); Status status = RRSetConfigFailed; status = XRRSetScreenConfigAndRate( fgDisplay.display, screenconfig, fgDisplay.RootWindow, size, fgDisplay.rotation, rates[rate], CurrentTime ); XRRFreeScreenConfigInfo(screenconfig); if (status == RRSetConfigSuccess) { XEvent ev; do { XNextEvent(fgDisplay.display, &ev); XRRUpdateConfiguration(&ev); } while ((ev.type != ConfigureNotify) && ((ev.type - fgDisplay.event_base) != RRScreenChangeNotify)); XSync(fgDisplay.display, True); return TRUE; } else { return FALSE; } } } } return FALSE; #else //USE_RANDR_EXT /* * This highly depends on the XFree86 extensions, not approved as X Consortium standards */ XF86VidModeModeInfo** displayModes; int i, displayModesCount; /* * Query for all the display available... */ XF86VidModeGetAllModeLines( fgDisplay.display, fgDisplay.Screen, &displayModesCount, &displayModes ); /* * Check every of the modes looking for one that matches our demands */ for( i=0; idotclock*1000/(displayModes[ i ]->htotal*displayModes[ i ]->vtotal); if( fghCheckDisplayMode( displayModes[ i ]->hdisplay, displayModes[ i ]->vdisplay, fgState.GameModeDepth, refresh ) ) { // Added failure code path, who can guarantee if that works always? // If i == 0, then the videomode we prefer is already set, so the short circuit // "||" does not evaluate the (in this case unnecessary) XF86VidModeSwitchToMode. //XFlush(fgDisplay.display); //XSync(fgDisplay.display, True); if ((i == 0) || (XF86VidModeSwitchToMode( fgDisplay.display, fgDisplay.Screen, displayModes[ i ]) > 0)) { XF86VidModeSetViewPort( fgDisplay.display, fgDisplay.Screen, (fgDisplay.ScreenWidth - fgState.GameModeSize.X) / 2, (fgDisplay.ScreenHeight - fgState.GameModeSize.Y) / 2 ); // Move the mouse pointer in the middle of the viewport. XWarpPointer( fgDisplay.display, None, fgDisplay.RootWindow, 0, 0, 0, 0, fgDisplay.ScreenWidth/2, fgDisplay.ScreenHeight/2 ); XFree(displayModes); XFlush(fgDisplay.display); return TRUE; } else { XFree(displayModes); XFlush(fgDisplay.display); return( FALSE ); } } } /* * Something must have went wrong */ XFree(displayModes); XFlush(fgDisplay.display); return( FALSE ); #endif //USE_RANDR_EXT } #endif /* WIN32 */ /* -- INTERFACE FUNCTIONS -------------------------------------------------- */ /* * Sets the game mode display string */ void fglutGameModeString( const char* string ) { #ifndef WIN32 int width = 640, height = 480, depth = 16, refresh = 72; /* * This one seems a bit easier than glutInitDisplayString. The bad thing * about it that I was unable to find the game mode string definition, so * that I assumed it is: "[width]x[height]:[depth]@[refresh rate]", which * appears in all GLUT game mode programs I have seen to date. */ if( sscanf( string, "%ix%i:%i@%i", &width, &height, &depth, &refresh ) != 4 ) if( sscanf( string, "%ix%i:%i", &width, &height, &depth ) != 3 ) if( sscanf( string, "%ix%i@%i", &width, &height, &refresh ) != 3 ) if( sscanf( string, "%ix%i", &width, &height ) != 2 ) if( sscanf( string, ":%i@%i", &depth, &refresh ) != 2 ) if( sscanf( string, ":%i", &depth ) != 1 ) if( sscanf( string, "@%i", &refresh ) != 1 ) GfOut( "unable to parse game mode string `%s'", string ); /* * Hopefully it worked, and if not, we still have the default values */ fgState.GameModeSize.X = width; fgState.GameModeSize.Y = height; fgState.GameModeDepth = depth; fgState.GameModeRefresh = refresh; #endif /* WIN32 */ } /* * Enters the game mode */ int fglutEnterGameMode( void ) { #ifndef WIN32 /* * Remember the current resolution, etc. */ fghRememberState(); /* * We are ready to change the current screen's resolution now */ if( fghChangeDisplayMode( FALSE ) == FALSE ) { GfOut( "failed to change screen settings" ); return( FALSE ); } /* * Return successfull */ return( TRUE ); #else /* WIN32 */ return( FALSE ); #endif /* WIN32 */ } /* * Leaves the game mode */ void fglutLeaveGameMode( void ) { #ifndef WIN32 /* * Then, have the desktop visual settings restored */ fghRestoreState(); #endif /* WIN32 */ }