mirror of
				https://github.com/labwc/labwc.git
				synced 2025-11-03 09:01:51 -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