Misiek\'s changes to fix Game Mode--getting display settings under Windows
[freeglut] / src / freeglut_gamemode.c
index 5f93431..54c924d 100644 (file)
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
 #include <GL/freeglut.h>
 #include "freeglut_internal.h"
 
@@ -48,7 +44,7 @@
  * Remembers the current visual settings, so that
  * we can change them and restore later...
  */
-void fghRememberState( void )
+static void fghRememberState( void )
 {
 #if TARGET_HOST_UNIX_X11
 
@@ -63,12 +59,12 @@ void fghRememberState( void )
      * Remember the current ViewPort location of the screen to be able to
      * restore the ViewPort on LeaveGameMode():
      */
-    XF86VidModeGetViewPort(
-        fgDisplay.Display,
-        fgDisplay.Screen,
-        &fgDisplay.DisplayViewPortX,
-        &fgDisplay.DisplayViewPortY
-    );
+    if( !XF86VidModeGetViewPort(
+             fgDisplay.Display,
+             fgDisplay.Screen,
+             &fgDisplay.DisplayViewPortX,
+             &fgDisplay.DisplayViewPortY ) )
+        fgWarning( "XF86VidModeGetViewPort failed" );
 
     /*
      * Remember the current pointer location before going fullscreen
@@ -96,7 +92,7 @@ void fghRememberState( void )
     );
 
     if( !fgDisplay.DisplayModeValid )
-            fgWarning( "Runtime use of XF86VidModeGetModeLine failed.\n" );
+            fgWarning( "XF86VidModeGetModeLine failed" );
 
 #   else
     /*
@@ -127,7 +123,7 @@ void fghRememberState( void )
 /*
  * Restores the previously remembered visual settings
  */
-void fghRestoreState( void )
+static void fghRestoreState( void )
 {
 #if TARGET_HOST_UNIX_X11
 
@@ -148,12 +144,16 @@ void fghRestoreState( void )
         XF86VidModeModeInfo** displayModes;
         int i, displayModesCount;
 
-        XF86VidModeGetAllModeLines(
-            fgDisplay.Display,
-            fgDisplay.Screen,
-            &displayModesCount,
-            &displayModes
-        );
+        if( !XF86VidModeGetAllModeLines(
+                 fgDisplay.Display,
+                 fgDisplay.Screen,
+                 &displayModesCount,
+                 &displayModes ) )
+        {
+            fgWarning( "XF86VidModeGetAllModeLines failed" );
+            return;
+        }
+
 
         /*
          * Check every of the modes looking for one that matches our demands.
@@ -165,17 +165,22 @@ void fghRestoreState( void )
                displayModes[ i ]->vdisplay == fgDisplay.DisplayMode.vdisplay &&
                displayModes[ i ]->dotclock == fgDisplay.DisplayModeClock )
             {
-                XF86VidModeSwitchToMode(
-                    fgDisplay.Display,
-                    fgDisplay.Screen,
-                    displayModes[ i ]
-                );
-                XF86VidModeSetViewPort(
-                     fgDisplay.Display,
-                     fgDisplay.Screen,
-                     fgDisplay.DisplayViewPortX,
-                     fgDisplay.DisplayViewPortY
-                );
+                if( !XF86VidModeSwitchToMode(
+                         fgDisplay.Display,
+                         fgDisplay.Screen,
+                         displayModes[ i ] ) )
+                {
+                    fgWarning( "XF86VidModeSwitchToMode failed" );
+                    break;
+                }
+
+                if( !XF86VidModeSetViewPort(
+                         fgDisplay.Display,
+                         fgDisplay.Screen,
+                         fgDisplay.DisplayViewPortX,
+                         fgDisplay.DisplayViewPortY ) )
+                    fgWarning( "XF86VidModeSetViewPort failed" );
+
 
                 /*
                  * For the case this would be the last X11 call the application
@@ -184,9 +189,10 @@ void fghRestoreState( void )
                  */
                 XFlush( fgDisplay.Display );
 
-                return;
+                break;
             }
         }
+        XFree( displayModes );
     }
 
 #   else
@@ -207,7 +213,7 @@ void fghRestoreState( void )
 /*
  * Checks the display mode settings against user's preferences
  */
-GLboolean fghCheckDisplayMode( int width, int height, int depth, int refresh )
+static GLboolean fghCheckDisplayMode( int width, int height, int depth, int refresh )
 {
     /* The desired values should be stored in fgState structure... */
     return ( width == fgState.GameModeSize.X ) &&
@@ -219,8 +225,9 @@ GLboolean fghCheckDisplayMode( int width, int height, int depth, int refresh )
 /*
  * Changes the current display mode to match user's settings
  */
-GLboolean fghChangeDisplayMode( GLboolean haveToTest )
+static GLboolean fghChangeDisplayMode( GLboolean haveToTest )
 {
+    GLboolean success = GL_FALSE;
 #if TARGET_HOST_UNIX_X11
 
     /*
@@ -236,118 +243,117 @@ GLboolean fghChangeDisplayMode( GLboolean haveToTest )
     if( haveToTest || fgDisplay.DisplayModeValid )
     {
         XF86VidModeModeInfo** displayModes;
-        int i, displayModesCount;
+        int i, ignoreRefreshRate, displayModesCount;
 
-        XF86VidModeGetAllModeLines(
-            fgDisplay.Display,
-            fgDisplay.Screen,
-            &displayModesCount,
-            &displayModes
-        );
+        if( !XF86VidModeGetAllModeLines(
+                 fgDisplay.Display,
+                 fgDisplay.Screen,
+                 &displayModesCount,
+                 &displayModes ) )
+        {
+            fgWarning( "XF86VidModeGetAllModeLines failed" );
+            return success;
+        }
 
-        /* Check every of the modes looking for one that matches our demands */
-        for( i = 0; i < displayModesCount; i++ )
+
+        /*
+         * Check every of the modes looking for one that matches our demands,
+         * ignoring the refresh rate if no exact match could be found.
+         */
+        for( ignoreRefreshRate = 0;
+             !success && ( ignoreRefreshRate <= 1 );
+             ignoreRefreshRate++)
         {
-            if( fghCheckDisplayMode( displayModes[ i ]->hdisplay,
-                                     displayModes[ i ]->vdisplay,
-                                     fgState.GameModeDepth,
-                                     fgState.GameModeRefresh ) )
+            for( i = 0;
+                 !success && ( i < displayModesCount );
+                 i++ )
             {
-                if( haveToTest )
-                    return GL_TRUE;
-                /* OKi, this is the display mode we have been looking for... */
-                XF86VidModeSwitchToMode(
-                    fgDisplay.Display,
-                    fgDisplay.Screen,
-                    displayModes[ i ]
-                );
-                return GL_TRUE;
+                /* Compute the displays refresh rate, dotclock comes in kHz. */
+                int refresh = ( displayModes[ i ]->dotclock * 1000 ) /
+                              ( displayModes[ i ]->htotal * displayModes[ i ]->vtotal );
+
+                success = fghCheckDisplayMode( displayModes[ i ]->hdisplay,
+                                               displayModes[ i ]->vdisplay,
+                                               fgState.GameModeDepth,
+                                               ( ignoreRefreshRate ? fgState.GameModeRefresh : refresh ) );
             }
         }
-    }
 
-    /* Something must have gone wrong */
-    return GL_FALSE;
+        if( !haveToTest && success ) {
+            if( !XF86VidModeSwitchToMode(
+                     fgDisplay.Display,
+                     fgDisplay.Screen,
+                     displayModes[ i ] ) )
+                fgWarning( "XF86VidModeSwitchToMode failed" );
+        }
+
+        XFree( displayModes );
+    }
 
 #   else
+
     /*
      * XXX warning fghChangeDisplayMode: missing XFree86 video mode extensions,
      * XXX game mode will not change screen resolution when activated
      */
+    success = GL_TRUE;
+
 #   endif
 
 #elif TARGET_HOST_WIN32 || TARGET_HOST_WINCE
 
-    unsigned int    displayModes = 0, mode = 0xffffffff;
-    GLboolean success = GL_FALSE;
-    /* HDC      desktopDC; */
     DEVMODE  devMode;
+    char fggmstr[255]="";
 
-    /*
-     * Enumerate the available display modes
-     * Try to get a complete match
-     */
-    while( EnumDisplaySettings( NULL, displayModes, &devMode ) )
-    {
-        /* Does the enumerated display mode match the user's preferences? */
-        if( fghCheckDisplayMode( devMode.dmPelsWidth,  devMode.dmPelsHeight,
-                                 devMode.dmBitsPerPel,
-                                 devMode.dmDisplayFrequency ) )
-        {
-            mode = displayModes;
-            break;
-        }
-        displayModes++;
-    }
+    success = GL_FALSE;
 
-    if( mode == 0xffffffff )
-    {
-        /* then try without Display Frequency */
-        displayModes = 0;
+    EnumDisplaySettings( NULL, -1, &devMode ); 
+    devMode.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
 
-        /* Enumerate the available display modes */
-        while( EnumDisplaySettings( NULL, displayModes, &devMode ) )
-        {
-            /* then try without Display Frequency */
-            if( fghCheckDisplayMode( devMode.dmPelsWidth,
-                                     devMode.dmPelsHeight,
-                                     devMode.dmBitsPerPel,
-                                     fgState.GameModeRefresh ) )
-            {
-                mode = displayModes;
-                break;
-            }
-            displayModes++;
-        }
-    }
+    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;
 
-    /* Did we find a matching display mode? */
-    if( mode != 0xffffffff )
+    switch ( ChangeDisplaySettingsEx(NULL, &devMode, NULL, haveToTest ? CDS_TEST : CDS_FULLSCREEN , NULL) )
     {
-        int retVal = DISP_CHANGE_SUCCESSFUL;
-
-        /* Mark the values we want to modify in the display change call */
-        devMode.dmFields |=
-            DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
-
-        retVal = ChangeDisplaySettings( &devMode, haveToTest ? CDS_TEST : 0 );
-
-        /* I don't know if it's really needed, but looks nice: */
-        success = (retVal == DISP_CHANGE_SUCCESSFUL) ||
-            (retVal == DISP_CHANGE_NOTUPDATED);
-
-        if( !haveToTest && success )
-        {
-            fgState.GameModeSize.X  = devMode.dmPelsWidth;
-            fgState.GameModeSize.Y  = devMode.dmPelsHeight;
-            fgState.GameModeDepth   = devMode.dmBitsPerPel;
-            fgState.GameModeRefresh = devMode.dmDisplayFrequency;
-        }
+    case DISP_CHANGE_SUCCESSFUL:
+        success = GL_TRUE;
+
+        // update vars in case if windows switched to proper mode
+        EnumDisplaySettings( NULL, FREEGLUT_ENUM_CURRENT_SETTINGS, &devMode );
+        fgState.GameModeSize.X  = devMode.dmPelsWidth;        
+        fgState.GameModeSize.Y  = devMode.dmPelsHeight;
+        fgState.GameModeDepth   = devMode.dmBitsPerPel;
+        fgState.GameModeRefresh = devMode.dmDisplayFrequency;
+               break;
+    case DISP_CHANGE_RESTART:
+        strcpy(fggmstr,"The computer must be restarted for the graphics mode to work.");
+        break;
+    case DISP_CHANGE_BADFLAGS:
+        strcpy(fggmstr,"An invalid set of flags was passed in.");
+        break;
+    case DISP_CHANGE_BADPARAM:
+        strcpy(fggmstr,"An invalid parameter was passed in. This can include an invalid flag or combination of flags.");
+        break;
+    case DISP_CHANGE_FAILED:
+        strcpy(fggmstr,"The display driver failed the specified graphics mode.");
+        break;
+    case DISP_CHANGE_BADMODE:
+        strcpy(fggmstr,"The graphics mode is not supported.");
+        break;
+    default:
+        strcpy(fggmstr,"Unknown error in graphics mode???"); /* dunno if it is possible,MSDN does not mention any other error */
+        break;
     }
 
-    return success;
-
+    if ( !success )
+        fgWarning(fggmstr); /* I'd rather get info whats going on in my program than wonder about */
+                            /* magic happenings behind my back, its lib for devels at last ;) */
 #endif
+
+    return success;
 }
 
 
@@ -360,6 +366,8 @@ void FGAPIENTRY glutGameModeString( const char* string )
 {
     int width = 640, height = 480, depth = 16, refresh = 72;
 
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutGameModeString" );
+
     /*
      * 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
@@ -391,6 +399,8 @@ void FGAPIENTRY glutGameModeString( const char* string )
  */
 int FGAPIENTRY glutEnterGameMode( void )
 {
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutEnterGameMode" );
+
     if( fgStructure.GameMode )
         fgAddToWindowDestroyList( fgStructure.GameMode );
     else
@@ -407,28 +417,19 @@ int FGAPIENTRY glutEnterGameMode( void )
         fgState.GameModeSize.X, fgState.GameModeSize.Y, GL_TRUE, GL_FALSE
     );
 
+    fgStructure.GameMode->State.Width  = fgState.GameModeSize.X;
+    fgStructure.GameMode->State.Height = fgState.GameModeSize.Y;
+
     fgStructure.GameMode->State.IsGameMode = GL_TRUE;
 
 #if TARGET_HOST_UNIX_X11
 
-    /* Move the window up to the topleft corner */
-    XMoveWindow( fgDisplay.Display, fgStructure.Window->Window.Handle, 0, 0 );
-
     /*
      * Sync needed to avoid a real race, the Xserver must have really created
      * the window before we can grab the pointer into it:
      */
     XSync( fgDisplay.Display, False );
 
-    /* Move the Pointer to the middle of the fullscreen window */
-    XWarpPointer(
-        fgDisplay.Display,
-        None,
-        fgDisplay.RootWindow,
-        0, 0, 0, 0,
-        fgState.GameModeSize.X/2, fgState.GameModeSize.Y/2
-    );
-
     /*
      * Grab the pointer to confine it into the window after the calls to
      * XWrapPointer() which ensure that the pointer really enters the window.
@@ -458,6 +459,15 @@ int FGAPIENTRY glutEnterGameMode( void )
         CurrentTime
     );
 
+    /* Move the Pointer to the middle of the fullscreen window */
+    XWarpPointer(
+        fgDisplay.Display,
+        None,
+        fgDisplay.RootWindow,
+        0, 0, 0, 0,
+        fgState.GameModeSize.X/2, fgState.GameModeSize.Y/2
+    );
+
 #   ifdef X_XF86VidModeSetViewPort
 
     if( fgDisplay.DisplayModeValid )
@@ -466,7 +476,8 @@ int FGAPIENTRY glutEnterGameMode( void )
         Window child;
 
         /* Change to viewport to the window topleft edge: */
-        XF86VidModeSetViewPort( fgDisplay.Display, fgDisplay.Screen, 0, 0 );
+        if( !XF86VidModeSetViewPort( fgDisplay.Display, fgDisplay.Screen, 0, 0 ) )
+            fgWarning( "XF86VidModeSetViewPort failed" );
 
         /*
          * Final window repositioning: It could be avoided using an undecorated
@@ -477,14 +488,14 @@ int FGAPIENTRY glutEnterGameMode( void )
         /* Get the current postion of the drawable area on screen */
         XTranslateCoordinates(
             fgDisplay.Display,
-            fgStructure.Window->Window.Handle,
+            fgStructure.CurrentWindow->Window.Handle,
             fgDisplay.RootWindow,
             0, 0, &x, &y,
             &child
         );
 
         /* Move the decorataions out of the topleft corner of the display */
-        XMoveWindow( fgDisplay.Display, fgStructure.Window->Window.Handle,
+        XMoveWindow( fgDisplay.Display, fgStructure.CurrentWindow->Window.Handle,
                      -x, -y);
     }
 
@@ -509,12 +520,13 @@ int FGAPIENTRY glutEnterGameMode( void )
  */
 void FGAPIENTRY glutLeaveGameMode( void )
 {
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveGameMode" );
+
     freeglut_return_if_fail( fgStructure.GameMode );
 
     fgStructure.GameMode->State.IsGameMode = GL_FALSE;
 
     fgAddToWindowDestroyList( fgStructure.GameMode );
-
     fgStructure.GameMode = NULL;
 
 #if TARGET_HOST_UNIX_X11
@@ -532,6 +544,8 @@ void FGAPIENTRY glutLeaveGameMode( void )
  */
 int FGAPIENTRY glutGameModeGet( GLenum eWhat )
 {
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutGameModeGet" );
+
     switch( eWhat )
     {
     case GLUT_GAME_MODE_ACTIVE:
@@ -559,6 +573,7 @@ int FGAPIENTRY glutGameModeGet( GLenum eWhat )
         return !!fgStructure.GameMode;
     }
 
+    fgWarning( "Unknown gamemode get: %d", eWhat );
     return -1;
 }