[Spice-devel] [RFC PATCH vd_agent v2 20/20] vdagent: Use guest_output_id from VDAgentMonitorsConfigV2

Lukáš Hrázký lhrazky at redhat.com
Thu Aug 16 16:26:49 UTC 2018


Instead of relying on the order, the VDAgentMonitorsConfigV2 message
contains the guest_output_id field, which identifies the output head to
which the config belongs.

Update the monitors_config algorithm to use the guest_output_id instead
of the index. Any output that does not have it's monitors_config with
it's guest_output_id in the list is disabled. The order of items in the
list no longer matters, the guest_output_id is used now.

Signed-off-by: Lukáš Hrázký <lhrazky at redhat.com>
---
 src/vdagent/x11-randr.c | 138 +++++++++++++++++++++++++---------------
 1 file changed, 86 insertions(+), 52 deletions(-)

diff --git a/src/vdagent/x11-randr.c b/src/vdagent/x11-randr.c
index 1c33c86..e2b02a9 100644
--- a/src/vdagent/x11-randr.c
+++ b/src/vdagent/x11-randr.c
@@ -629,19 +629,28 @@ static int enabled_monitors(VDAgentMonitorsConfigV2 *mon)
 static int same_monitor_configs(VDAgentMonitorsConfigV2 *conf1,
                                 VDAgentMonitorsConfigV2 *conf2)
 {
-    int i;
-
     if (conf1 == NULL || conf2 == NULL ||
             conf1->num_of_monitors != conf2->num_of_monitors)
         return 0;
 
-    for (i = 0; i < conf1->num_of_monitors; i++) {
+    for (size_t i = 0; i < conf1->num_of_monitors; i++) {
         VDAgentMonConfigV2 *mon1 = &conf1->monitors[i];
-        VDAgentMonConfigV2 *mon2 = &conf2->monitors[i];
-        /* NOTE: we don't compare depth. */
-        if (mon1->x != mon2->x || mon1->y != mon2->y ||
-               mon1->width != mon2->width || mon1->height != mon2->height)
+        int found = 0;
+
+        for (size_t j = 0; j < conf1->num_of_monitors; j++) {
+            VDAgentMonConfigV2 *mon2 = &conf2->monitors[j];
+            /* NOTE: we don't compare depth. */
+            if (mon1->guest_output_id == mon2->guest_output_id &&
+                mon1->x == mon2->x && mon1->y == mon2->y &&
+                mon1->width == mon2->width && mon1->height == mon2->height) {
+                found = 1;
+                break;
+            }
+        }
+
+        if (!found) {
             return 0;
+        }
     }
     return 1;
 }
@@ -688,6 +697,7 @@ static VDAgentMonitorsConfigV2 *get_current_mon_config(struct vdagent_x11 *x11)
         mon_config->monitors[i].y      = crtc->y;
         mon_config->monitors[i].width  = mode->width;
         mon_config->monitors[i].height = mode->height;
+        mon_config->monitors[i].guest_output_id = i;
         num_of_monitors = i + 1;
     }
     mon_config->num_of_monitors = num_of_monitors;
@@ -713,8 +723,8 @@ static void dump_monitors_config(struct vdagent_x11 *x11,
         m = &mon_config->monitors[i];
         if (!monitor_enabled(m))
             continue;
-        syslog(LOG_DEBUG, "    monitor %d, config %dx%d+%d+%d",
-               i, m->width, m->height, m->x, m->y);
+        syslog(LOG_DEBUG, "    monitor guest_output_id: %d, config %dx%d+%d+%d",
+               m->guest_output_id, m->width, m->height, m->x, m->y);
     }
 }
 
@@ -733,7 +743,7 @@ void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
                                     int fallback)
 {
     int primary_w, primary_h;
-    int i, real_num_of_monitors = 0;
+    int i;
     VDAgentMonitorsConfigV2 *curr = NULL;
 
     if (!x11->has_xrandr)
@@ -748,43 +758,63 @@ void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
         dump_monitors_config(x11, mon_config, "from guest");
     }
 
-    for (i = 0; i < mon_config->num_of_monitors; i++) {
-        if (monitor_enabled(&mon_config->monitors[i]))
-            real_num_of_monitors = i + 1;
-    }
-    mon_config->num_of_monitors = real_num_of_monitors;
+    // a boolean mask of present monitor configs for given ID (index in the array)
+    uint8_t presence_mask[MONITOR_SIZE_COUNT] = { 0 };
+
+    // Filter the incoming monitor configs, removing those with an invalid
+    // guest_output_id, so that from now on we have a clean list of configs to
+    // work with.
+    VDAgentMonitorsConfigV2 *filtered_mc = g_malloc(config_size(mon_config->num_of_monitors));
+    filtered_mc->num_of_monitors = 0;
+    filtered_mc->flags = mon_config->flags;
 
     update_randr_res(x11, 0);
-    if (mon_config->num_of_monitors > x11->randr.res->noutput) {
-        syslog(LOG_WARNING,
-               "warning unexpected client request: #mon %d > driver output %d",
-               mon_config->num_of_monitors, x11->randr.res->noutput);
-        mon_config->num_of_monitors = x11->randr.res->noutput;
+
+    for (i = 0; i < mon_config->num_of_monitors; i++) {
+        if (mon_config->monitors[i].guest_output_id >= x11->randr.res->noutput) {
+            syslog(LOG_WARNING, "Received monitor config guest_output_id %d"
+                   " > driver max output nr %d, skipping",
+                   mon_config->monitors[i].guest_output_id, x11->randr.res->noutput - 1);
+            continue;
+        }
+
+        if (mon_config->monitors[i].guest_output_id >= MONITOR_SIZE_COUNT) {
+            syslog(LOG_WARNING, "Received monitor config guest_output_id %d"
+                   " > monitor count limit %d, skipping",
+                   mon_config->monitors[i].guest_output_id, MONITOR_SIZE_COUNT - 1);
+            continue;
+        }
+
+        memcpy(&filtered_mc->monitors[filtered_mc->num_of_monitors],
+               &mon_config->monitors[i],
+               sizeof(VDAgentMonConfigV2));
+
+        presence_mask[mon_config->monitors[i].guest_output_id] = 1;
+
+        filtered_mc->num_of_monitors++;
     }
 
-    if (mon_config->num_of_monitors > MONITOR_SIZE_COUNT) {
-        syslog(LOG_WARNING, "warning: client send %d monitors, capping at %d",
-               mon_config->num_of_monitors, MONITOR_SIZE_COUNT);
-        mon_config->num_of_monitors = MONITOR_SIZE_COUNT;
+    if (x11->debug) {
+        dump_monitors_config(x11, filtered_mc, "filtered");
     }
 
-    zero_base_monitors(x11, mon_config, &primary_w, &primary_h);
+    zero_base_monitors(x11, filtered_mc, &primary_w, &primary_h);
 
     constrain_to_screen(x11, &primary_w, &primary_h);
 
     if (x11->debug) {
-        dump_monitors_config(x11, mon_config, "after zeroing");
+        dump_monitors_config(x11, filtered_mc, "after zeroing");
     }
 
     curr = get_current_mon_config(x11);
     if (!curr)
         goto exit;
-    if (same_monitor_configs(mon_config, curr) &&
+    if (same_monitor_configs(filtered_mc, curr) &&
            x11->width[0] == primary_w && x11->height[0] == primary_h) {
         goto exit;
     }
 
-    if (same_monitor_configs(mon_config, x11->randr.failed_conf)) {
+    if (same_monitor_configs(filtered_mc, x11->randr.failed_conf)) {
         syslog(LOG_WARNING, "Ignoring previous failed client monitor config");
         goto exit;
     }
@@ -793,16 +823,20 @@ void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
     g_unlink(config);
     g_free(config);
 
-    for (i = mon_config->num_of_monitors; i < x11->randr.res->noutput; i++)
-        xrandr_disable_output(x11, i);
-
-    /* First, disable disabled CRTCs... */
-    for (i = 0; i < mon_config->num_of_monitors; ++i) {
-        if (!monitor_enabled(&mon_config->monitors[i])) {
+    // disable all outputs which we don't have a config for
+    for (i = 0; i < MIN(sizeof(presence_mask), x11->randr.res->noutput); i++) {
+        if (!presence_mask[i]) {
             xrandr_disable_output(x11, i);
         }
     }
 
+    // disable outputs that are disabled in the monitor configs
+    for (i = 0; i < filtered_mc->num_of_monitors; ++i) {
+        if (!monitor_enabled(&filtered_mc->monitors[i])) {
+            xrandr_disable_output(x11, filtered_mc->monitors[i].guest_output_id);
+        }
+    }
+
     /* ... and disable the ones that would be bigger than
      * the new RandR screen once it is resized. If they are enabled the
      * XRRSetScreenSize call will fail with BadMatch. They will be
@@ -820,9 +854,10 @@ void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
         if ((x + width > primary_w) || (y + height > primary_h)) {
             if (x11->debug)
                 syslog(LOG_DEBUG, "Disabling monitor %d: %dx%d+%d+%d > (%d,%d)",
-                       i, width, height, x, y, primary_w, primary_h);
+                       curr->monitors[i].guest_output_id,
+                       width, height, x, y, primary_w, primary_h);
 
-            xrandr_disable_output(x11, i);
+            xrandr_disable_output(x11, curr->monitors[i].guest_output_id);
         }
     }
 
@@ -848,10 +883,10 @@ void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
                    fullscreen it will keep sending the failing config. */
                 free(x11->randr.failed_conf);
                 x11->randr.failed_conf =
-                    malloc(config_size(mon_config->num_of_monitors));
+                    malloc(config_size(filtered_mc->num_of_monitors));
                 if (x11->randr.failed_conf)
-                    memcpy(x11->randr.failed_conf, mon_config,
-                           config_size(mon_config->num_of_monitors));
+                    memcpy(x11->randr.failed_conf, filtered_mc,
+                           config_size(filtered_mc->num_of_monitors));
                 return;
             }
         }
@@ -859,34 +894,33 @@ void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
 
     /* Finally, we set the new resolutions on RandR CRTCs now that the
      * RandR screen is big enough to hold these.  */
-    for (i = 0; i < mon_config->num_of_monitors; ++i) {
+    for (i = 0; i < filtered_mc->num_of_monitors; ++i) {
         int width, height;
         int x, y;
 
-        if (!monitor_enabled(&mon_config->monitors[i])) {
+        if (!monitor_enabled(&filtered_mc->monitors[i])) {
             continue;
         }
         /* Try to create the requested resolution */
-        width = mon_config->monitors[i].width;
-        height = mon_config->monitors[i].height;
-        x = mon_config->monitors[i].x;
-        y = mon_config->monitors[i].y;
+        width = filtered_mc->monitors[i].width;
+        height = filtered_mc->monitors[i].height;
+        x = filtered_mc->monitors[i].x;
+        y = filtered_mc->monitors[i].y;
 
         if (x11->debug) {
             syslog(LOG_DEBUG, "Setting resolution for monitor %d: %dx%d+%d+%d)",
-                   i, width, height, x, y);
+                   filtered_mc->monitors[i].guest_output_id, width, height, x, y);
         }
 
-        if (!xrandr_add_and_set(x11, i, x, y, width, height) &&
-                enabled_monitors(mon_config) == 1) {
-            set_screen_to_best_size(x11, width, height,
-                                    &primary_w, &primary_h);
+        if (!xrandr_add_and_set(x11, filtered_mc->monitors[i].guest_output_id, x, y, width, height) &&
+            enabled_monitors(filtered_mc) == 1)
+        {
+            set_screen_to_best_size(x11, width, height, &primary_w, &primary_h);
             break;
         }
     }
 
-    update_randr_res(x11,
-        x11->randr.num_monitors != enabled_monitors(mon_config));
+    update_randr_res(x11, x11->randr.num_monitors != enabled_monitors(filtered_mc));
     x11->width[0] = primary_w;
     x11->height[0] = primary_h;
 
-- 
2.18.0



More information about the Spice-devel mailing list