mirror of
https://github.com/labwc/labwc.git
synced 2025-11-04 13:30:07 -05:00
Implement key binds to control virtual outputs (#1287)
Add actions `VirtualOutputAdd` and `VirtualOutputRemove`
This commit is contained in:
parent
e303281333
commit
111b955b53
5 changed files with 150 additions and 1 deletions
|
|
@ -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
|
*wrap* [yes|no] Wrap around from last desktop to first, and vice
|
||||||
versa. Default yes.
|
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" />*
|
*<action name="None" />*
|
||||||
If used as the only action for a binding: clear an earlier defined binding.
|
If used as the only action for a binding: clear an earlier defined binding.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -203,6 +203,10 @@ struct server {
|
||||||
struct wlr_renderer *renderer;
|
struct wlr_renderer *renderer;
|
||||||
struct wlr_allocator *allocator;
|
struct wlr_allocator *allocator;
|
||||||
struct wlr_backend *backend;
|
struct wlr_backend *backend;
|
||||||
|
struct headless {
|
||||||
|
struct wlr_backend *backend;
|
||||||
|
char pending_output_name[4096];
|
||||||
|
} headless;
|
||||||
struct wlr_session *session;
|
struct wlr_session *session;
|
||||||
|
|
||||||
struct wlr_xdg_shell *xdg_shell;
|
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);
|
struct wlr_box output_usable_area_scaled(struct output *output);
|
||||||
void handle_output_power_manager_set_mode(struct wl_listener *listener,
|
void handle_output_power_manager_set_mode(struct wl_listener *listener,
|
||||||
void *data);
|
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_init(struct server *server);
|
||||||
void server_start(struct server *server);
|
void server_start(struct server *server);
|
||||||
|
|
|
||||||
25
src/action.c
25
src/action.c
|
|
@ -98,6 +98,8 @@ enum action_type {
|
||||||
ACTION_TYPE_FOCUS_OUTPUT,
|
ACTION_TYPE_FOCUS_OUTPUT,
|
||||||
ACTION_TYPE_IF,
|
ACTION_TYPE_IF,
|
||||||
ACTION_TYPE_FOR_EACH,
|
ACTION_TYPE_FOR_EACH,
|
||||||
|
ACTION_TYPE_VIRTUAL_OUTPUT_ADD,
|
||||||
|
ACTION_TYPE_VIRTUAL_OUTPUT_REMOVE,
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *action_names[] = {
|
const char *action_names[] = {
|
||||||
|
|
@ -142,6 +144,8 @@ const char *action_names[] = {
|
||||||
"FocusOutput",
|
"FocusOutput",
|
||||||
"If",
|
"If",
|
||||||
"ForEach",
|
"ForEach",
|
||||||
|
"VirtualOutputAdd",
|
||||||
|
"VirtualOutputRemove",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -363,6 +367,13 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
break;
|
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'",
|
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);
|
wl_array_release(&views);
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case ACTION_TYPE_INVALID:
|
||||||
wlr_log(WLR_ERROR, "Not executing unknown action");
|
wlr_log(WLR_ERROR, "Not executing unknown action");
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
63
src/output.c
63
src/output.c
|
|
@ -9,6 +9,8 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
|
#include <wlr/backend/drm.h>
|
||||||
|
#include <wlr/backend/headless.h>
|
||||||
#include <wlr/types/wlr_buffer.h>
|
#include <wlr/types/wlr_buffer.h>
|
||||||
#include <wlr/types/wlr_drm_lease_v1.h>
|
#include <wlr/types/wlr_drm_lease_v1.h>
|
||||||
#include <wlr/types/wlr_output.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 server *server = wl_container_of(listener, server, new_output);
|
||||||
struct wlr_output *wlr_output = data;
|
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
|
* 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
|
||||||
|
|
@ -179,7 +187,7 @@ new_output_notify(struct wl_listener *listener, void *data)
|
||||||
* This is also useful for debugging the DRM parts of
|
* This is also useful for debugging the DRM parts of
|
||||||
* another compositor.
|
* 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(
|
wlr_drm_lease_v1_manager_offer_output(
|
||||||
server->drm_lease_manager, wlr_output);
|
server->drm_lease_manager, wlr_output);
|
||||||
}
|
}
|
||||||
|
|
@ -746,3 +754,56 @@ handle_output_power_manager_set_mode(struct wl_listener *listener, void *data)
|
||||||
break;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
18
src/server.c
18
src/server.c
|
|
@ -3,6 +3,8 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/wait.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_data_control_v1.h>
|
||||||
#include <wlr/types/wlr_export_dmabuf_v1.h>
|
#include <wlr/types/wlr_export_dmabuf_v1.h>
|
||||||
#include <wlr/types/wlr_fractional_scale_v1.h>
|
#include <wlr/types/wlr_fractional_scale_v1.h>
|
||||||
|
|
@ -256,6 +258,22 @@ server_init(struct server *server)
|
||||||
exit(EXIT_FAILURE);
|
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
|
* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The
|
||||||
* user can also specify a renderer using the WLR_RENDERER env var.
|
* user can also specify a renderer using the WLR_RENDERER env var.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue