]> git.mdlowis.com Git - proto/labwc.git/commitdiff
src/output.c: refactor virtual output related functions
authorConsolatis <35009135+Consolatis@users.noreply.github.com>
Wed, 6 Mar 2024 23:22:51 +0000 (00:22 +0100)
committerJohan Malm <johanmalm@users.noreply.github.com>
Thu, 7 Mar 2024 19:51:54 +0000 (19:51 +0000)
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
include/output-virtual.h [new file with mode: 0644]
src/action.c
src/meson.build
src/output-virtual.c [new file with mode: 0644]
src/output.c

index 204f60e3c36ff60ab66b781e61c6435618bf3227..38fd5c9fe64ab3b3f3538d85d244e6b12f4b268f 100644 (file)
@@ -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 (file)
index 0000000..4bceb39
--- /dev/null
@@ -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
index 1f8289e01215051f8084fc9aa540252a96bbab8b..6c0267335438cf2aff4b22bc382669a64a45df1a 100644 (file)
@@ -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:
index b544e99f74dc3f7b93fc39cecc1a92f7d4a2f142..a71eb06bc23e557d3e8143e2d6970f884e6913bd 100644 (file)
@@ -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 (file)
index 0000000..72b1e06
--- /dev/null
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <wlr/backend/headless.h>
+#include <wlr/types/wlr_output.h>
+#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;
+               }
+       }
+}
index 16eee4b81d85b495f59ac0fc7717551391fe9824..aad280e8febe6db5222a7dbe33aa167f73a80b14 100644 (file)
@@ -10,7 +10,6 @@
 #include <assert.h>
 #include <strings.h>
 #include <wlr/backend/drm.h>
-#include <wlr/backend/headless.h>
 #include <wlr/backend/wayland.h>
 #include <wlr/types/wlr_buffer.h>
 #include <wlr/types/wlr_drm_lease_v1.h>
@@ -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)
 {