From 95e8573388db84f8c727dcdb64649c74ffcce4d8 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Thu, 7 Mar 2024 00:22:51 +0100 Subject: [PATCH] src/output.c: refactor virtual output related functions This commit moves the virtual output related functions into their own file at `src/output-virtual.c` with its own include file to reduce `include/labwc.h` bit by bit. Additionally, it removes the need to keep the `server->headless.pending_output_name` char array around by temporarily disconnecting the handler when creating a new virtual output. This allows to set the output name right in the `output_virtual_add()` call rather than to store the pending name until the new output event handler has been called. It also makes adding a virtual fallback output easier in a follow-up PR. --- include/labwc.h | 3 -- include/output-virtual.h | 12 +++++ src/action.c | 6 ++- src/meson.build | 1 + src/output-virtual.c | 102 +++++++++++++++++++++++++++++++++++++++ src/output.c | 73 +++++----------------------- 6 files changed, 132 insertions(+), 65 deletions(-) create mode 100644 include/output-virtual.h create mode 100644 src/output-virtual.c diff --git a/include/labwc.h b/include/labwc.h index 204f60e3..38fd5c9f 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -211,7 +211,6 @@ struct server { struct wlr_backend *backend; struct headless { struct wlr_backend *backend; - char pending_output_name[4096]; } headless; struct wlr_session *session; @@ -491,8 +490,6 @@ struct wlr_box output_usable_area_in_layout_coords(struct output *output); struct wlr_box output_usable_area_scaled(struct output *output); void handle_output_power_manager_set_mode(struct wl_listener *listener, void *data); -void output_add_virtual(struct server *server, const char *output_name); -void output_remove_virtual(struct server *server, const char *output_name); void output_enable_adaptive_sync(struct wlr_output *output, bool enabled); void new_tearing_hint(struct wl_listener *listener, void *data); diff --git a/include/output-virtual.h b/include/output-virtual.h new file mode 100644 index 00000000..4bceb39c --- /dev/null +++ b/include/output-virtual.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_OUTPUT_VIRTUAL_H +#define LABWC_OUTPUT_VIRTUAL_H + +struct server; +struct wlr_output; + +void output_virtual_add(struct server *server, const char *output_name, + struct wlr_output **store_wlr_output); +void output_virtual_remove(struct server *server, const char *output_name); + +#endif diff --git a/src/action.c b/src/action.c index 1f8289e0..6c026733 100644 --- a/src/action.c +++ b/src/action.c @@ -16,6 +16,7 @@ #include "debug.h" #include "labwc.h" #include "menu/menu.h" +#include "output-virtual.h" #include "placement.h" #include "regions.h" #include "ssd.h" @@ -996,14 +997,15 @@ actions_run(struct view *activator, struct server *server, { const char *output_name = action_get_str(action, "output_name", NULL); - output_add_virtual(server, output_name); + output_virtual_add(server, output_name, + /*store_wlr_output*/ NULL); } break; case ACTION_TYPE_VIRTUAL_OUTPUT_REMOVE: { const char *output_name = action_get_str(action, "output_name", NULL); - output_remove_virtual(server, output_name); + output_virtual_remove(server, output_name); } break; case ACTION_TYPE_AUTO_PLACE: diff --git a/src/meson.build b/src/meson.build index b544e99f..a71eb06b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -13,6 +13,7 @@ labwc_sources = files( 'node.c', 'osd.c', 'output.c', + 'output-virtual.c', 'placement.c', 'regions.c', 'resistance.c', diff --git a/src/output-virtual.c b/src/output-virtual.c new file mode 100644 index 00000000..72b1e068 --- /dev/null +++ b/src/output-virtual.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include "labwc.h" +#include "output-virtual.h" + +void +output_virtual_add(struct server *server, const char *output_name, + struct wlr_output **store_wlr_output) +{ + if (output_name) { + /* Prevent creating outputs with the same name */ + struct output *output; + wl_list_for_each(output, &server->outputs, link) { + if (wlr_output_is_headless(output->wlr_output) && + !strcmp(output->wlr_output->name, output_name)) { + wlr_log(WLR_DEBUG, + "refusing to create virtual output with duplicate name"); + return; + } + } + } + + /* + * The headless backend will always emit the new output signal (and + * thus call our handler) before `wlr_headless_add_output()` returns. + * + * This makes it impossible to + * - modify the output before it gets enabled in the handler + * - use a pointer of the new wlr_output within the handler + * + * So we temporarily disconnect the handler when creating the output + * and then call the handler manually. + * + * This is important because some operations like `wlr_output_set_name()` + * can only be done before the output has been enabled. + * + * If we add a virtual output before the headless backend has been started + * we may end up calling the new output handler twice, one time manually + * and one time by the headless backend when it starts up and sends the + * signal for all its configured outputs. Rather than keeping a global + * server->headless.started state around that we could check here we just + * ignore duplicated new output calls in new_output_notify(). + */ + wl_list_remove(&server->new_output.link); + + struct wlr_output *wlr_output = wlr_headless_add_output( + server->headless.backend, 1920, 1080); + + if (!wlr_output) { + wlr_log(WLR_ERROR, "Failed to create virtual output %s", + output_name ? output_name : ""); + goto restore_handler; + } + + if (output_name) { + wlr_output_set_name(wlr_output, output_name); + } + if (store_wlr_output) { + /* Ensures that we can use the new wlr_output pointer within new_output_nofity() */ + *store_wlr_output = wlr_output; + } + + /* Notify about the new output manually */ + if (server->new_output.notify) { + server->new_output.notify(&server->new_output, wlr_output); + } + +restore_handler: + /* And finally restore output notifications */ + wl_signal_add(&server->backend->events.new_output, &server->new_output); +} + +void +output_virtual_remove(struct server *server, const char *output_name) +{ + struct output *output; + wl_list_for_each(output, &server->outputs, link) { + if (!wlr_output_is_headless(output->wlr_output)) { + continue; + } + + if (output_name) { + /* + * Given virtual output name, find and + * destroy virtual output by that name. + */ + if (!strcmp(output->wlr_output->name, output_name)) { + wlr_output_destroy(output->wlr_output); + return; + } + } else { + /* + * When virtual output name was not supplied by user, + * simply destroy the first virtual output found. + */ + wlr_output_destroy(output->wlr_output); + return; + } + } +} diff --git a/src/output.c b/src/output.c index 16eee4b8..aad280e8 100644 --- a/src/output.c +++ b/src/output.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -227,15 +226,22 @@ new_output_notify(struct wl_listener *listener, void *data) struct server *server = wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; - /* Name virtual output */ - if (wlr_output_is_headless(wlr_output) && server->headless.pending_output_name[0] != '\0') { - wlr_output_set_name(wlr_output, server->headless.pending_output_name); - server->headless.pending_output_name[0] = '\0'; + struct output *output; + wl_list_for_each(output, &server->outputs, link) { + if (output->wlr_output == wlr_output) { + /* + * This is a duplicated notification. + * We may end up here when a virtual output + * was added before the headless backend was + * started up. + */ + return; + } } /* * We offer any display as available for lease, some apps like - * gamescope, want to take ownership of a display when they can + * gamescope want to take ownership of a display when they can * to use planes and present directly. * This is also useful for debugging the DRM parts of * another compositor. @@ -305,7 +311,7 @@ new_output_notify(struct wl_listener *listener, void *data) wlr_output_commit(wlr_output); - struct output *output = znew(*output); + output = znew(*output); output->wlr_output = wlr_output; wlr_output->data = output; output->server = server; @@ -894,59 +900,6 @@ handle_output_power_manager_set_mode(struct wl_listener *listener, void *data) } } -void -output_add_virtual(struct server *server, const char *output_name) -{ - if (output_name) { - /* Prevent creating outputs with the same name */ - struct output *output; - wl_list_for_each(output, &server->outputs, link) { - if (wlr_output_is_headless(output->wlr_output) && - !strcmp(output->wlr_output->name, output_name)) { - wlr_log(WLR_DEBUG, - "refusing to create virtual output with duplicate name"); - return; - } - } - snprintf(server->headless.pending_output_name, - sizeof(server->headless.pending_output_name), "%s", output_name); - } else { - server->headless.pending_output_name[0] = '\0'; - } - /* - * Setting it to (0, 0) here disallows changing resolution from tools like - * wlr-randr (returns error) - */ - wlr_headless_add_output(server->headless.backend, 1920, 1080); -} - -void -output_remove_virtual(struct server *server, const char *output_name) -{ - struct output *output; - wl_list_for_each(output, &server->outputs, link) { - if (wlr_output_is_headless(output->wlr_output)) { - if (output_name) { - /* - * Given virtual output name, find and destroy virtual output by - * that name. - */ - if (!strcmp(output->wlr_output->name, output_name)) { - wlr_output_destroy(output->wlr_output); - return; - } - } else { - /* - * When virtual output name was no supplied by user, simply - * destroy the first virtual output found. - */ - wlr_output_destroy(output->wlr_output); - return; - } - } - } -} - void output_enable_adaptive_sync(struct wlr_output *output, bool enabled) {