From d553333da871cbbc16dd3cb9ed85babf5afbd5cd Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Wed, 26 Jun 2024 21:02:30 +0300 Subject: [PATCH] frontend: Share a native output using 'mirror-of' keyword This would allow to screen-share a particular output like the following: [output] name=vnc same-as=DP-5 [output] name=rdp-0 same-as=DP-4 [output] name=pipewire same-as=eDP-1 Both 'vnc', 'pipewire' 'rdp-0' remote outputs would then be a screen-share 'DP-5', respectively, 'e-DP1', or the 'DP-4' DRM output. Currently, this is intended only for VNC, RDP and PipeWire remote outputs. This patch exports weston_output_set_position(), and uses that for overlapping a remote output with a native DRM one, rather than using weston_output_move() as that has a side-effect when reflowing outputs from shells. Further more creating this remote output is driven entirely by compositor signal events such that enabling an DRM native output would enable the remote output, while disabling the native would have the same outcome for the remote one. Signed-off-by: Marius Vlad --- frontend/main.c | 248 ++++++++++++++++++++++++++++++++++ include/libweston/libweston.h | 8 ++ libweston/compositor.c | 6 +- 3 files changed, 257 insertions(+), 5 deletions(-) diff --git a/frontend/main.c b/frontend/main.c index 527ff161a..e33e7c989 100644 --- a/frontend/main.c +++ b/frontend/main.c @@ -151,6 +151,7 @@ struct wet_compositor { bool use_color_manager; bool drm_backend_loaded; struct wl_listener screenshot_auth; + struct wl_listener output_created_listener; enum require_outputs require_outputs; }; @@ -1681,6 +1682,135 @@ allow_content_protection(struct weston_output *output, weston_output_allow_protection(output, allow_hdcp); } +static int +wet_config_find_output_mirror(struct weston_output *output, + struct wet_compositor *wet, + char **mirror_key_value, + char **mirror_output_name, + struct weston_config_section **section) +{ + const char *section_name; + int ret = 0; + + while (weston_config_next_section(wet->config, section, §ion_name)) { + char *output_name = NULL; + char *mirror_of_key = NULL; + + /* ignore sections we're not interested in */ + if (strcmp(section_name, "output")) + continue; + + weston_config_section_get_string(*section, "mirror-of", + &mirror_of_key, NULL); + + /* ignore outputs which do not have the mirror of key */ + if (!mirror_of_key) + continue; + + *mirror_key_value = mirror_of_key; + weston_config_section_get_string(*section, "name", + &output_name, NULL); + + if (output_name) { + *mirror_output_name = output_name; + goto out; + } + + free(output_name); + } + + ret = -1; +out: + return ret; +} + +static struct weston_head * +wet_head_find_by_name(struct wet_compositor *wet, const char *name) +{ + struct weston_head *it = NULL; + struct weston_head *head_found = NULL; + + while ((it = weston_compositor_iterate_heads(wet->compositor, it))) { + if (!strcmp(it->name, name)) { + head_found = it; + break; + } + } + + return head_found; +} + + +static struct wet_backend * +wet_get_backend_from_head(struct wet_compositor *wet, struct weston_head *head) +{ + struct wet_backend *b = NULL; + wl_list_for_each(b, &wet->backend_list, compositor_link) + if (b->backend == head->backend) + return b; + + return NULL; +} + +static struct weston_head * +wet_config_find_head_to_mirror(struct weston_output *output, + struct wet_compositor *wet) +{ + struct weston_head *head = NULL; + struct weston_config_section *section = NULL; + + do { + char *mof_name = NULL; + char *remote_output_name = NULL; + + /* do we have a mirror-of key at all? */ + if (wet_config_find_output_mirror(output, wet, &mof_name, + &remote_output_name, + §ion)) + break; + + assert(mof_name); + + /* do we have a matching output between signal event and the + * output to mirror ? */ + if (strcmp(mof_name, output->name)) { + free(mof_name); + free(remote_output_name); + continue; + } + + /* grab the output name of this 'remote_output_name' */ + head = wet_head_find_by_name(wet, remote_output_name); + + free(mof_name); + free(remote_output_name); + } while (!head); + + return head; +} + +static bool +wet_config_head_has_mirror_of_entry(struct wet_compositor *wet, char *head_name) +{ + struct weston_config_section *section; + + section = weston_config_get_section(wet->config, "output", "name", head_name); + + if (section) { + char *mirror_of_key; + + weston_config_section_get_string(section, "mirror-of", + &mirror_of_key, NULL); + + if (mirror_of_key) { + free(mirror_of_key); + return true; + } + } + + return false; +} + static void parse_simple_mode(struct weston_output *output, struct weston_config_section *section, int *width, @@ -1874,8 +2004,21 @@ simple_head_enable(struct wet_compositor *wet, struct wet_backend *wb, wet_head_additional_setup wet_head_post_enable) { struct weston_output *output; + enum weston_compositor_backend backend_type; int ret = 0; + backend_type = weston_get_backend_type(head->backend); + + /* remote type of outputs: RDP/VNC/PipeWire that mirror out + * a native one will be handled automatically with the help + * of compositor outputs signals */ + if ((backend_type == WESTON_BACKEND_RDP || + backend_type == WESTON_BACKEND_VNC || + backend_type == WESTON_BACKEND_PIPEWIRE) && + wet_config_head_has_mirror_of_entry(wet, head->name) && + !head_to_mirror) + return; + output = weston_compositor_create_output(wet->compositor, head, head->name); if (!output) { @@ -2360,14 +2503,106 @@ static void wet_output_handle_destroy(struct wl_listener *listener, void *data) { struct wet_output *output; + struct wet_compositor *wet; + struct weston_head *head = NULL; output = wl_container_of(listener, output, output_destroy_listener); assert(output->output == data); + wet = output->layoutput->compositor; + head = wet_config_find_head_to_mirror(output->output, wet); + if (head && !wet->compositor->shutting_down) { + simple_head_disable(head); + } + + output->output = NULL; wl_list_remove(&output->output_destroy_listener.link); } +static void +wet_output_overlap_pre_enable(struct weston_head *head, + struct weston_head *head_to_mirror) +{ + weston_output_set_position(head->output, head_to_mirror->output->pos); +} + +static void +wet_output_compute_output_from_mirror(struct weston_output *output, + struct weston_output *mirror, + struct weston_mode *mode, + int *scale) +{ + mode->width = output->native_mode_copy.width / + mirror->current_scale; + + mode->height = output->native_mode_copy.height / + mirror->current_scale; + + mode->refresh = output->native_mode_copy.refresh; + *scale = output->current_scale; +} + +/* + * "A" is being a mirror-of output "B" then: + * + * - "A" defaults to scale=1, but the [output] section may define another + * scale, or the remote backend may provide the scale from the client. + * - The resolution of "A" is determined from the desktop area of "B" and the + * output scale of "A". + */ +static void +wet_output_overlap_post_enable(struct weston_head *head, + struct weston_head *head_to_mirror) +{ + struct weston_mode mode; + int scale = 1; + + wet_output_compute_output_from_mirror(head_to_mirror->output, + head->output, &mode, &scale); + + weston_log("Setting modeline to output '%s' to %dx%d, scale: %d\n", + head->name, mode.width, mode.height, scale); + + weston_output_mode_set_native(head->output, &mode, scale); +} + +static void +wet_output_handle_create(struct wl_listener *listener, void *data) +{ + struct wet_compositor *wet = + container_of(listener, struct wet_compositor, output_created_listener); + struct weston_output *output = data; + struct weston_head *head = NULL; + struct weston_head *head_to_mirror = + weston_output_get_first_head(output); + + struct wet_backend *wb; + + /* just ignore events from other remote backends */ + switch (weston_get_backend_type(output->backend)) { + case WESTON_BACKEND_RDP: + case WESTON_BACKEND_VNC: + case WESTON_BACKEND_PIPEWIRE: + return; + default: + break; + } + + head = wet_config_find_head_to_mirror(output, wet); + if (!head) + return; + + wb = wet_get_backend_from_head(wet, head); + assert(wb); + + simple_head_enable(wet, wb, head, head_to_mirror, + wet_output_overlap_pre_enable, + wet_output_overlap_post_enable); + weston_head_reset_device_changed(head); + +} + static struct wet_output * wet_layoutput_create_output_with_head(struct wet_layoutput *lo, const char *name, @@ -3944,6 +4179,15 @@ load_backends(struct weston_compositor *ec, const char *backends, return 0; } +static void +wet_handle_mirror_outputs(struct wet_compositor *wet) +{ + wet->output_created_listener.notify = wet_output_handle_create; + + wl_signal_add(&wet->compositor->output_created_signal, + &wet->output_created_listener); +} + static char * copy_command_line(int argc, char * const argv[]) { @@ -4358,6 +4602,8 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data) if (weston_compositor_backends_loaded(wet.compositor) < 0) goto out; + wet_handle_mirror_outputs(&wet); + if (test_data && !check_compositor_capabilities(wet.compositor, test_data->test_quirks.required_capabilities)) { ret = WET_MAIN_RET_MISSING_CAPS; @@ -4492,6 +4738,8 @@ out: if (wet_xwl) wet_xwayland_destroy(wet.compositor, wet_xwl); + if (wet.output_created_listener.notify) + wl_list_remove(&wet.output_created_listener.link); weston_compositor_destroy(wet.compositor); wet_compositor_destroy_layout(&wet); weston_log_scope_destroy(protocol_scope); diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 35c420073..137ad172c 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -2820,6 +2820,14 @@ weston_compositor_add_screenshot_authority(struct weston_compositor *compositor, int weston_compositor_backends_loaded(struct weston_compositor *compositor); +void +weston_output_set_position(struct weston_output *output, + struct weston_coord_global pos); + +int +weston_output_mode_set_native(struct weston_output *output, + struct weston_mode *mode, + int32_t scale); #ifdef __cplusplus } #endif diff --git a/libweston/compositor.c b/libweston/compositor.c index fb5acc62b..ee1fad3ba 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -7247,10 +7247,6 @@ weston_head_get_destroy_listener(struct weston_head *head, return wl_signal_get(&head->destroy_signal, notify); } -static void -weston_output_set_position(struct weston_output *output, - struct weston_coord_global pos); - /* Move other outputs when one is resized so the space remains contiguous. */ static void weston_compositor_reflow_outputs(struct weston_compositor *compositor, @@ -7345,7 +7341,7 @@ weston_output_init_geometry(struct weston_output *output, /** * \ingroup output */ -static void +WL_EXPORT void weston_output_set_position(struct weston_output *output, struct weston_coord_global pos) {