Fixing multitouch for Windows per e-mail from Florian Echtler dated 5/3/11 10:33 AM
[freeglut] / src / freeglut_gamemode.c
index 490d2f6..e590385 100644 (file)
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#define  G_LOG_DOMAIN  "freeglut-gamemode"
-
-#include "../include/GL/freeglut.h"
+#include <GL/freeglut.h>
 #include "freeglut_internal.h"
 
 /*
 
 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
 
+#if TARGET_HOST_POSIX_X11
+static int xrandr_resize(int xsz, int ysz, int rate, int just_checking)
+{
+#ifdef HAVE_X11_EXTENSIONS_XRANDR_H
+    int event_base, error_base, ver_major, ver_minor, use_rate;
+    XRRScreenConfiguration *xrr_config = 0;
+    Status result = -1;
+
+    /* must check at runtime for the availability of the extension */
+    if(!XRRQueryExtension(fgDisplay.Display, &event_base, &error_base)) {
+        return -1;
+    }
+
+    XRRQueryVersion(fgDisplay.Display, &ver_major, &ver_minor);
+
+    /* we only heed the rate if we CAN actually use it (Xrandr >= 1.1) and
+     * the user actually cares about it (rate > 0)
+     */
+    use_rate = rate > 0 && ver_major >= 1 && ver_minor >= 1;
+
+    /* this loop is only so that the whole thing will be repeated if someone
+     * else changes video mode between our query the current information and
+     * the attempt to change it.
+     */
+    do {
+        XRRScreenSize *ssizes;
+        short *rates;
+        Rotation rot;
+        int i, ssizes_count, rates_count, curr, res_idx = -1;
+        Time timestamp, cfg_timestamp;
+
+        if(xrr_config) {
+            XRRFreeScreenConfigInfo(xrr_config);
+        }
+
+        if(!(xrr_config = XRRGetScreenInfo(fgDisplay.Display, fgDisplay.RootWindow))) {
+            fgWarning("XRRGetScreenInfo failed");
+            break;
+        }
+        ssizes = XRRConfigSizes(xrr_config, &ssizes_count);
+        curr = XRRConfigCurrentConfiguration(xrr_config, &rot);
+        timestamp = XRRConfigTimes(xrr_config, &cfg_timestamp);
+
+        /* if either of xsz or ysz are unspecified, use the current values */
+        if(xsz <= 0)
+            xsz = fgState.GameModeSize.X = ssizes[curr].width;
+        if(ysz <= 0)
+            ysz = fgState.GameModeSize.Y = ssizes[curr].height;
+
+
+        if(xsz == ssizes[curr].width && ysz == ssizes[curr].height) {
+            /* no need to switch, we're already in the requested resolution */
+            res_idx = curr;
+        } else {
+            for(i=0; i<ssizes_count; i++) {
+                if(ssizes[i].width == xsz && ssizes[i].height == ysz) {
+                    res_idx = i;
+                    break;  /* found it */
+                }
+            }
+        }
+        if(res_idx == -1)
+            break;  /* no matching resolution */
+
+#if RANDR_MAJOR >= 1 && RANDR_MINOR >= 1
+        if(rate <= 0) {
+            fgState.GameModeRefresh = XRRConfigCurrentRate(xrr_config);
+        }
+
+        if(use_rate) {
+            rate = fgState.GameModeRefresh;
+
+            /* for the selected resolution, let's find out if there is
+             * a matching refresh rate available.
+             */
+            rates = XRRConfigRates(xrr_config, res_idx, &rates_count);
+
+            for(i=0; i<rates_count; i++) {
+                if(rates[i] == rate) {
+                    break;
+                }
+            }
+            if(i == rates_count) {
+                break; /* no matching rate */
+            }
+        }
+#endif
+
+        if(just_checking) {
+            result = 0;
+            break;
+        }
+
+#if RANDR_MAJOR >= 1 && RANDR_MINOR >= 1
+        if(use_rate)
+            result = XRRSetScreenConfigAndRate(fgDisplay.Display, xrr_config,
+                    fgDisplay.RootWindow, res_idx, rot, rate, timestamp);
+        else
+#endif
+            result = XRRSetScreenConfig(fgDisplay.Display, xrr_config,
+                    fgDisplay.RootWindow, res_idx, rot, timestamp);
+
+    } while(result == RRSetConfigInvalidTime);
+
+    if(xrr_config) {
+        XRRFreeScreenConfigInfo(xrr_config);
+    }
+
+    if(result == 0) {
+        return 0;
+    }
+
+#endif  /* HAVE_X11_EXTENSIONS_XRANDR_H */
+    return -1;
+}
+#endif  /* TARGET_HOST_POSIX_X11 */
+
 /*
  * 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
+#if TARGET_HOST_POSIX_X11
+    int event_base, error_base;
 
     /*
-     * This highly depends on the XFree86 extensions, not approved as X Consortium standards
+     * Remember the current pointer location before going fullscreen
+     * for restoring it later:
      */
-#   ifdef X_XF86VidModeGetModeLine
+    Window junk_window;
+    unsigned int junk_mask;
+
+    XQueryPointer(fgDisplay.Display, fgDisplay.RootWindow,
+            &junk_window, &junk_window,
+            &fgDisplay.DisplayPointerX, &fgDisplay.DisplayPointerY,
+            &fgDisplay.DisplayPointerX, &fgDisplay.DisplayPointerY, &junk_mask);
+
+#   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_refresh = -1;
+
+#       if RANDR_MAJOR >= 1 && RANDR_MINOR >= 1
+            if(fgState.GameModeRefresh != -1) {
+                fgDisplay.prev_refresh = XRRConfigCurrentRate(xrr_config);
+            }
+#       endif
 
+            fgDisplay.prev_size_valid = 1;
 
-    /*
-     * 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
-    );
+            XRRFreeScreenConfigInfo(xrr_config);
+        }
+    }
+#   endif
 
     /*
-     * Remember the current pointer location before going fullscreen
-     * for restoring it later:
+     * This highly depends on the XFree86 extensions,
+     * not approved as X Consortium standards
      */
-    {
-       Window junk_window;
-       unsigned int mask;
-
-       XQueryPointer(
-           fgDisplay.Display, fgDisplay.RootWindow,
-           &junk_window, &junk_window,
-           &fgDisplay.DisplayPointerX, &fgDisplay.DisplayPointerY,
-           &fgDisplay.DisplayPointerX, &fgDisplay.DisplayPointerY, &mask
-       );
+#   ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
+    if(!XF86VidModeQueryExtension(fgDisplay.Display, &event_base, &error_base)) {
+        return;
     }
 
     /*
-     * Query the current display settings:
+     * Remember the current ViewPort location of the screen to be able to
+     * restore the ViewPort on LeaveGameMode():
      */
-    fgDisplay.DisplayModeValid = 
+    if( !XF86VidModeGetViewPort(
+             fgDisplay.Display,
+             fgDisplay.Screen,
+             &fgDisplay.DisplayViewPortX,
+             &fgDisplay.DisplayViewPortY ) )
+        fgWarning( "XF86VidModeGetViewPort failed" );
+
+
+    /* Query the current display settings: */
+    fgDisplay.DisplayModeValid =
       XF86VidModeGetModeLine(
         fgDisplay.Display,
         fgDisplay.Screen,
@@ -98,30 +236,25 @@ void fghRememberState( void )
         &fgDisplay.DisplayMode
     );
 
-    if (!fgDisplay.DisplayModeValid)
-       fgWarning( "Runtime use of XF86VidModeGetModeLine failed.\n" );
-
-#   else
-#       warning fghRememberState: missing XFree86 video mode extensions, game mode will not change screen resolution when activated
+    if( !fgDisplay.DisplayModeValid )
+        fgWarning( "XF86VidModeGetModeLine failed" );
 #   endif
 
-#elif TARGET_HOST_WIN32
+#elif TARGET_HOST_MS_WINDOWS
 
 /*    DEVMODE devMode; */
 
-    /*
-     * Grab the current desktop settings...
-     */
+    /* Grab the current desktop settings... */
 
 /* hack to get around my stupid cross-gcc headers */
 #define FREEGLUT_ENUM_CURRENT_SETTINGS -1
 
-    EnumDisplaySettings( NULL, FREEGLUT_ENUM_CURRENT_SETTINGS, &fgDisplay.DisplayMode );
+    EnumDisplaySettings( fgDisplay.DisplayName, FREEGLUT_ENUM_CURRENT_SETTINGS,
+                         &fgDisplay.DisplayMode );
 
-    /*
-     * Make sure we will be restoring all settings needed
-     */
-    fgDisplay.DisplayMode.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
+    /* Make sure we will be restoring all settings needed */
+    fgDisplay.DisplayMode.dmFields |=
+        DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
 
 #endif
 }
@@ -129,270 +262,339 @@ void fghRememberState( void )
 /*
  * Restores the previously remembered visual settings
  */
-void fghRestoreState( void )
+static void fghRestoreState( void )
 {
-#if TARGET_HOST_UNIX_X11
-    /*
-     * Restore the remembered pointer position:
-     */
+#if TARGET_HOST_POSIX_X11
+    /* Restore the remembered pointer position: */
     XWarpPointer(
-       fgDisplay.Display, None, fgDisplay.RootWindow, 0, 0, 0, 0,
-       fgDisplay.DisplayPointerX, fgDisplay.DisplayPointerY
+        fgDisplay.Display, None, fgDisplay.RootWindow, 0, 0, 0, 0,
+        fgDisplay.DisplayPointerX, fgDisplay.DisplayPointerY
     );
 
+
+#   ifdef HAVE_X11_EXTENSIONS_XRANDR_H
+    if(fgDisplay.prev_size_valid) {
+        if(xrandr_resize(fgDisplay.prev_xsz, fgDisplay.prev_ysz, fgDisplay.prev_refresh, 0) != -1) {
+            fgDisplay.prev_size_valid = 0;
+#       ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
+            fgDisplay.DisplayModeValid = 0;
+#       endif
+            return;
+        }
+    }
+#   endif
+
+
+
+#   ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
     /*
-     * This highly depends on the XFree86 extensions, not approved as X Consortium standards
+     * This highly depends on the XFree86 extensions,
+     * not approved as X Consortium standards
      */
-#   ifdef X_XF86VidModeGetAllModeLines
 
-    if (fgDisplay.DisplayModeValid)
+    if( fgDisplay.DisplayModeValid )
     {
         XF86VidModeModeInfo** displayModes;
         int i, displayModesCount;
 
-        /*
-         * Query for all the display available...
-         */
-        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
+         * Check every of the modes looking for one that matches our demands.
+         * If we find one, switch to it and restore the remembered viewport.
          */
-        for( i=0; i<displayModesCount; i++ )
+        for( i = 0; i < displayModesCount; i++ )
         {
-            if( displayModes[ i ]->hdisplay == fgDisplay.DisplayMode.hdisplay &&
-                displayModes[ i ]->vdisplay == fgDisplay.DisplayMode.vdisplay &&
-                displayModes[ i ]->dotclock == fgDisplay.DisplayModeClock )
+            if(displayModes[ i ]->hdisplay == fgDisplay.DisplayMode.hdisplay &&
+               displayModes[ i ]->vdisplay == fgDisplay.DisplayMode.vdisplay &&
+               displayModes[ i ]->dotclock == fgDisplay.DisplayModeClock )
             {
+                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" );
+
+
                 /*
-                 * OK, this is the display mode we have been looking for...
+                 * For the case this would be the last X11 call the application
+                 * calls exit() we've to flush the X11 output queue to have the
+                 * commands sent to the X server before the application exits.
                  */
-                XF86VidModeSwitchToMode(
-                    fgDisplay.Display,
-                    fgDisplay.Screen,
-                    displayModes[ i ]
-                );
-
-               /*
-                * Now we can restore the remembered ViewPort:
-                */
-               XF86VidModeSetViewPort(
-                    fgDisplay.Display,
-                    fgDisplay.Screen,
-                    fgDisplay.DisplayViewPortX,
-                    fgDisplay.DisplayViewPortY
-               );
-
-               /*
-                * For the case this would be the last X11 call the application
-                * calls exit() we've to flush the X11 output queue to have the
-                * commands sent to the X server before the application exists.
-                */
-               XFlush(fgDisplay.Display);
-
-                return;
+                XFlush( fgDisplay.Display );
+
+                fgDisplay.DisplayModeValid = 0;
+#       ifdef HAVE_X11_EXTENSIONS_XRANDR_H
+                fgDisplay.prev_size_valid = 0;
+#       endif
+
+                break;
             }
         }
+        XFree( displayModes );
     }
 
-#   else
-#       warning fghRestoreState: missing XFree86 video mode extensions, game mode will not change screen resolution when activated
 #   endif
 
-#elif TARGET_HOST_WIN32
+#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 HAVE_X11_EXTENSIONS_XF86VMODE_H
+
 /*
- * Checks the display mode settings against user's preferences
+ * Checks a single 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) && (height == fgState.GameModeSize.Y) &&
-            (depth == fgState.GameModeDepth)  && (refresh == fgState.GameModeRefresh) );
+    /* The desired values should be stored in fgState structure... */
+    return ( width == fgState.GameModeSize.X ) &&
+           ( height == fgState.GameModeSize.Y ) &&
+           ( depth == fgState.GameModeDepth ) &&
+           ( refresh == fgState.GameModeRefresh );
 }
 
 /*
- * Changes the current display mode to match user's settings
+ * Checks all display modes settings against user's preferences.
+ * Returns the mode number found or -1 if none could be found.
  */
-GLboolean fghChangeDisplayMode( GLboolean haveToTest )
+static int fghCheckDisplayModes( GLboolean exactMatch, int displayModesCount, XF86VidModeModeInfo** displayModes )
 {
-#if TARGET_HOST_UNIX_X11
-
-    /*
-     * This highly depends on the XFree86 extensions, not approved as X Consortium standards
-     */
-#   ifdef X_XF86VidModeGetAllModeLines
-
-    /*
-     * This is also used by applcations which check modes by calling
-     * glutGameModeGet(GLUT_GAME_MODE_POSSIBLE), so allow the check:
-     */
-    if (haveToTest || fgDisplay.DisplayModeValid)
+    int i;
+    for( i = 0; i < displayModesCount; i++ )
     {
-        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; i<displayModesCount; i++ )
-        {
-            if( fghCheckDisplayMode( displayModes[ i ]->hdisplay, displayModes[ i ]->vdisplay,
-                                     fgState.GameModeDepth, fgState.GameModeRefresh ) )
+        /* Compute the displays refresh rate, dotclock comes in kHz. */
+        int refresh = ( displayModes[ i ]->dotclock * 1000 ) /
+                      ( displayModes[ i ]->htotal * displayModes[ i ]->vtotal );
+
+        if( fghCheckDisplayMode( displayModes[ i ]->hdisplay,
+                                 displayModes[ i ]->vdisplay,
+                                 fgState.GameModeDepth,
+                                 ( exactMatch ? refresh : fgState.GameModeRefresh ) ) ) {
+            if (!exactMatch)
             {
-                   if( haveToTest )
-                               return( TRUE );
-                /*
-                 * OKi, this is the display mode we have been looking for...
-                 */
-                XF86VidModeSwitchToMode(
-                    fgDisplay.Display,
-                    fgDisplay.Screen,
-                    displayModes[ i ]
-                );
-                /*
-                 * Return successfull...
+                /* Update the chosen refresh rate, otherwise a
+                 * glutGameModeGet(GLUT_GAME_MODE_REFRESH_RATE) would not
+                 * return the right values
                  */
-                return( TRUE );
+                fgState.GameModeRefresh = refresh;
             }
+
+            return i;
         }
     }
+    return -1;
+}
 
-    /*
-     * Something must have went wrong
-     */
-    return( FALSE );
+#endif
+#endif
 
-#   else
-#       warning fghChangeDisplayMode: missing XFree86 video mode extensions, game mode will not change screen resolution when activated
+/*
+ * Changes the current display mode to match user's settings
+ */
+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,
+                fgState.GameModeRefresh, haveToTest) != -1) {
+        return GL_TRUE;
+    }
 #   endif
 
-#elif TARGET_HOST_WIN32
 
-    unsigned int    displayModes = 0, mode = 0xffffffff;
-    GLboolean success = FALSE;
-/*    HDC      desktopDC; */
-    DEVMODE  devMode;
+    /*
+     * This highly depends on the XFree86 extensions,
+     * not approved as X Consortium standards
+     */
+#   ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
 
     /*
-     * Enumerate the available display modes
-     * Try to get a complete match
+     * This is also used by applications which check modes by calling
+     * glutGameModeGet(GLUT_GAME_MODE_POSSIBLE), so allow the check:
      */
-    while( EnumDisplaySettings( NULL, displayModes, &devMode ) == TRUE )
+    if( haveToTest || fgDisplay.DisplayModeValid )
     {
-        /*
-         * Does the enumerated display mode match the user's preferences?
+        XF86VidModeModeInfo** displayModes;
+        int i, displayModesCount;
+
+        /* If we don't have a valid modeline in the display structure, which
+         * can happen if this is called from glutGameModeGet instead of
+         * glutEnterGameMode, then we need to query the current mode, to make
+         * unspecified settings to default to their current values.
          */
-        if( fghCheckDisplayMode( devMode.dmPelsWidth,  devMode.dmPelsHeight,
-                                 devMode.dmBitsPerPel, devMode.dmDisplayFrequency ) )
+        if(!fgDisplay.DisplayModeValid) {
+            if(!XF86VidModeGetModeLine(fgDisplay.Display, fgDisplay.Screen,
+                    &fgDisplay.DisplayModeClock, &fgDisplay.DisplayMode)) {
+                return success;
+            }
+        }
+
+        if (fgState.GameModeSize.X == -1)
+        {
+            fgState.GameModeSize.X = fgDisplay.DisplayMode.hdisplay;
+        }
+        if (fgState.GameModeSize.Y == -1)
+        {
+            fgState.GameModeSize.Y = fgDisplay.DisplayMode.vdisplay;
+        }
+        if (fgState.GameModeDepth == -1)
         {
-            /*
-             * OKi, we've found a matching display mode, remember its number and break
+            /* can't get color depth from this, nor can we change it, do nothing
+             * TODO: get with XGetVisualInfo()? but then how to set?
              */
-            mode = displayModes;
-            break;
         }
+        if (fgState.GameModeRefresh == -1)
+        {
+            /* Compute the displays refresh rate, dotclock comes in kHz. */
+            int refresh = ( fgDisplay.DisplayModeClock * 1000 ) /
+                ( fgDisplay.DisplayMode.htotal * fgDisplay.DisplayMode.vtotal );
 
-        /*
-         * Switch to the next display mode, if any
-         */
-        displayModes++;
-    }
+            fgState.GameModeRefresh = refresh;
+        }
 
-    if ( mode == 0xffffffff )
-    {
-      /* then try without Display Frequency */
-      displayModes = 0;
-
-      /*
-       * Enumerate the available display modes
-       */
-      while( EnumDisplaySettings( NULL, displayModes, &devMode ) == TRUE )
-      {
-        /* then try without Display Frequency */
-
-        if( fghCheckDisplayMode( devMode.dmPelsWidth,  devMode.dmPelsHeight,
-                                 devMode.dmBitsPerPel, fgState.GameModeRefresh))
+        /* query all possible display modes */
+        if( !XF86VidModeGetAllModeLines(
+                 fgDisplay.Display,
+                 fgDisplay.Screen,
+                 &displayModesCount,
+                 &displayModes ) )
         {
-          /*
-           * OKi, we've found a matching display mode, remember its number and break
-           */
-          mode = displayModes;
-          break;
+            fgWarning( "XF86VidModeGetAllModeLines failed" );
+            return success;
         }
-       
+
+
         /*
-         * Switch to the next display mode, if any
+         * Check every of the modes looking for one that matches our demands,
+         * ignoring the refresh rate if no exact match could be found.
          */
-        displayModes++;
-      }
+        i = fghCheckDisplayModes( GL_TRUE, displayModesCount, displayModes );
+        if( i < 0 ) {
+            i = fghCheckDisplayModes( GL_FALSE, displayModesCount, displayModes );
+        }
+        success = ( i < 0 ) ? GL_FALSE : GL_TRUE;
+
+        if( !haveToTest && success ) {
+            if( !XF86VidModeSwitchToMode(
+                     fgDisplay.Display,
+                     fgDisplay.Screen,
+                     displayModes[ i ] ) )
+                fgWarning( "XF86VidModeSwitchToMode failed" );
+        }
+
+        XFree( displayModes );
     }
 
-    /*
-     * Did we find a matching display mode?
-     */
-    if( mode != 0xffffffff )
-    {
-        int retVal = DISP_CHANGE_SUCCESSFUL;
+#   endif
 
-        /*
-         * Mark the values we want to modify in the display change call
-         */
-        devMode.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
 
-        /*
-         * Change the current display mode (possibly in test mode only)
-         */
-        retVal = ChangeDisplaySettings( &devMode, haveToTest ? CDS_TEST : 0 );
+#elif TARGET_HOST_MS_WINDOWS
 
-        /*
-         * I don't know if it's really needed, but looks nice:
-         */
-        success = (retVal == DISP_CHANGE_SUCCESSFUL) || (retVal == DISP_CHANGE_NOTUPDATED);
+    DEVMODE  devMode;
+    char *fggmstr = NULL;
+    char displayMode[300];
 
-        /*
-         * If it was not a test, remember the current screen settings
-         */
-        if( !haveToTest && success )
+    success = GL_FALSE;
+
+    EnumDisplaySettings( fgDisplay.DisplayName, -1, &devMode ); 
+    devMode.dmFields = 0;
+
+    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(fgDisplay.DisplayName, &devMode, NULL, haveToTest ? CDS_TEST : CDS_FULLSCREEN , NULL) )
+    {
+    case DISP_CHANGE_SUCCESSFUL:
+        success = GL_TRUE;
+
+        if (!haveToTest)
         {
-            fgState.GameModeSize.X  = devMode.dmPelsWidth;
+            /* update vars in case if windows switched to proper mode */
+            EnumDisplaySettings( fgDisplay.DisplayName, 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:
+        fggmstr = "The computer must be restarted for the graphics mode to work.";
+        break;
+    case DISP_CHANGE_BADFLAGS:
+        fggmstr = "An invalid set of flags was passed in.";
+        break;
+    case DISP_CHANGE_BADPARAM:
+        fggmstr = "An invalid parameter was passed in. This can include an invalid flag or combination of flags.";
+        break;
+    case DISP_CHANGE_FAILED:
+        fggmstr = "The display driver failed the specified graphics mode.";
+        break;
+    case DISP_CHANGE_BADMODE:
+        fggmstr = "The graphics mode is not supported.";
+        break;
+    default:
+        fggmstr = "Unknown error in graphics mode???"; /* dunno if it is possible,MSDN does not mention any other error */
+        break;
     }
 
-    /*
-     * Otherwise we must have failed somewhere
-     */
-    return( success );
-
+    if ( !success )
+    {
+        /* 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 ;) */
+        
+        /* append display mode to error to make things more informative */
+        sprintf(displayMode,"%s Problem with requested mode: %ix%i:%i@%i", fggmstr, devMode.dmPelsWidth, devMode.dmPelsHeight, devMode.dmBitsPerPel, devMode.dmDisplayFrequency);
+        fgWarning(displayMode);
+    }
 #endif
+
+    return success;
 }
 
 
@@ -403,7 +605,9 @@ 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" );
 
     /*
      * This one seems a bit easier than glutInitDisplayString. The bad thing
@@ -411,17 +615,22 @@ void FGAPIENTRY glutGameModeString( const char* string )
      * 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@%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 )
-                                fgWarning( "unable to parse game mode string `%s'", string );
-
-    /*
-     * Hopefully it worked, and if not, we still have the default values
+                                fgWarning(
+                                    "unable to parse game mode string `%s'",
+                                    string
+                                );
+
+    /* All values not specified are now set to -1, which means those
+     * aspects of the current display mode are not changed in
+     * fghChangeDisplayMode() above.
      */
     fgState.GameModeSize.X  = width;
     fgState.GameModeSize.Y  = height;
@@ -429,65 +638,43 @@ void FGAPIENTRY glutGameModeString( const char* string )
     fgState.GameModeRefresh = refresh;
 }
 
+
+
 /*
  * Enters the game mode
  */
 int FGAPIENTRY glutEnterGameMode( void )
 {
-    /*
-     * Check if a game mode window already exists...
-     */
-    if( fgStructure.GameMode != NULL )
-    {
-        /*
-         * ...if so, delete it before proceeding...
-         */
-        fgAddToWindowDestroyList( fgStructure.GameMode, TRUE );
-    }
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutEnterGameMode" );
+
+    if( fgStructure.GameModeWindow )
+        fgAddToWindowDestroyList( fgStructure.GameModeWindow );
     else
-    {
-        /*
-         * ...otherwise remember the current resolution, etc.
-         */
-        fghRememberState();
-    }
+        fghRememberState( );
 
-    /*
-     * We are ready to change the current screen's resolution now
-     */
-    if( fghChangeDisplayMode( FALSE ) == FALSE )
+    if( ! fghChangeDisplayMode( GL_FALSE ) )
     {
-             fgWarning( "failed to change screen settings" );
-        return( FALSE );
+        fgWarning( "failed to change screen settings" );
+        return 0;
     }
 
-    /*
-     * Finally, have the game mode window created
-     */
-    fgStructure.GameMode = fgCreateWindow( 
-        NULL, "FREEGLUT", 0, 0, fgState.GameModeSize.X, fgState.GameModeSize.Y, TRUE 
+    fgStructure.GameModeWindow = fgCreateWindow(
+        NULL, "FREEGLUT", GL_TRUE, 0, 0,
+        GL_TRUE, fgState.GameModeSize.X, fgState.GameModeSize.Y,
+        GL_TRUE, GL_FALSE
     );
 
-#if TARGET_HOST_UNIX_X11
+    fgStructure.GameModeWindow->State.Width  = fgState.GameModeSize.X;
+    fgStructure.GameModeWindow->State.Height = fgState.GameModeSize.Y;
+    fgStructure.GameModeWindow->State.NeedToResize = GL_TRUE;
 
-    /* Move the window up to the topleft corner */
-    XMoveWindow(fgDisplay.Display, fgStructure.Window->Window.Handle, 0, 0);
+#if TARGET_HOST_POSIX_X11
 
     /*
      * 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
-    );
-
+    XSync( fgDisplay.Display, False );
     /*
      * Grab the pointer to confine it into the window after the calls to
      * XWrapPointer() which ensure that the pointer really enters the window.
@@ -497,64 +684,71 @@ int FGAPIENTRY glutEnterGameMode( void )
      * (XSetInputFocus) is called with a not yet viewable window, it will exit
      * the application which we have to aviod, so wait until it's viewable:
      */
-    while (GrabSuccess =! XGrabPointer(
-           fgDisplay.Display, fgStructure.GameMode->Window.Handle,
-           TRUE, ButtonPressMask|ButtonReleaseMask|ButtonMotionMask
-               |PointerMotionMask,
-           GrabModeAsync, GrabModeAsync,
-           fgStructure.GameMode->Window.Handle, None, CurrentTime)) {
-       usleep (100);
-    }
-
+    while( GrabSuccess != XGrabPointer(
+               fgDisplay.Display, fgStructure.GameModeWindow->Window.Handle,
+               TRUE,
+               ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
+               | PointerMotionMask,
+               GrabModeAsync, GrabModeAsync,
+               fgStructure.GameModeWindow->Window.Handle, None, CurrentTime) )
+        usleep( 100 );
     /*
      * Change input focus to the new window. This will exit the application
      * if the new window is not viewable yet, see the XGrabPointer loop above.
      */
     XSetInputFocus(
         fgDisplay.Display,
-        fgStructure.GameMode->Window.Handle,
+        fgStructure.GameModeWindow->Window.Handle,
         RevertToNone,
         CurrentTime
     );
 
-#   ifdef X_XF86VidModeSetViewPort
+    /* 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
+    );
 
-    if (fgDisplay.DisplayModeValid) {
-       int x, y;
-       Window child;
+#   ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
 
-       /*
-        * Change to viewport to the window topleft edge:
-        */
-       XF86VidModeSetViewPort(fgDisplay.Display, fgDisplay.Screen, 0, 0);
+    if( fgDisplay.DisplayModeValid )
+    {
+        int x, y;
+        Window child;
+
+        /* Change to viewport to the window topleft edge: */
+        if( !XF86VidModeSetViewPort( fgDisplay.Display, fgDisplay.Screen, 0, 0 ) )
+            fgWarning( "XF86VidModeSetViewPort failed" );
 
-       /*
-        * Final window repositioning: It could be avoided using an undecorated
-        * window using override_redirect, but this * would possily require more
-        * changes and investigation.
-        */
+        /*
+         * Final window repositioning: It could be avoided using an undecorated
+         * window using override_redirect, but this * would possily require
+         * more changes and investigation.
+         */
 
         /* Get the current postion of the drawable area on screen */
-       XTranslateCoordinates(
-           fgDisplay.Display,
-           fgStructure.Window->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, -x, -y);
+        XTranslateCoordinates(
+            fgDisplay.Display,
+            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.CurrentWindow->Window.Handle,
+                     -x, -y);
     }
 
 #endif
 
-    /*
-     * Grab the keyboard, too
-     */
+    /* Grab the keyboard, too */
     XGrabKeyboard(
         fgDisplay.Display,
-        fgStructure.GameMode->Window.Handle,
+        fgStructure.GameModeWindow->Window.Handle,
         FALSE,
         GrabModeAsync, GrabModeAsync,
         CurrentTime
@@ -562,10 +756,7 @@ int FGAPIENTRY glutEnterGameMode( void )
 
 #endif
 
-    /*
-     * Return successfull
-     */
-    return( TRUE );
+    return fgStructure.GameModeWindow->ID;
 }
 
 /*
@@ -573,26 +764,20 @@ int FGAPIENTRY glutEnterGameMode( void )
  */
 void FGAPIENTRY glutLeaveGameMode( void )
 {
-    freeglut_return_if_fail( fgStructure.GameMode != NULL );
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveGameMode" );
 
-    /*
-     * First of all, have the game mode window destroyed
-     */
-    fgAddToWindowDestroyList( fgStructure.GameMode, TRUE );
+    freeglut_return_if_fail( fgStructure.GameModeWindow );
 
-#if TARGET_HOST_UNIX_X11
+    fgAddToWindowDestroyList( fgStructure.GameModeWindow );
+    fgStructure.GameModeWindow = NULL;
+
+#if TARGET_HOST_POSIX_X11
 
-    /*
-     * Ungrab the mouse and keyboard
-     */
     XUngrabPointer( fgDisplay.Display, CurrentTime );
     XUngrabKeyboard( fgDisplay.Display, CurrentTime );
 
 #endif
 
-    /*
-     * Then, have the desktop visual settings restored
-     */
     fghRestoreState();
 }
 
@@ -601,59 +786,37 @@ void FGAPIENTRY glutLeaveGameMode( void )
  */
 int FGAPIENTRY glutGameModeGet( GLenum eWhat )
 {
-    /*
-     * See why are we bothered
-     */
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutGameModeGet" );
+
     switch( eWhat )
     {
     case GLUT_GAME_MODE_ACTIVE:
-        /*
-         * Check if the game mode is currently active
-         */
-        return( fgStructure.GameMode != NULL );
+        return !!fgStructure.GameModeWindow;
 
     case GLUT_GAME_MODE_POSSIBLE:
-        /*
-         * Check if the current game mode settings are valid
-         */
-        return( fghChangeDisplayMode( TRUE ) );
+        return fghChangeDisplayMode( GL_TRUE );
 
     case GLUT_GAME_MODE_WIDTH:
-        /*
-         * The game mode screen width
-         */
-        return( fgState.GameModeSize.X );
+        return fgState.GameModeSize.X;
 
     case GLUT_GAME_MODE_HEIGHT:
-        /*
-         * The game mode screen height
-         */
-        return( fgState.GameModeSize.Y );
+        return fgState.GameModeSize.Y;
 
     case GLUT_GAME_MODE_PIXEL_DEPTH:
-        /*
-         * The game mode pixel depth
-         */
-        return( fgState.GameModeDepth );
+        return fgState.GameModeDepth;
 
     case GLUT_GAME_MODE_REFRESH_RATE:
-        /*
-         * The game mode refresh rate
-         */
-        return( fgState.GameModeRefresh );
+        return fgState.GameModeRefresh;
 
     case GLUT_GAME_MODE_DISPLAY_CHANGED:
         /*
          * This is true if the game mode has been activated successfully..
          */
-        return( fgStructure.GameMode != NULL );
+        return !!fgStructure.GameModeWindow;
     }
 
-    return( -1 );
+    fgWarning( "Unknown gamemode get: %d", eWhat );
+    return -1;
 }
 
 /*** END OF FILE ***/
-
-
-
-