]> git.mdlowis.com Git - proto/labwc.git/commitdiff
Implement key binds to control virtual outputs (#1287)
authorkyak <699631+kyak@users.noreply.github.com>
Sat, 9 Dec 2023 09:01:11 +0000 (12:01 +0300)
committerGitHub <noreply@github.com>
Sat, 9 Dec 2023 09:01:11 +0000 (09:01 +0000)
Add actions `VirtualOutputAdd` and `VirtualOutputRemove`

docs/labwc-actions.5.scd
include/labwc.h
src/action.c
src/output.c
src/server.c

index 984f35f0feafdaab91ca3937680697222304239f..f639c4905bff419a507d6ad4802efab4a531ab92 100644 (file)
@@ -180,6 +180,45 @@ Actions are used in menus and keyboard/mouse bindings.
        *wrap* [yes|no] Wrap around from last desktop to first, and vice
        versa. Default yes.
 
+*<action name="VirtualOutputAdd" output_name="value" />*
+       Add virtual output (headless backend).
+
+       For example, it can be used to overlay virtual output on real output, but with
+       a different resolution (this can be done with `wlr-randr` or `wdisplays`).
+       After that, virtual output can be selected for screen sharing (casting),
+       effectively sharing only the region of the screen.
+
+       It must be noted that overlaying virtual output and real output is not
+       endorsed or explicitely supported by wlroots. For example, after configuring
+       virtual output, real output must be reconfigured as well (for the overlay
+       configuration to work correctly). This is the example configuration:
+
+```
+<keybind key="W-v">
+  <action name="VirtualOutputAdd" output_name="ScreenCasting"/>
+  <action name="Execute" command='sh -c "wlr-randr --output ScreenCasting --pos 0,0 --scale 2 --custom-mode 3840x2110; wlr-randr --output eDP-1 --pos 0,0 --scale 2 --mode 3840x2160"'/>
+</keybind>
+<keybind key="W-c">
+  <action name="VirtualOutputRemove"/>
+</keybind>
+```
+
+       Note that the vertical resolution of "ScreenCasting" output is just 50px
+       smaller than "eDP-1" output to cut off bottom panel from screen sharing.
+
+       Virtual output is also useful for extending the desktop to (maybe mobile)
+       remote systems like tablets. E.g. simply adding a virtual output, attaching
+       wayvnc to it and running a VNC client on the remote system.
+
+       *output_name* The name of virtual output. Providing virtual output name is
+       beneficial for further automation. Default is "HEADLESS-X".
+
+*<action name="VirtualOutputRemove" output_name="value" />*
+       Remove virtual output (headless backend).
+
+       *output_name* The name of virtual output. If not supplied, will remove the
+       last virtual output added.
+
 *<action name="None" />*
        If used as the only action for a binding: clear an earlier defined binding.
 
index 86198a038a092b9b7bcf319b316fceeed621f15c..a4729178cbd5dc626b95265ae4807611a93ef09f 100644 (file)
@@ -203,6 +203,10 @@ struct server {
        struct wlr_renderer *renderer;
        struct wlr_allocator *allocator;
        struct wlr_backend *backend;
+       struct headless {
+               struct wlr_backend *backend;
+               char pending_output_name[4096];
+       } headless;
        struct wlr_session *session;
 
        struct wlr_xdg_shell *xdg_shell;
@@ -467,6 +471,8 @@ 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 server_init(struct server *server);
 void server_start(struct server *server);
index 3a97efe48b36022e0e6d7dbb3cea22cfbdac4af5..7762bc928135641841bf019e5498c4b7d9b3a982 100644 (file)
@@ -98,6 +98,8 @@ enum action_type {
        ACTION_TYPE_FOCUS_OUTPUT,
        ACTION_TYPE_IF,
        ACTION_TYPE_FOR_EACH,
+       ACTION_TYPE_VIRTUAL_OUTPUT_ADD,
+       ACTION_TYPE_VIRTUAL_OUTPUT_REMOVE,
 };
 
 const char *action_names[] = {
@@ -142,6 +144,8 @@ const char *action_names[] = {
        "FocusOutput",
        "If",
        "ForEach",
+       "VirtualOutputAdd",
+       "VirtualOutputRemove",
        NULL
 };
 
@@ -363,6 +367,13 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
                        goto cleanup;
                }
                break;
+       case ACTION_TYPE_VIRTUAL_OUTPUT_ADD:
+       case ACTION_TYPE_VIRTUAL_OUTPUT_REMOVE:
+               if (!strcmp(argument, "output_name")) {
+                       action_arg_add_str(action, argument, content);
+                       goto cleanup;
+               }
+               break;
        }
 
        wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s'",
@@ -915,6 +926,20 @@ actions_run(struct view *activator, struct server *server,
                                wl_array_release(&views);
                        }
                        break;
+               case ACTION_TYPE_VIRTUAL_OUTPUT_ADD:
+                       {
+                               const char *output_name = action_get_str(action, "output_name",
+                                               NULL);
+                               output_add_virtual(server, output_name);
+                       }
+                       break;
+               case ACTION_TYPE_VIRTUAL_OUTPUT_REMOVE:
+                       {
+                               const char *output_name = action_get_str(action, "output_name",
+                                               NULL);
+                               output_remove_virtual(server, output_name);
+                       }
+                       break;
                case ACTION_TYPE_INVALID:
                        wlr_log(WLR_ERROR, "Not executing unknown action");
                        break;
index e9ed98004a62649dd00b5b0d5b600f594a306607..5be82fb4cc0f198a28d57770480833b69085fdf3 100644 (file)
@@ -9,6 +9,8 @@
 #define _POSIX_C_SOURCE 200809L
 #include <assert.h>
 #include <strings.h>
+#include <wlr/backend/drm.h>
+#include <wlr/backend/headless.h>
 #include <wlr/types/wlr_buffer.h>
 #include <wlr/types/wlr_drm_lease_v1.h>
 #include <wlr/types/wlr_output.h>
@@ -172,6 +174,12 @@ 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';
+       }
+
        /*
         * We offer any display as available for lease, some apps like
         * gamescope, want to take ownership of a display when they can
@@ -179,7 +187,7 @@ new_output_notify(struct wl_listener *listener, void *data)
         * This is also useful for debugging the DRM parts of
         * another compositor.
         */
-       if (server->drm_lease_manager) {
+       if (server->drm_lease_manager && wlr_output_is_drm(wlr_output)) {
                wlr_drm_lease_v1_manager_offer_output(
                        server->drm_lease_manager, wlr_output);
        }
@@ -746,3 +754,56 @@ handle_output_power_manager_set_mode(struct wl_listener *listener, void *data)
                break;
        }
 }
+
+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;
+                       }
+               }
+               strncpy(server->headless.pending_output_name, output_name,
+                               sizeof(server->headless.pending_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;
+                       }
+               }
+       }
+}
index 0aa07a09b9add7fd30c41e9502cc361037b43d1b..e7a58be981ea6189f590ec4e6f897c793ea754bc 100644 (file)
@@ -3,6 +3,8 @@
 #include "config.h"
 #include <signal.h>
 #include <sys/wait.h>
+#include <wlr/backend/headless.h>
+#include <wlr/backend/multi.h>
 #include <wlr/types/wlr_data_control_v1.h>
 #include <wlr/types/wlr_export_dmabuf_v1.h>
 #include <wlr/types/wlr_fractional_scale_v1.h>
@@ -256,6 +258,22 @@ server_init(struct server *server)
                exit(EXIT_FAILURE);
        }
 
+       /* Create headless backend to enable adding virtual outputs later on */
+       server->headless.backend = wlr_headless_backend_create(server->wl_display);
+       if (!server->headless.backend) {
+               wlr_log(WLR_ERROR, "unable to create headless backend");
+               exit(EXIT_FAILURE);
+       }
+       wlr_multi_backend_add(server->backend, server->headless.backend);
+
+       /*
+        * If we don't populate headless backend with a virtual output (that we
+        * create and immediately destroy), then virtual outputs being added
+        * later do not work properly when overlaid on real output. Content is
+        * drawn on the virtual output, but not drawn on the real output.
+        */
+       wlr_output_destroy(wlr_headless_add_output(server->headless.backend, 0, 0));
+
        /*
         * Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The
         * user can also specify a renderer using the WLR_RENDERER env var.