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

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