From 96924553f04981570022a136ce9b576190a44ffc Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 13 Mar 2020 18:00:46 -0700 Subject: [PATCH] linux-capture: Use RandR monitors for screen information RandR has two sets of screen geometry information: 1. CRTC. These are the physical scanout engines in the hardware 2. Monitors. These are the logical partitions of the screen. By default, each CRTC gets mapped to a Monitor. However, some monitors actually require two CRTCs to drive them due to limitations in the scanout hardware. Users can also create 'virtual' monitors to support VNC or other systems. This patch makes the RandR code prefer the Monitor mechanism to the older CRTC mechanism. If the server doesn't support a new enough RandR version, the existing CRTC code is used instead. The name of the monitor is also provided in place of the arbitrary number to help users select the desired source. Signed-off-by: Keith Packard --- plugins/linux-capture/xhelpers.c | 78 +++++++++++++++++++++++++++++- plugins/linux-capture/xhelpers.h | 2 +- plugins/linux-capture/xshm-input.c | 22 ++++++--- 3 files changed, 94 insertions(+), 8 deletions(-) diff --git a/plugins/linux-capture/xhelpers.c b/plugins/linux-capture/xhelpers.c index a6e88842a..aa76f047d 100644 --- a/plugins/linux-capture/xhelpers.c +++ b/plugins/linux-capture/xhelpers.c @@ -104,6 +104,22 @@ bool randr_is_active(xcb_connection_t *xcb) return true; } +static bool randr_has_monitors(xcb_connection_t *xcb) +{ + xcb_randr_query_version_cookie_t ver_c; + xcb_randr_query_version_reply_t *ver_r; + + ver_c = xcb_randr_query_version(xcb, XCB_RANDR_MAJOR_VERSION, + XCB_RANDR_MINOR_VERSION); + ver_r = xcb_randr_query_version_reply(xcb, ver_c, 0); + if (!ver_r) + return 0; + + bool ret = ver_r->major_version > 1 || ver_r->minor_version >= 5; + free(ver_r); + return ret; +} + int randr_screen_count(xcb_connection_t *xcb) { if (!xcb) @@ -111,6 +127,19 @@ int randr_screen_count(xcb_connection_t *xcb) xcb_screen_t *screen; screen = xcb_setup_roots_iterator(xcb_get_setup(xcb)).data; + if (randr_has_monitors(xcb)) { + xcb_randr_get_monitors_cookie_t mon_c; + xcb_randr_get_monitors_reply_t *mon_r; + + mon_c = xcb_randr_get_monitors(xcb, screen->root, true); + mon_r = xcb_randr_get_monitors_reply(xcb, mon_c, 0); + if (!mon_r) + return 0; + + int count = xcb_randr_get_monitors_monitors_length(mon_r); + free(mon_r); + return count; + } xcb_randr_get_screen_resources_cookie_t res_c; xcb_randr_get_screen_resources_reply_t *res_r; @@ -124,11 +153,58 @@ int randr_screen_count(xcb_connection_t *xcb) int randr_screen_geo(xcb_connection_t *xcb, int_fast32_t screen, int_fast32_t *x, int_fast32_t *y, int_fast32_t *w, - int_fast32_t *h, xcb_screen_t **rscreen) + int_fast32_t *h, xcb_screen_t **rscreen, char **name) { xcb_screen_t *xscreen; xscreen = xcb_setup_roots_iterator(xcb_get_setup(xcb)).data; + if (randr_has_monitors(xcb)) { + xcb_randr_get_monitors_cookie_t mon_c; + xcb_randr_get_monitors_reply_t *mon_r; + + mon_c = xcb_randr_get_monitors(xcb, xscreen->root, true); + mon_r = xcb_randr_get_monitors_reply(xcb, mon_c, 0); + if (!mon_r) + return 0; + + int monitors = xcb_randr_get_monitors_monitors_length(mon_r); + if (screen < 0 || screen >= monitors) { + free(mon_r); + goto fail; + } + + xcb_randr_monitor_info_iterator_t mon_i; + mon_i = xcb_randr_get_monitors_monitors_iterator(mon_r); + + int s; + for (s = 0; s < screen; s++) + xcb_randr_monitor_info_next(&mon_i); + + xcb_randr_monitor_info_t *mon = mon_i.data; + + *x = mon->x; + *y = mon->y; + *w = mon->width; + *h = mon->height; + if (rscreen) + *rscreen = xscreen; + + if (mon->name && name) { + xcb_get_atom_name_cookie_t atom_c; + xcb_get_atom_name_reply_t *atom_r; + + atom_c = xcb_get_atom_name(xcb, mon->name); + atom_r = xcb_get_atom_name_reply(xcb, atom_c, 0); + if (atom_r) { + *name = strndup( + xcb_get_atom_name_name(atom_r), + xcb_get_atom_name_name_length(atom_r)); + free(atom_r); + } + } + free(mon_r); + return 0; + } xcb_randr_get_screen_resources_cookie_t res_c; xcb_randr_get_screen_resources_reply_t *res_r; diff --git a/plugins/linux-capture/xhelpers.h b/plugins/linux-capture/xhelpers.h index ffc3f8b3a..7908c5ce2 100644 --- a/plugins/linux-capture/xhelpers.h +++ b/plugins/linux-capture/xhelpers.h @@ -94,7 +94,7 @@ int randr_screen_count(xcb_connection_t *xcb); */ int randr_screen_geo(xcb_connection_t *xcb, int_fast32_t screen, int_fast32_t *x, int_fast32_t *y, int_fast32_t *w, - int_fast32_t *h, xcb_screen_t **rscreen); + int_fast32_t *h, xcb_screen_t **rscreen, char **name); /** * Get screen geometry for a X11 screen diff --git a/plugins/linux-capture/xshm-input.c b/plugins/linux-capture/xshm-input.c index 70c3fc06f..3f60a15da 100644 --- a/plugins/linux-capture/xshm-input.c +++ b/plugins/linux-capture/xshm-input.c @@ -104,7 +104,7 @@ static int_fast32_t xshm_update_geometry(struct xshm_data *data) if (data->use_randr) { if (randr_screen_geo(data->xcb, data->screen_id, &data->x_org, &data->y_org, &data->width, &data->height, - &data->xcb_screen) < 0) { + &data->xcb_screen, NULL) < 0) { return -1; } } else if (data->use_xinerama) { @@ -308,21 +308,31 @@ static bool xshm_server_changed(obs_properties_t *props, obs_property_t *p, : xcb_setup_roots_length(xcb_get_setup(xcb)); for (int_fast32_t i = 0; i < count; ++i) { + char *name; + char name_tmp[12]; int_fast32_t x, y, w, h; x = y = w = h = 0; + name = NULL; if (randr) - randr_screen_geo(xcb, i, &x, &y, &w, &h, NULL); + randr_screen_geo(xcb, i, &x, &y, &w, &h, NULL, &name); else if (xinerama) xinerama_screen_geo(xcb, i, &x, &y, &w, &h); else x11_screen_geo(xcb, i, &w, &h); + if (name == NULL) { + sprintf(name_tmp, "%" PRIuFAST32, i); + name = name_tmp; + } + dstr_printf(&screen_info, - "Screen %" PRIuFAST32 " (%" PRIuFAST32 - "x%" PRIuFAST32 " @ %" PRIuFAST32 ",%" PRIuFAST32 - ")", - i, w, h, x, y); + "Screen %s (%" PRIuFAST32 "x%" PRIuFAST32 + " @ %" PRIuFAST32 ",%" PRIuFAST32 ")", + name, w, h, x, y); + + if (name != name_tmp) + free(name); if (h > 0 && w > 0) obs_property_list_add_int(screens, screen_info.array, -- GitLab