From cf6f8d2eb50aa28829ee2b76c61aab411f8730ae Mon Sep 17 00:00:00 2001
From: Henri Verbeet <hverbeet@codeweavers.com>
Date: Sun, 15 Jul 2012 15:45:00 +0200
Subject: [PATCH] winex11: Add support for XRandR 1.2.

---
 configure                 |  16 +++
 configure.ac              |   7 +-
 dlls/winex11.drv/xrandr.c | 237 +++++++++++++++++++++++++++++++++++---
 include/config.h.in       |   3 +
 4 files changed, 244 insertions(+), 19 deletions(-)

diff --git a/configure b/configure
index 5f8b931335b..5be9da3e967 100755
--- a/configure
+++ b/configure
@@ -8607,7 +8607,23 @@ cat >>confdefs.h <<_ACEOF
 #define SONAME_LIBXRANDR "$ac_cv_lib_soname_Xrandr"
 _ACEOF
 
+       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <X11/extensions/Xrandr.h>
+int
+main ()
+{
+static typeof(XRRGetScreenResources) *f; if (f) return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
 
+$as_echo "#define HAVE_XRRGETSCREENRESOURCES 1" >>confdefs.h
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 fi
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
diff --git a/configure.ac b/configure.ac
index 67a0af687ec..52ef054e64b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1051,7 +1051,12 @@ then
         then
                 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <X11/Xlib.h>
 #include <X11/extensions/Xrandr.h>]], [[static typeof(XRRSetScreenConfigAndRate) * func; if (func) return 0;]])],
-                  [WINE_CHECK_SONAME(Xrandr,XRRQueryExtension,,,[$X_LIBS $XLIB $X_EXTRA_LIBS])])
+                  [WINE_CHECK_SONAME(Xrandr,XRRQueryExtension,
+                    [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <X11/extensions/Xrandr.h>]],
+                        [[static typeof(XRRGetScreenResources) *f; if (f) return 0;]])],
+                      [AC_DEFINE(HAVE_XRRGETSCREENRESOURCES, 1,
+                        [Define if Xrandr has the XRRGetScreenResources function])])],,
+                    [$X_LIBS $XLIB $X_EXTRA_LIBS])])
         fi
         WINE_NOTICE_WITH(xrandr,[test "x$ac_cv_lib_soname_Xrandr" = "x"],
                          [libxrandr ${notice_platform}development files not found, XRandr won't be supported.])
diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c
index dfe392ebb92..e2dbff392ee 100644
--- a/dlls/winex11.drv/xrandr.c
+++ b/dlls/winex11.drv/xrandr.c
@@ -2,6 +2,7 @@
  * Wine X11drv Xrandr interface
  *
  * Copyright 2003 Alexander James Pasadyn
+ * Copyright 2012 Henri Verbeet for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -45,12 +46,24 @@ MAKE_FUNCPTR(XRRRates)
 MAKE_FUNCPTR(XRRSetScreenConfig)
 MAKE_FUNCPTR(XRRSetScreenConfigAndRate)
 MAKE_FUNCPTR(XRRSizes)
+
+#ifdef HAVE_XRRGETSCREENRESOURCES
+MAKE_FUNCPTR(XRRFreeCrtcInfo)
+MAKE_FUNCPTR(XRRFreeOutputInfo)
+MAKE_FUNCPTR(XRRFreeScreenResources)
+MAKE_FUNCPTR(XRRGetCrtcInfo)
+MAKE_FUNCPTR(XRRGetOutputInfo)
+MAKE_FUNCPTR(XRRGetScreenResources)
+MAKE_FUNCPTR(XRRSetCrtcConfig)
+static RRMode *xrandr12_modes;
+#endif
+
 #undef MAKE_FUNCPTR
 
 extern int usexrandr;
 
 static struct x11drv_mode_info *dd_modes;
-static SizeID *xrandr_modes;
+static SizeID *xrandr10_modes;
 static unsigned int xrandr_mode_count;
 
 static int load_xrandr(void)
@@ -75,11 +88,20 @@ static int load_xrandr(void)
         LOAD_FUNCPTR(XRRSetScreenConfig)
         LOAD_FUNCPTR(XRRSetScreenConfigAndRate)
         LOAD_FUNCPTR(XRRSizes)
-
+        r = 1;
+
+#ifdef HAVE_XRRGETSCREENRESOURCES
+        LOAD_FUNCPTR(XRRFreeCrtcInfo)
+        LOAD_FUNCPTR(XRRFreeOutputInfo)
+        LOAD_FUNCPTR(XRRFreeScreenResources)
+        LOAD_FUNCPTR(XRRGetCrtcInfo)
+        LOAD_FUNCPTR(XRRGetOutputInfo)
+        LOAD_FUNCPTR(XRRGetScreenResources)
+        LOAD_FUNCPTR(XRRSetCrtcConfig)
+        r = 2;
+#endif
 #undef LOAD_FUNCPTR
 
-        r = 1;   /* success */
-
 sym_not_found:
         if (!r)  TRACE("Unable to load function ptrs from XRandR library\n");
     }
@@ -91,7 +113,7 @@ static int XRandRErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
     return 1;
 }
 
-static int X11DRV_XRandR_GetCurrentMode(void)
+static int xrandr10_get_current_mode(void)
 {
     SizeID size;
     Rotation rot;
@@ -110,7 +132,7 @@ static int X11DRV_XRandR_GetCurrentMode(void)
     wine_tsx11_unlock();
     for (i = 0; i < xrandr_mode_count; ++i)
     {
-        if (xrandr_modes[i] == size && dd_modes[i].refresh_rate == rate)
+        if (xrandr10_modes[i] == size && dd_modes[i].refresh_rate == rate)
         {
             res = i;
             break;
@@ -124,7 +146,7 @@ static int X11DRV_XRandR_GetCurrentMode(void)
     return res;
 }
 
-static LONG X11DRV_XRandR_SetCurrentMode(int mode)
+static LONG xrandr10_set_current_mode( int mode )
 {
     SizeID size;
     Rotation rot;
@@ -144,7 +166,7 @@ static LONG X11DRV_XRandR_SetCurrentMode(int mode)
           dd_modes[mode].height,
           dd_modes[mode].refresh_rate);
 
-    size = xrandr_modes[mode];
+    size = xrandr10_modes[mode];
     rate = dd_modes[mode].refresh_rate;
 
     if (rate)
@@ -164,7 +186,7 @@ static LONG X11DRV_XRandR_SetCurrentMode(int mode)
     return DISP_CHANGE_FAILED;
 }
 
-static void xrandr_init_modes(void)
+static void xrandr10_init_modes(void)
 {
     XRRScreenSize *sizes;
     int sizes_count;
@@ -201,15 +223,15 @@ static void xrandr_init_modes(void)
 
     TRACE("XRandR modes: count=%d\n", nmodes);
 
-    if (!(xrandr_modes = HeapAlloc( GetProcessHeap(), 0, sizeof(*xrandr_modes) * nmodes )))
+    if (!(xrandr10_modes = HeapAlloc( GetProcessHeap(), 0, sizeof(*xrandr10_modes) * nmodes )))
     {
         ERR("Failed to allocate xrandr mode info array.\n");
         return;
     }
 
-    dd_modes = X11DRV_Settings_SetHandlers( "XRandR",
-                                            X11DRV_XRandR_GetCurrentMode,
-                                            X11DRV_XRandR_SetCurrentMode,
+    dd_modes = X11DRV_Settings_SetHandlers( "XRandR 1.0",
+                                            xrandr10_get_current_mode,
+                                            xrandr10_set_current_mode,
                                             nmodes, 1 );
 
     xrandr_mode_count = 0;
@@ -225,13 +247,13 @@ static void xrandr_init_modes(void)
             for (j = 0; j < rates_count; ++j)
             {
                 X11DRV_Settings_AddOneMode( sizes[i].width, sizes[i].height, 0, rates[j] );
-                xrandr_modes[xrandr_mode_count++] = i;
+                xrandr10_modes[xrandr_mode_count++] = i;
             }
         }
         else
         {
             X11DRV_Settings_AddOneMode( sizes[i].width, sizes[i].height, 0, 0 );
-            xrandr_modes[xrandr_mode_count++] = i;
+            xrandr10_modes[xrandr_mode_count++] = i;
         }
     }
 
@@ -242,16 +264,189 @@ static void xrandr_init_modes(void)
     TRACE("Enabling XRandR\n");
 }
 
+#ifdef HAVE_XRRGETSCREENRESOURCES
+
+static int xrandr12_get_current_mode(void)
+{
+    XRRScreenResources *resources;
+    XRRCrtcInfo *crtc_info;
+    int i, ret = -1;
+
+    wine_tsx11_lock();
+    if (!(resources = pXRRGetScreenResources( gdi_display, root_window )))
+    {
+        wine_tsx11_unlock();
+        ERR("Failed to get screen resources.\n");
+        return 0;
+    }
+
+    if (!resources->ncrtc || !(crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[0] )))
+    {
+        pXRRFreeScreenResources( resources );
+        wine_tsx11_unlock();
+        ERR("Failed to get CRTC info.\n");
+        return 0;
+    }
+
+    TRACE("CRTC 0: mode %#lx, %ux%u+%d+%d.\n", crtc_info->mode,
+          crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y);
+
+    for (i = 0; i < xrandr_mode_count; ++i)
+    {
+        if (xrandr12_modes[i] == crtc_info->mode)
+        {
+            ret = i;
+            break;
+        }
+    }
+
+    pXRRFreeCrtcInfo( crtc_info );
+    pXRRFreeScreenResources( resources );
+    wine_tsx11_unlock();
+
+    if (ret == -1)
+    {
+        ERR("Unknown mode, returning default.\n");
+        ret = 0;
+    }
+
+    return ret;
+}
+
+static LONG xrandr12_set_current_mode( int mode )
+{
+    Status status = RRSetConfigFailed;
+    XRRScreenResources *resources;
+    XRRCrtcInfo *crtc_info;
+
+    mode = mode % xrandr_mode_count;
+
+    wine_tsx11_lock();
+    if (!(resources = pXRRGetScreenResources( gdi_display, root_window )))
+    {
+        wine_tsx11_unlock();
+        ERR("Failed to get screen resources.\n");
+        return DISP_CHANGE_FAILED;
+    }
+
+    if (!resources->ncrtc || !(crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[0] )))
+    {
+        pXRRFreeScreenResources( resources );
+        wine_tsx11_unlock();
+        ERR("Failed to get CRTC info.\n");
+        return DISP_CHANGE_FAILED;
+    }
+
+    TRACE("CRTC 0: mode %#lx, %ux%u+%d+%d.\n", crtc_info->mode,
+          crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y);
+
+    status = pXRRSetCrtcConfig( gdi_display, resources, resources->crtcs[0], CurrentTime, crtc_info->x,
+            crtc_info->y, xrandr12_modes[mode], crtc_info->rotation, crtc_info->outputs, crtc_info->noutput );
+
+    pXRRFreeCrtcInfo( crtc_info );
+    pXRRFreeScreenResources( resources );
+    wine_tsx11_unlock();
+
+    if (status != RRSetConfigSuccess)
+    {
+        ERR("Resolution change not successful -- perhaps display has changed?\n");
+        return DISP_CHANGE_FAILED;
+    }
+
+    X11DRV_resize_desktop( dd_modes[mode].width, dd_modes[mode].height );
+    return DISP_CHANGE_SUCCESSFUL;
+}
+
+static void xrandr12_init_modes(void)
+{
+    XRRScreenResources *resources;
+    XRROutputInfo *output_info;
+    XRRCrtcInfo *crtc_info;
+    int i, j;
+
+    if (!(resources = pXRRGetScreenResources( gdi_display, root_window )))
+    {
+        ERR("Failed to get screen resources.\n");
+        return;
+    }
+
+    if (!resources->ncrtc || !(crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[0] )))
+    {
+        pXRRFreeScreenResources( resources );
+        ERR("Failed to get CRTC info.\n");
+        return;
+    }
+
+    TRACE("CRTC 0: mode %#lx, %ux%u+%d+%d.\n", crtc_info->mode,
+          crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y);
+
+    if (!crtc_info->noutput || !(output_info = pXRRGetOutputInfo( gdi_display, resources, crtc_info->outputs[0] )))
+    {
+        pXRRFreeCrtcInfo( crtc_info );
+        pXRRFreeScreenResources( resources );
+        ERR("Failed to get output info.\n");
+        return;
+    }
+
+    TRACE("OUTPUT 0: name %s.\n", debugstr_a(output_info->name));
+
+    if (!output_info->nmode)
+    {
+        ERR("Output has no modes.\n");
+        goto done;
+    }
+
+    if (!(xrandr12_modes = HeapAlloc( GetProcessHeap(), 0, sizeof(*xrandr12_modes) * output_info->nmode )))
+    {
+        ERR("Failed to allocate xrandr mode info array.\n");
+        goto done;
+    }
+
+    dd_modes = X11DRV_Settings_SetHandlers( "XRandR 1.2",
+                                            xrandr12_get_current_mode,
+                                            xrandr12_set_current_mode,
+                                            output_info->nmode, 1 );
+
+    xrandr_mode_count = 0;
+    for (i = 0; i < output_info->nmode; ++i)
+    {
+        for (j = 0; j < resources->nmode; ++j)
+        {
+            XRRModeInfo *mode = &resources->modes[j];
+
+            if (mode->id == output_info->modes[i])
+            {
+                unsigned int dots = mode->hTotal * mode->vTotal;
+                unsigned int refresh = dots ? (mode->dotClock + dots / 2) / dots : 0;
+
+                TRACE("Adding mode %#lx: %ux%u@%u.\n", mode->id, mode->width, mode->height, refresh);
+                X11DRV_Settings_AddOneMode( mode->width, mode->height, 0, refresh );
+                xrandr12_modes[xrandr_mode_count++] = mode->id;
+                break;
+            }
+        }
+    }
+
+    X11DRV_Settings_AddDepthModes();
+
+done:
+    pXRRFreeOutputInfo( output_info );
+    pXRRFreeCrtcInfo( crtc_info );
+    pXRRFreeScreenResources( resources );
+}
+
+#endif /* HAVE_XRRGETSCREENRESOURCES */
+
 void X11DRV_XRandR_Init(void)
 {
-    int event_base, error_base, minor;
+    int event_base, error_base, minor, ret;
     static int major;
     Bool ok;
 
     if (major) return; /* already initialized? */
     if (!usexrandr) return; /* disabled in config */
     if (root_window != DefaultRootWindow( gdi_display )) return;
-    if (!load_xrandr()) return;  /* can't load the Xrandr library */
+    if (!(ret = load_xrandr())) return;  /* can't load the Xrandr library */
 
     /* see if Xrandr is available */
     wine_tsx11_lock();
@@ -261,7 +456,13 @@ void X11DRV_XRandR_Init(void)
     if (X11DRV_check_error() || !ok) goto done;
 
     TRACE("Found XRandR %d.%d.\n", major, minor);
-    xrandr_init_modes();
+
+#ifdef HAVE_XRRGETSCREENRESOURCES
+    if (ret >= 2 && (major > 1 || (major == 1 && minor >= 2)))
+        xrandr12_init_modes();
+    else
+#endif
+        xrandr10_init_modes();
 
 done:
     wine_tsx11_unlock();
diff --git a/include/config.h.in b/include/config.h.in
index 983c732b17f..bb46d5076ea 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -1145,6 +1145,9 @@
 /* Define if Xrender has the XRenderSetPictureTransform function */
 #undef HAVE_XRENDERSETPICTURETRANSFORM
 
+/* Define if Xrandr has the XRRGetScreenResources function */
+#undef HAVE_XRRGETSCREENRESOURCES
+
 /* Define to 1 if you have the `z' library (-lz). */
 #undef HAVE_ZLIB
 
-- 
GitLab