diff options
author | Julien Ropé <jrope@redhat.com> | 2020-03-24 16:42:16 +0100 |
---|---|---|
committer | Julien Ropé <jrope@redhat.com> | 2020-10-19 17:58:43 +0200 |
commit | d92f37ae06aafa77b8bc27a458c449c28d0d5f09 (patch) | |
tree | 89fd16fee0f80fcd75d1316382185ef8feb5143c | |
parent | f59192a021ffe5a59eba850ccef9c389681579e5 (diff) |
Prepare mapping based on connector name.
Split the lookup_xrandr_output_for_device_info to access the part
retrieving the expected connector name.
Call that separate function in the "non-X11" part of the code, and use a
hashtable to map Spice display ID to its expected connector name.
This hashtable will later be used when we need to report resolution
changes to the daemon/server.
Signed-off-by: Julien Ropé <jrope@redhat.com>
Acked-by: Frediano Ziglio <fziglio@redhat.com>
-rw-r--r-- | src/vdagent/device-info.c | 167 | ||||
-rw-r--r-- | src/vdagent/device-info.h | 4 | ||||
-rw-r--r-- | src/vdagent/display.c | 37 |
3 files changed, 139 insertions, 69 deletions
diff --git a/src/vdagent/device-info.c b/src/vdagent/device-info.c index 6b0e28f..8cf72c9 100644 --- a/src/vdagent/device-info.c +++ b/src/vdagent/device-info.c @@ -388,12 +388,14 @@ static char* find_device_at_pci_address(PciAddress *pci_addr, int *vendor_id, in return NULL; } -// PCI address should be in the following format: -// pci/$domain/$slot.$fn/$slot.$fn -bool lookup_xrandr_output_for_device_info(VDAgentDeviceDisplayInfo *device_info, - Display *xdisplay, - XRRScreenResources *xres, - RROutput *output_id) + +/** + * Look up DRM info for the device, and retrieve the expected connector name. + * This name will later be compared to the monitor names found at the display manager level. + */ +int get_connector_name_for_device_info(VDAgentDeviceDisplayInfo *device_info, + char *expected_name, size_t name_size, + bool has_virtual_zero_display) { PciAddress *user_pci_addr = parse_pci_address_from_spice((char*)device_info->device_address); if (!user_pci_addr) { @@ -401,7 +403,7 @@ bool lookup_xrandr_output_for_device_info(VDAgentDeviceDisplayInfo *device_info, "Couldn't parse PCI address '%s'. " "Address should be the form 'pci/$domain/$slot.$fn/$slot.fn...", device_info->device_address); - return false; + return -1; } int vendor_id = 0; @@ -412,68 +414,106 @@ bool lookup_xrandr_output_for_device_info(VDAgentDeviceDisplayInfo *device_info, int drm_fd = open(dev_path, O_RDWR); if (drm_fd < 0) { syslog(LOG_WARNING, "Unable to open file %s", dev_path); - return false; + g_free(dev_path); + return -1; } drmModeResPtr res = drmModeGetResources(drm_fd); - if (res) { - // find the drm output that is equal to device_display_id - if (device_info->device_display_id >= res->count_connectors) { - syslog(LOG_WARNING, - "Specified display id %i is higher than the maximum display id " - "provided by this device (%i)", - device_info->device_display_id, res->count_connectors - 1); - close(drm_fd); - return false; - } + if (res == NULL) { + syslog(LOG_WARNING, + "Unable to get DRM resources for card %s. " + "Falling back to using xrandr output index.", + dev_path); + close(drm_fd); + g_free(dev_path); + return 1; // error out - actual handling is deferred to the caller + } - drmModeConnectorPtr conn = - drmModeGetConnector(drm_fd, res->connectors[device_info->device_display_id]); - drmModeFreeResources(res); - res = NULL; + // no need for dev_path anymore + g_free(dev_path); + + // find the drm output that is equal to device_display_id + if (device_info->device_display_id >= res->count_connectors) { + syslog(LOG_WARNING, + "Specified display id %i is higher than the maximum display id " + "provided by this device (%i)", + device_info->device_display_id, res->count_connectors - 1); close(drm_fd); + return -1; + } - if (conn == NULL) { - syslog(LOG_WARNING, "Unable to get drm connector for display id %i", - device_info->device_display_id); - return false; - } + drmModeConnectorPtr conn = + drmModeGetConnector(drm_fd, res->connectors[device_info->device_display_id]); + drmModeFreeResources(res); + res = NULL; + close(drm_fd); - bool decrement_name = false; - if (vendor_id == PCI_VENDOR_ID_REDHAT && device_id == PCI_DEVICE_ID_QXL) { - // Older QXL drivers numbered their outputs starting with - // 0. This contrasts with most drivers who start numbering - // outputs with 1. In this case, the expected drm connector - // name will need to be decremented before comparing to the - // xrandr output name - for (int i = 0; i < xres->noutput; ++i) { - XRROutputInfo *oinfo = XRRGetOutputInfo(xdisplay, xres, xres->outputs[i]); - if (!oinfo) { - syslog(LOG_WARNING, "Unable to lookup XRandr output info for output %li", - xres->outputs[i]); - return false; - } - if (strcmp(oinfo->name, "Virtual-0") == 0) { - decrement_name = true; - XRRFreeOutputInfo(oinfo); - break; - } - XRRFreeOutputInfo(oinfo); - } + if (conn == NULL) { + syslog(LOG_WARNING, "Unable to get drm connector for display id %i", + device_info->device_display_id); + return -1; + } + + bool decrement_name = false; + + if (vendor_id == PCI_VENDOR_ID_REDHAT && device_id == PCI_DEVICE_ID_QXL + && has_virtual_zero_display) { + decrement_name = true; + } + + // Compare the name of the xrandr output against what we would + // expect based on the drm connection type. The xrandr names + // are driver-specific, so we need to special-case some + // drivers. Most hardware these days uses the 'modesetting' + // driver, but the QXL device uses its own driver which has + // different naming conventions + if (vendor_id == PCI_VENDOR_ID_REDHAT && device_id == PCI_DEVICE_ID_QXL) { + drm_conn_name_qxl(conn, expected_name, name_size, decrement_name); + } else { + drm_conn_name_modesetting(conn, expected_name, name_size); + } + drmModeFreeConnector(conn); + + return 0; +} + +// PCI address should be in the following format: +// pci/$domain/$slot.$fn/$slot.$fn +bool lookup_xrandr_output_for_device_info(VDAgentDeviceDisplayInfo *device_info, + Display *xdisplay, + XRRScreenResources *xres, + RROutput *output_id) +{ + char expected_name[100]; + int ret; + + // Older QXL drivers numbered their outputs starting with + // 0. This contrasts with most drivers who start numbering + // outputs with 1. In this case, the expected drm connector + // name will need to be decremented before comparing to the + // xrandr output name + bool has_virtual_zero_display = false; + for (int i = 0; i < xres->noutput; ++i) { + XRROutputInfo *oinfo = XRRGetOutputInfo(xdisplay, xres, xres->outputs[i]); + if (!oinfo) { + syslog(LOG_WARNING, "Unable to lookup XRandr output info for output %li", + xres->outputs[i]); + return false; } - // Compare the name of the xrandr output against what we would - // expect based on the drm connection type. The xrandr names - // are driver-specific, so we need to special-case some - // drivers. Most hardware these days uses the 'modesetting' - // driver, but the QXL device uses its own driver which has - // different naming conventions - char expected_name[100]; - if (vendor_id == PCI_VENDOR_ID_REDHAT && device_id == PCI_DEVICE_ID_QXL) { - drm_conn_name_qxl(conn, expected_name, sizeof(expected_name), decrement_name); - } else { - drm_conn_name_modesetting(conn, expected_name, sizeof(expected_name)); + if (strcmp(oinfo->name, "Virtual-0") == 0) { + has_virtual_zero_display = true; + XRRFreeOutputInfo(oinfo); + break; } + XRRFreeOutputInfo(oinfo); + } + ret = get_connector_name_for_device_info(device_info, expected_name, sizeof(expected_name), + has_virtual_zero_display); + switch (ret) { + case -1: // generic error => exit + return false; + case 0: // Loop through xrandr outputs and check whether the xrandr // output name matches the drm connector name for (int i = 0; i < xres->noutput; ++i) { @@ -493,13 +533,8 @@ bool lookup_xrandr_output_for_device_info(VDAgentDeviceDisplayInfo *device_info, } XRRFreeOutputInfo(oinfo); } - drmModeFreeConnector(conn); - } else { - close(drm_fd); - syslog(LOG_WARNING, - "Unable to get DRM resources for card %s. " - "Falling back to using xrandr output index.", - dev_path); + break; + case 1: // no DRM info found // This is probably a proprietary driver (e.g. Nvidia) that does // not provide outputs via drm, so the only thing we can do is just // assume that it is the only device assigned to X, and use the diff --git a/src/vdagent/device-info.h b/src/vdagent/device-info.h index 8646cc5..d4d8cbd 100644 --- a/src/vdagent/device-info.h +++ b/src/vdagent/device-info.h @@ -28,3 +28,7 @@ bool lookup_xrandr_output_for_device_info(VDAgentDeviceDisplayInfo *device_info, Display *xdisplay, XRRScreenResources *xres, RROutput *output_id); + +int get_connector_name_for_device_info(VDAgentDeviceDisplayInfo *device_info, + char *expected_name, size_t name_size, + bool has_virtual_zero_display); diff --git a/src/vdagent/display.c b/src/vdagent/display.c index 2577f9c..b82db5c 100644 --- a/src/vdagent/display.c +++ b/src/vdagent/display.c @@ -25,9 +25,12 @@ #ifdef WITH_GTK #include <gdk/gdk.h> #include <gtk/gtk.h> // for GTK_CHECK_VERSION -#ifdef GDK_WINDOWING_X11 - #if ! GTK_CHECK_VERSION(3, 98, 0) - #include <gdk/gdkx.h> +#if GTK_CHECK_VERSION(3, 98, 0) + #include <gdk/wayland/gdkwayland.h> + #include <gdk/x11/gdkx.h> +#else + #ifdef GDK_WINDOWING_X11 + #include <gdk/gdkx.h> #endif #endif #endif @@ -50,6 +53,10 @@ * The x11.c and x11-randr.c files contains the x11-specific functions. */ struct VDAgentDisplay { +#ifdef USE_GTK_FOR_MONITORS + // association between SPICE display ID and expected connector name + GHashTable *connector_mapping; +#endif struct vdagent_x11 *x11; UdscsConnection *vdagentd; int debug; @@ -132,6 +139,9 @@ VDAgentDisplay* vdagent_display_create(UdscsConnection *vdagentd, int debug, int } display->x11->vdagent_display = display; +#ifdef USE_GTK_FOR_MONITORS + display->connector_mapping = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); +#endif display->x11_channel = g_io_channel_unix_new(vdagent_x11_get_fd(display->x11)); if (display->x11_channel == NULL) { @@ -167,6 +177,10 @@ void vdagent_display_destroy(VDAgentDisplay *display, int vdagentd_disconnected) return; } +#ifdef USE_GTK_FOR_MONITORS + g_hash_table_destroy(display->connector_mapping); +#endif + g_clear_pointer(&display->x11_channel, g_io_channel_unref); vdagent_x11_destroy(display->x11, vdagentd_disconnected); } @@ -227,6 +241,23 @@ void vdagent_display_handle_graphics_device_info(VDAgentDisplay *display, uint8_ device_display_info->channel_id, device_display_info->monitor_id); } +#ifdef USE_GTK_FOR_MONITORS + if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) { + // Get the expected connector name from hardware info. Store it with the SPICE display ID. + char expected_name[100]; + int ret = get_connector_name_for_device_info(device_display_info, expected_name, + sizeof(expected_name), false); + if (ret == 0) { + g_hash_table_insert(display->connector_mapping, + g_strdup(expected_name), + GUINT_TO_POINTER(device_display_info->channel_id + device_display_info->monitor_id)); + syslog(LOG_DEBUG, "Mapping connector %s to display #%d", expected_name, + (device_display_info->channel_id + device_display_info->monitor_id)); + } + } + else + // under X11, use the X11 API +#endif vdagent_x11_handle_device_display_info(display->x11, device_display_info); device_display_info = (VDAgentDeviceDisplayInfo*) ((char*) device_display_info + |