Add a display_id field to the structure that we use to send down the list of guest display resolutions to the vdagentd daemon. This allows us to map the spice display id to the proper X display for determining mouse locations, etc.
In the case where we have an mjpeg plugin running in the streaming agent, we have two spice displays representing the same guest display. In that scenario, the session agent may maintain the following guest output mapping: spice channel 0 (QXL) => X output 0 spice channel 1 (streaming-agent) => X output 0 While this is not necessarily a supported scenario, it would be nice if the cursor input worked properly in this case. The root problem is that when the session agent sends down the guest xorg resolutions to the system daemon, it simply loops through the list of xorg displays, and for each X display it looks up the first spice display ID associated with it and sends that down to the daemon. In the scenario mentioned above, since there is only a single X display configured (albeit represented by two different spice displays), we would send down a single display resolution to the system daemon: - { width=1280, height=1024, x=0, y=0, display_id=0 } Notice that there is no entry for display_id=1. When the agent receives a cursor input message for display channel 1, that message will get passed to the systemn daemon, which will attempt to look up display_id 1 in order to convert the event coordinates to global coordinates. Finding no entry for display_id=1, the mouse events do not work. In this patch, when we want to send the guest resolutions down to the system daemon, we still loop through the list of X outputs, but for each output we also loop through the guest output mapping table and send a resolution structure down to the daemon for each registered output mapping. This means that in the previously mentioned scenario, we would send down the following information: - { width=1280, height=1024, x=0, y=0, display_id=0 } - { width=1280, height=1024, x=0, y=0, display_id=1 } This means that when the client sends a mouse event for display_id=1, the system daemon knows the coordinates of the guest display associated with that ID and can process the mouse event properly. --- src/vdagent/x11-randr.c | 82 ++++++++++++++++++++++++++++------------- src/vdagentd-proto.h | 1 + src/vdagentd/uinput.c | 23 +++++++++--- 3 files changed, 76 insertions(+), 30 deletions(-) diff --git a/src/vdagent/x11-randr.c b/src/vdagent/x11-randr.c index 8ff5a76..88857c8 100644 --- a/src/vdagent/x11-randr.c +++ b/src/vdagent/x11-randr.c @@ -689,7 +689,6 @@ static int config_size(int num_of_monitors) num_of_monitors * sizeof(VDAgentMonConfig); } - // gets monitor information about the specified output index and returns true if there was no error static bool get_monitor_info_for_output_index(struct vdagent_x11 *x11, int output_index, int *x, int *y, int *width, int *height) @@ -840,6 +839,9 @@ void vdagent_x11_handle_graphics_device_info(struct vdagent_x11 *x11, uint8_t *d device_display_info = (VDAgentDeviceDisplayInfo*) ((char*) device_display_info + sizeof(VDAgentDeviceDisplayInfo) + device_display_info->device_address_len); } + + // make sure daemon is up-to-date with (possibly updated) device IDs + vdagent_x11_send_daemon_guest_xorg_res(x11, 1); } static int get_output_index_for_display_id(struct vdagent_x11 *x11, int display_id) @@ -1067,7 +1069,7 @@ exit: void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11, int update) { - struct vdagentd_guest_xorg_resolution *res = NULL; + GArray *res_array = g_array_new(FALSE, FALSE, sizeof(struct vdagentd_guest_xorg_resolution)); int i, width = 0, height = 0, screen_count = 0; if (x11->has_xrandr) { @@ -1075,15 +1077,38 @@ void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11, int update) update_randr_res(x11, 0); screen_count = x11->randr.res->noutput; - res = g_new(struct vdagentd_guest_xorg_resolution, screen_count); for (i = 0; i < screen_count; i++) { - struct vdagentd_guest_xorg_resolution *curr = &res[i]; - if (!get_monitor_info_for_output_index(x11, i, &curr->x, &curr->y, - &curr->width, &curr->height)) { - g_free(res); + struct vdagentd_guest_xorg_resolution curr; + if (!get_monitor_info_for_output_index(x11, i, &curr.x, &curr.y, + &curr.width, &curr.height)) { + g_array_free(res_array, TRUE); goto no_info; } + if (g_hash_table_size(x11->guest_output_map) == 0) { + syslog(LOG_DEBUG, "No guest output map, using output index as display id"); + curr.display_id = i; + g_array_append_val(res_array, curr); + } else { + // There may be multiple spice outputs representing a single guest output. Send them + // all down. + RROutput output_id = x11->randr.res->outputs[i]; + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init(&iter, x11->guest_output_map); + bool found = false; + while (g_hash_table_iter_next(&iter, &key, &value)) { + gint64 *other_id = value; + if (*other_id == output_id) { + curr.display_id = GPOINTER_TO_INT(key); + g_array_append_val(res_array, curr); + found = true; + } + } + if (!found) { + syslog(LOG_WARNING, "Unable to find a display id for output index %d)", i); + } + } } width = x11->width[0]; height = x11->height[0]; @@ -1093,54 +1118,61 @@ void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11, int update) screen_info = XineramaQueryScreens(x11->display, &screen_count); if (!screen_info) goto no_info; - res = g_new(struct vdagentd_guest_xorg_resolution, screen_count); + g_array_set_size(res_array, screen_count); for (i = 0; i < screen_count; i++) { if (screen_info[i].screen_number >= screen_count) { syslog(LOG_ERR, "Invalid screen number in xinerama screen info (%d >= %d)", screen_info[i].screen_number, screen_count); XFree(screen_info); - g_free(res); + g_array_free(res_array, true); return; } - res[screen_info[i].screen_number].width = screen_info[i].width; - res[screen_info[i].screen_number].height = screen_info[i].height; - res[screen_info[i].screen_number].x = screen_info[i].x_org; - res[screen_info[i].screen_number].y = screen_info[i].y_org; + struct vdagentd_guest_xorg_resolution *curr = &g_array_index(res_array, + struct vdagentd_guest_xorg_resolution, + screen_info[i].screen_number); + curr->width = screen_info[i].width; + curr->height = screen_info[i].height; + curr->x = screen_info[i].x_org; + curr->y = screen_info[i].y_org; } XFree(screen_info); width = x11->width[0]; height = x11->height[0]; } else { no_info: - screen_count = x11->screen_count; - res = g_new(struct vdagentd_guest_xorg_resolution, screen_count); for (i = 0; i < screen_count; i++) { - res[i].width = x11->width[i]; - res[i].height = x11->height[i]; + struct vdagentd_guest_xorg_resolution res; + res.width = x11->width[i]; + res.height = x11->height[i]; /* No way to get screen coordinates, assume rtl order */ - res[i].x = width; - res[i].y = 0; + res.x = width; + res.y = 0; width += x11->width[i]; if (x11->height[i] > height) height = x11->height[i]; + g_array_append_val(res_array, res); } } if (screen_count == 0) { syslog(LOG_DEBUG, "Screen count is zero, are we on wayland?"); - g_free(res); + g_array_free(res_array, TRUE); return; } if (x11->debug) { syslog(LOG_DEBUG, "Sending guest screen resolutions to vdagentd:"); - for (i = 0; i < screen_count; i++) { - syslog(LOG_DEBUG, " screen %d %dx%d%+d%+d", i, - res[i].width, res[i].height, res[i].x, res[i].y); + if (res_array->len > screen_count) { + syslog(LOG_DEBUG, "(NOTE: list may contain overlapping areas when multiple spice displays show the same guest output)"); + } + for (i = 0; i < res_array->len; i++) { + struct vdagentd_guest_xorg_resolution *res = (struct vdagentd_guest_xorg_resolution*)res_array->data; + syslog(LOG_DEBUG, " screen %d %dx%d%+d%+d, display_id=%d", i, + res[i].width, res[i].height, res[i].x, res[i].y, res[i].display_id); } } udscs_write(x11->vdagentd, VDAGENTD_GUEST_XORG_RESOLUTION, width, height, - (uint8_t *)res, screen_count * sizeof(*res)); - g_free(res); + (uint8_t *)res_array->data, res_array->len * sizeof(struct vdagentd_guest_xorg_resolution)); + g_array_free(res_array, TRUE); } diff --git a/src/vdagentd-proto.h b/src/vdagentd-proto.h index 0bc2689..d64be17 100644 --- a/src/vdagentd-proto.h +++ b/src/vdagentd-proto.h @@ -53,6 +53,7 @@ struct vdagentd_guest_xorg_resolution { int height; int x; int y; + int display_id; }; #endif diff --git a/src/vdagentd/uinput.c b/src/vdagentd/uinput.c index 4f854bf..ff37e1e 100644 --- a/src/vdagentd/uinput.c +++ b/src/vdagentd/uinput.c @@ -175,6 +175,18 @@ static void uinput_send_event(struct vdagentd_uinput **uinputp, } } +static struct vdagentd_guest_xorg_resolution* lookup_screen_info(struct vdagentd_uinput *uinput, int display_id) +{ + int i; + for (i = 0; i < uinput->screen_count; i++) { + if (uinput->screen_info[i].display_id == display_id) { + return &uinput->screen_info[i]; + } + } + syslog(LOG_WARNING, "Unable to find output index for display id %d", display_id); + return NULL; +} + void vdagentd_uinput_do_mouse(struct vdagentd_uinput **uinputp, VDAgentMouseState *mouse) { @@ -196,16 +208,17 @@ void vdagentd_uinput_do_mouse(struct vdagentd_uinput **uinputp, int i, down; if (*uinputp) { - if (mouse->display_id >= uinput->screen_count) { - syslog(LOG_WARNING, "mouse event for unknown monitor (%d >= %d)", - mouse->display_id, uinput->screen_count); + struct vdagentd_guest_xorg_resolution *screen_info = lookup_screen_info(uinput, mouse->display_id); + if (screen_info == NULL) { + syslog(LOG_WARNING, "mouse event for unknown monitor %d", + mouse->display_id); return; } if (uinput->debug) syslog(LOG_DEBUG, "mouse-event: mon %d %dx%d", mouse->display_id, mouse->x, mouse->y); - mouse->x += uinput->screen_info[mouse->display_id].x; - mouse->y += uinput->screen_info[mouse->display_id].y; + mouse->x += screen_info->x; + mouse->y += screen_info->y; #ifdef WITH_STATIC_UINPUT mouse->x = mouse->x * 32767 / (uinput->width - 1); mouse->y = mouse->y * 32767 / (uinput->height - 1); -- 2.17.2 _______________________________________________ Spice-devel mailing list Spice-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/spice-devel