cf81640
From: Christophe Fergeau <cfergeau@redhat.com>
cf81640
Date: Fri, 14 Oct 2016 14:22:36 +0200
cf81640
Subject: [PATCH] qxl: Only emit QXL_INTERRUPT_CLIENT_MONITORS_CONFIG on config
cf81640
 changes
cf81640
cf81640
Currently if the client keeps sending the same monitor config to
cf81640
QEMU/spice-server, QEMU will always raise
cf81640
a QXL_INTERRUPT_CLIENT_MONITORS_CONFIG regardless of whether there was a
cf81640
change or not.
cf81640
Guest-side (with fedora 25), the kernel QXL KMS driver will also forward the
cf81640
event to user-space without checking if there were actual changes.
cf81640
Next in line are gnome-shell/mutter (on a default f25 install), which
cf81640
will try to reconfigure everything without checking if there is anything
cf81640
to do.
cf81640
Where this gets ugly is that when applying the resolution changes,
cf81640
gnome-shell/mutter will call drmModeRmFB, drmModeAddFB, and
cf81640
drmModeSetCrtc, which will cause the primary surface to be destroyed and
cf81640
recreated by the QXL KMS driver. This in turn will cause the client to
cf81640
resend a client monitors config message, which will cause QEMU to reemit
cf81640
an interrupt with an unchanged monitors configuration, ...
cf81640
This causes https://bugzilla.redhat.com/show_bug.cgi?id=1266484
cf81640
cf81640
This commit makes sure that we only emit
cf81640
QXL_INTERRUPT_CLIENT_MONITORS_CONFIG when there are actual configuration
cf81640
changes the guest should act on.
cf81640
---
cf81640
 hw/display/qxl.c | 20 +++++++++++++++++++-
cf81640
 1 file changed, 19 insertions(+), 1 deletion(-)
cf81640
cf81640
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
cf81640
index 0e2682d..56759f8 100644
cf81640
--- a/hw/display/qxl.c
cf81640
+++ b/hw/display/qxl.c
cf81640
@@ -1000,6 +1000,7 @@ static int interface_client_monitors_config(QXLInstance *sin,
cf81640
     QXLRom *rom = memory_region_get_ram_ptr(&qxl->rom_bar);
cf81640
     int i;
cf81640
     unsigned max_outputs = ARRAY_SIZE(rom->client_monitors_config.heads);
cf81640
+    bool config_changed = false;
cf81640
 
cf81640
     if (qxl->revision < 4) {
cf81640
         trace_qxl_client_monitors_config_unsupported_by_device(qxl->id,
cf81640
@@ -1030,6 +1031,21 @@ static int interface_client_monitors_config(QXLInstance *sin,
cf81640
     }
cf81640
 #endif
cf81640
 
cf81640
+    if (rom->client_monitors_config.count != MIN(monitors_config->num_of_monitors, max_outputs)) {
cf81640
+        config_changed = true;
cf81640
+    }
cf81640
+    for (i = 0 ; i < rom->client_monitors_config.count ; ++i) {
cf81640
+        VDAgentMonConfig *monitor = &monitors_config->monitors[i];
cf81640
+        QXLURect *rect = &rom->client_monitors_config.heads[i];
cf81640
+        /* monitor->depth ignored */
cf81640
+        if ((rect->left != monitor->x) ||
cf81640
+            (rect->top != monitor->y)  ||
cf81640
+            (rect->right != monitor->x + monitor->width) ||
cf81640
+            (rect->bottom != monitor->y + monitor->height)) {
cf81640
+            config_changed = true;
cf81640
+        }
cf81640
+    }
cf81640
+
cf81640
     memset(&rom->client_monitors_config, 0,
cf81640
            sizeof(rom->client_monitors_config));
cf81640
     rom->client_monitors_config.count = monitors_config->num_of_monitors;
cf81640
@@ -1059,7 +1075,9 @@ static int interface_client_monitors_config(QXLInstance *sin,
cf81640
     trace_qxl_interrupt_client_monitors_config(qxl->id,
cf81640
                         rom->client_monitors_config.count,
cf81640
                         rom->client_monitors_config.heads);
cf81640
-    qxl_send_events(qxl, QXL_INTERRUPT_CLIENT_MONITORS_CONFIG);
cf81640
+    if (config_changed) {
cf81640
+        qxl_send_events(qxl, QXL_INTERRUPT_CLIENT_MONITORS_CONFIG);
cf81640
+    }
cf81640
     return 1;
cf81640
 }
cf81640