|
|
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;
|