Implementing John Tsiombikas' game mode patch per e-mail dated 3/15/11 8:04 PM
[freeglut] / src / freeglut_gamemode.c
index a039d7f..137f1ea 100644 (file)
 
 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
 
+static int xrandr_resize(int xsz, int ysz, int just_checking)
+{
+    int res = -1;
+
+#ifdef HAVE_X11_EXTENSIONS_XRANDR_H
+    int event_base, error_base;
+    Status st;
+    XRRScreenConfiguration *xrr_config;
+    XRRScreenSize *ssizes;
+    Rotation rot;
+    int i, ssizes_count, curr;
+    Time timestamp, cfg_timestamp;
+
+    /* must check at runtime for the availability of the extension */
+    if(!XRRQueryExtension(fgDisplay.Display, &event_base, &error_base)) {
+        return -1;
+    }
+
+    if(!(xrr_config = XRRGetScreenInfo(fgDisplay.Display, fgDisplay.RootWindow))) {
+        fgWarning("XRRGetScreenInfo failed");
+        return -1;
+    }
+    ssizes = XRRConfigSizes(xrr_config, &ssizes_count);
+    curr = XRRConfigCurrentConfiguration(xrr_config, &rot);
+    timestamp = XRRConfigTimes(xrr_config, &cfg_timestamp);
+
+    if(xsz == ssizes[curr].width && ysz == ssizes[curr].height) {
+        /* no need to switch, we're already in the requested mode */
+        res = 0;
+        goto done;
+    }
+
+    for(i=0; i<ssizes_count; i++) {
+        if(ssizes[i].width == xsz && ssizes[i].height == ysz) {
+            break;  /* found it */
+        }
+    }
+    if(i == ssizes_count)
+        goto done;
+
+    if(just_checking) {
+        res = 0;
+        goto done;
+    }
+
+    if((st = XRRSetScreenConfig(fgDisplay.Display, xrr_config, fgDisplay.RootWindow,
+                    i, rot, timestamp)) != 0) {
+        fgWarning("XRRSetScreenConfig failed");
+        goto done;
+    }
+    res = 0;
+
+done:
+    XRRFreeScreenConfigInfo(xrr_config);
+#endif
+    return res;
+}
+
+
 /*
  * Remembers the current visual settings, so that
  * we can change them and restore later...
 static void fghRememberState( void )
 {
 #if TARGET_HOST_POSIX_X11
+    int event_base, error_base;
+
+#   ifdef HAVE_X11_EXTENSIONS_XRANDR_H
+    if(XRRQueryExtension(fgDisplay.Display, &event_base, &error_base)) {
+        XRRScreenConfiguration *xrr_config;
+        XRRScreenSize *ssizes;
+        Rotation rot;
+        int ssize_count, curr;
+
+        if((xrr_config = XRRGetScreenInfo(fgDisplay.Display, fgDisplay.RootWindow))) {
+            ssizes = XRRConfigSizes(xrr_config, &ssize_count);
+            curr = XRRConfigCurrentConfiguration(xrr_config, &rot);
+
+            fgDisplay.prev_xsz = ssizes[curr].width;
+            fgDisplay.prev_ysz = ssizes[curr].height;
+            fgDisplay.prev_size_valid = 1;
+            XRRFreeScreenConfigInfo(xrr_config);
+            return;
+        }
+    }
+#   endif
 
     /*
      * This highly depends on the XFree86 extensions,
      * not approved as X Consortium standards
      */
-#   ifdef X_XF86VidModeGetModeLine
-
+#   ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
+    if(!XF86VidModeQueryExtension(fgDisplay.Display, &event_base, &error_base)) {
+        return;
+    }
 
     /*
      * Remember the current ViewPort location of the screen to be able to
@@ -93,12 +175,6 @@ static void fghRememberState( void )
 
     if( !fgDisplay.DisplayModeValid )
             fgWarning( "XF86VidModeGetModeLine failed" );
-
-#   else
-    /*
-     * XXX warning fghRememberState: missing XFree86 video mode extensions,
-     * XXX game mode will not change screen resolution when activated
-     */
 #   endif
 
 #elif TARGET_HOST_MS_WINDOWS
@@ -110,7 +186,7 @@ static void fghRememberState( void )
 /* hack to get around my stupid cross-gcc headers */
 #define FREEGLUT_ENUM_CURRENT_SETTINGS -1
 
-    EnumDisplaySettings( NULL, FREEGLUT_ENUM_CURRENT_SETTINGS,
+    EnumDisplaySettings( fgDisplay.DisplayName, FREEGLUT_ENUM_CURRENT_SETTINGS,
                          &fgDisplay.DisplayMode );
 
     /* Make sure we will be restoring all settings needed */
@@ -127,7 +203,17 @@ static void fghRestoreState( void )
 {
 #if TARGET_HOST_POSIX_X11
 
-#   ifdef X_XF86VidModeGetAllModeLines
+#   ifdef HAVE_X11_EXTENSIONS_XRANDR_H
+    if(fgDisplay.prev_size_valid) {
+        if(xrandr_resize(fgDisplay.prev_xsz, fgDisplay.prev_ysz, 0) != -1) {
+            fgDisplay.prev_size_valid = 0;
+            return;
+        }
+    }
+#   endif
+
+
+#   ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
     /* Restore the remembered pointer position: */
     XWarpPointer(
         fgDisplay.Display, None, fgDisplay.RootWindow, 0, 0, 0, 0,
@@ -179,7 +265,7 @@ static void fghRestoreState( void )
                          fgDisplay.Screen,
                          fgDisplay.DisplayViewPortX,
                          fgDisplay.DisplayViewPortY ) )
-                    fgWarning( "XF86VidModeSetViewPort failed" );
+                    fgWarning( "HAVE_X11_EXTENSIONS_XF86VMODE_H failed" );
 
 
                 /*
@@ -195,23 +281,18 @@ static void fghRestoreState( void )
         XFree( displayModes );
     }
 
-#   else
-    /*
-     * XXX warning fghRestoreState: missing XFree86 video mode extensions,
-     * XXX game mode will not change screen resolution when activated
-     */
 #   endif
 
 #elif TARGET_HOST_MS_WINDOWS
 
-    /* Restore the previously rememebered desktop display settings */
-    ChangeDisplaySettings( &fgDisplay.DisplayMode, 0 );
+    /* Restore the previously remembered desktop display settings */
+    ChangeDisplaySettingsEx( fgDisplay.DisplayName,&fgDisplay.DisplayMode, 0,0,0 );
 
 #endif
 }
 
 #if TARGET_HOST_POSIX_X11
-#ifdef X_XF86VidModeGetAllModeLines
+#ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
 
 /*
  * Checks a single display mode settings against user's preferences.
@@ -242,6 +323,15 @@ static int fghCheckDisplayModes( GLboolean exactMatch, int displayModesCount, XF
                                  displayModes[ i ]->vdisplay,
                                  fgState.GameModeDepth,
                                  ( exactMatch ? refresh : fgState.GameModeRefresh ) ) ) {
+            if (!exactMatch)
+            {
+                /* Update the chosen refresh rate, otherwise a
+                 * glutGameModeGet(GLUT_GAME_MODE_REFRESH_RATE) would not
+                 * return the right values
+                 */
+                fgState.GameModeRefresh = refresh;
+            }
+
             return i;
         }
     }
@@ -259,11 +349,19 @@ static GLboolean fghChangeDisplayMode( GLboolean haveToTest )
     GLboolean success = GL_FALSE;
 #if TARGET_HOST_POSIX_X11
 
+    /* first try to use XRandR, then fallback to XF86VidMode */
+#   ifdef HAVE_X11_EXTENSIONS_XRANDR_H
+    if(xrandr_resize(fgState.GameModeSize.X, fgState.GameModeSize.Y, haveToTest) != -1) {
+        return GL_TRUE;
+    }
+#   endif
+
+
     /*
      * This highly depends on the XFree86 extensions,
      * not approved as X Consortium standards
      */
-#   ifdef X_XF86VidModeGetAllModeLines
+#   ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
 
     /*
      * This is also used by applcations which check modes by calling
@@ -323,22 +421,37 @@ static GLboolean fghChangeDisplayMode( GLboolean haveToTest )
 
     success = GL_FALSE;
 
-    EnumDisplaySettings( NULL, -1, &devMode ); 
-    devMode.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
+    EnumDisplaySettings( fgDisplay.DisplayName, -1, &devMode ); 
+    devMode.dmFields = 0;
 
-    devMode.dmPelsWidth  = fgState.GameModeSize.X;
-    devMode.dmPelsHeight = fgState.GameModeSize.Y;
-    devMode.dmBitsPerPel = fgState.GameModeDepth;
-    devMode.dmDisplayFrequency = fgState.GameModeRefresh;
-    devMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
+    if (fgState.GameModeSize.X!=-1)
+    {
+        devMode.dmPelsWidth  = fgState.GameModeSize.X;
+        devMode.dmFields |= DM_PELSWIDTH;
+    }
+    if (fgState.GameModeSize.Y!=-1)
+    {
+        devMode.dmPelsHeight  = fgState.GameModeSize.Y;
+        devMode.dmFields |= DM_PELSHEIGHT;
+    }
+    if (fgState.GameModeDepth!=-1)
+    {
+        devMode.dmBitsPerPel  = fgState.GameModeDepth;
+        devMode.dmFields |= DM_BITSPERPEL;
+    }
+    if (fgState.GameModeRefresh!=-1)
+    {
+        devMode.dmDisplayFrequency  = fgState.GameModeRefresh;
+        devMode.dmFields |= DM_DISPLAYFREQUENCY;
+    }
 
-    switch ( ChangeDisplaySettingsEx(NULL, &devMode, NULL, haveToTest ? CDS_TEST : CDS_FULLSCREEN , NULL) )
+    switch ( ChangeDisplaySettingsEx(fgDisplay.DisplayName, &devMode, NULL, haveToTest ? CDS_TEST : CDS_FULLSCREEN , NULL) )
     {
     case DISP_CHANGE_SUCCESSFUL:
         success = GL_TRUE;
 
         /* update vars in case if windows switched to proper mode */
-        EnumDisplaySettings( NULL, FREEGLUT_ENUM_CURRENT_SETTINGS, &devMode );
+        EnumDisplaySettings( fgDisplay.DisplayName, FREEGLUT_ENUM_CURRENT_SETTINGS, &devMode );
         fgState.GameModeSize.X  = devMode.dmPelsWidth;        
         fgState.GameModeSize.Y  = devMode.dmPelsHeight;
         fgState.GameModeDepth   = devMode.dmBitsPerPel;
@@ -380,7 +493,7 @@ static GLboolean fghChangeDisplayMode( GLboolean haveToTest )
  */
 void FGAPIENTRY glutGameModeString( const char* string )
 {
-    int width = 640, height = 480, depth = 16, refresh = 72;
+    int width = -1, height = -1, depth = -1, refresh = -1;
 
     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutGameModeString" );
 
@@ -404,12 +517,14 @@ void FGAPIENTRY glutGameModeString( const char* 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;
+    if ( width   > 0 ) fgState.GameModeSize.X  = width;
+    if ( height  > 0 ) fgState.GameModeSize.Y  = height;
+    if ( depth   > 0 ) fgState.GameModeDepth   = depth;
+    if ( refresh > 0 ) fgState.GameModeRefresh = refresh;
 }
 
+
+
 /*
  * Enters the game mode
  */
@@ -429,8 +544,9 @@ int FGAPIENTRY glutEnterGameMode( void )
     }
 
     fgStructure.GameModeWindow = fgCreateWindow(
-        NULL, "FREEGLUT", 0, 0,
-        fgState.GameModeSize.X, fgState.GameModeSize.Y, GL_TRUE, GL_FALSE
+        NULL, "FREEGLUT", GL_TRUE, 0, 0,
+        GL_TRUE, fgState.GameModeSize.X, fgState.GameModeSize.Y,
+        GL_TRUE, GL_FALSE
     );
 
     fgStructure.GameModeWindow->State.Width  = fgState.GameModeSize.X;
@@ -483,7 +599,7 @@ int FGAPIENTRY glutEnterGameMode( void )
         fgState.GameModeSize.X/2, fgState.GameModeSize.Y/2
     );
 
-#   ifdef X_XF86VidModeSetViewPort
+#   ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
 
     if( fgDisplay.DisplayModeValid )
     {