Blame SOURCES/0010-randr-Make-resolution-changing-more-robust.patch

a547b4
From 5cf857175d724e399b53d9ce29d09fa7aef5bf36 Mon Sep 17 00:00:00 2001
a547b4
From: Christophe Fergeau <cfergeau@redhat.com>
a547b4
Date: Thu, 9 Jan 2014 17:41:07 +0100
a547b4
Subject: [PATCH] randr: Make resolution changing more robust
a547b4
a547b4
When running a guest using a KMS QXL driver on a RHEL6 hypervisor,
a547b4
spice_vdagent would crash on resolution changes with:
a547b4
X Error of failed request:  BadValue (integer parameter out of range for operation)
a547b4
  Major opcode of failed request:  139 (RANDR)
a547b4
  Minor opcode of failed request:  21 (RRSetCrtcConfig)
a547b4
  Value in failed request:  0x0
a547b4
  Serial number of failed request:  75
a547b4
  Current serial number in output stream:  75
a547b4
a547b4
(if using a newer QEMU, this will not crash as the resolution codepath is
a547b4
totally different, see red_dispatcher_use_client_monitors_config() in
a547b4
spice-server, when using the UMS QXL driver, the crash will not happen
a547b4
because of
a547b4
http://cgit.freedesktop.org/xorg/driver/xf86-video-qxl/commit/?id=907d0ff0
a547b4
).
a547b4
a547b4
This crash happens in vdagent_x11_set_monitor_config() because the X server
a547b4
rejects the request because we are trying to set a CRTC to a size which is
a547b4
bigger than the current screen (using 'screen' and 'CRTC' as in the RandR
a547b4
spec at http://www.x.org/releases/X11R7.5/doc/randrproto/randrproto.txt ).
a547b4
a547b4
If we resize the screen before changing the CRTCs resolution, then this
a547b4
error will no longer happen. However, if we first call XRRSetScreenSize()
a547b4
and then XRRSetCrtcConfig(), the call to XRRSetScreeSize() will fail when
a547b4
we try to make the screen smaller as there will be active CRTCs which would
a547b4
be bigger than the screen.
a547b4
a547b4
In order to solve these issues, we follow the same process as what mutter
a547b4
does in meta_monitor_manager_xrandr_apply_configuration()
a547b4
https://git.gnome.org/browse/mutter/tree/src/core/monitor-xrandr.c#n689:
a547b4
a547b4
1. we disable the CRTCs which are off and the ones which are bigger than the
a547b4
   size we want to set the screen to.
a547b4
2. we resize the screen with a call to XRRSetScreenSize().
a547b4
3. we set each CRTCs to their new resolution.
a547b4
a547b4
https://bugzilla.redhat.com/show_bug.cgi?id=1066422
a547b4
(cherry picked from commit 9ae51f3702b1fc0d2747e44474c87a818d1e8ec3)
a547b4
---
a547b4
 src/vdagent-x11-randr.c | 61 +++++++++++++++++++++++++++++++++++++------------
a547b4
 1 file changed, 47 insertions(+), 14 deletions(-)
a547b4
a547b4
diff --git a/src/vdagent-x11-randr.c b/src/vdagent-x11-randr.c
a547b4
index 5223f88..5fcfcc8 100644
a547b4
--- a/src/vdagent-x11-randr.c
a547b4
+++ b/src/vdagent-x11-randr.c
a547b4
@@ -688,8 +688,6 @@ void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
a547b4
                                     VDAgentMonitorsConfig *mon_config,
a547b4
                                     int fallback)
a547b4
 {
a547b4
-    int width, height;
a547b4
-    int x, y;
a547b4
     int primary_w, primary_h;
a547b4
     int i, real_num_of_monitors = 0;
a547b4
     VDAgentMonitorsConfig *curr = NULL;
a547b4
@@ -748,24 +746,38 @@ void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
a547b4
     for (i = mon_config->num_of_monitors; i < x11->randr.res->noutput; i++)
a547b4
         xrandr_disable_output(x11, i);
a547b4
 
a547b4
+    /* First, disable disabled CRTCs... */
a547b4
     for (i = 0; i < mon_config->num_of_monitors; ++i) {
a547b4
         if (!monitor_enabled(&mon_config->monitors[i])) {
a547b4
             xrandr_disable_output(x11, i);
a547b4
-            continue;
a547b4
         }
a547b4
-        /* Try to create the requested resolution */
a547b4
-        width = mon_config->monitors[i].width;
a547b4
-        height = mon_config->monitors[i].height;
a547b4
-        x = mon_config->monitors[i].x;
a547b4
-        y = mon_config->monitors[i].y;
a547b4
-        if (!xrandr_add_and_set(x11, i, x, y, width, height) &&
a547b4
-                enabled_monitors(mon_config) == 1) {
a547b4
-            set_screen_to_best_size(x11, width, height,
a547b4
-                                    &primary_w, &primary_h);
a547b4
-            goto update;
a547b4
+    }
a547b4
+
a547b4
+    /* ... and disable the ones that would be bigger than
a547b4
+     * the new RandR screen once it is resized. If they are enabled the
a547b4
+     * XRRSetScreenSize call will fail with BadMatch. They will be
a547b4
+     * reenabled after hanging the screen size.
a547b4
+     */
a547b4
+    for (i = 0; i < curr->num_of_monitors; ++i) {
a547b4
+        int width, height;
a547b4
+        int x, y;
a547b4
+
a547b4
+        width = curr->monitors[i].width;
a547b4
+        height = curr->monitors[i].height;
a547b4
+        x = curr->monitors[i].x;
a547b4
+        y = curr->monitors[i].y;
a547b4
+
a547b4
+        if ((x + width > primary_w) || (y + height > primary_h)) {
a547b4
+            if (x11->debug)
a547b4
+                syslog(LOG_DEBUG, "Disabling monitor %d: "
a547b4
+                       "%dx%d+%d+%d > (%d,%d)",
a547b4
+                       i, width, height, x, y, primary_w, primary_h);
a547b4
+
a547b4
+            xrandr_disable_output(x11, i);
a547b4
         }
a547b4
     }
a547b4
 
a547b4
+    /* Then we can resize the RandR screen. */
a547b4
     if (primary_w != x11->width[0] || primary_h != x11->height[0]) {
a547b4
         if (x11->debug)
a547b4
             syslog(LOG_DEBUG, "Changing screen size to %dx%d",
a547b4
@@ -793,7 +805,28 @@ void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
a547b4
         }
a547b4
     }
a547b4
 
a547b4
-update:
a547b4
+    /* Finally, we set the new resolutions on RandR CRTCs now that the
a547b4
+     * RandR screen is big enough to hold these.  */
a547b4
+    for (i = 0; i < mon_config->num_of_monitors; ++i) {
a547b4
+        int width, height;
a547b4
+        int x, y;
a547b4
+
a547b4
+        if (!monitor_enabled(&mon_config->monitors[i])) {
a547b4
+            continue;
a547b4
+        }
a547b4
+        /* Try to create the requested resolution */
a547b4
+        width = mon_config->monitors[i].width;
a547b4
+        height = mon_config->monitors[i].height;
a547b4
+        x = mon_config->monitors[i].x;
a547b4
+        y = mon_config->monitors[i].y;
a547b4
+        if (!xrandr_add_and_set(x11, i, x, y, width, height) &&
a547b4
+                enabled_monitors(mon_config) == 1) {
a547b4
+            set_screen_to_best_size(x11, width, height,
a547b4
+                                    &primary_w, &primary_h);
a547b4
+            break;
a547b4
+        }
a547b4
+    }
a547b4
+
a547b4
     update_randr_res(x11,
a547b4
         x11->randr.num_monitors != enabled_monitors(mon_config));
a547b4
     x11->width[0] = primary_w;