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@xxxxxxxxxx> --- 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 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel