From 39b1d92f9b43cd786d9d9848061f5d28c063e03a Mon Sep 17 00:00:00 2001 From: Daniel Barlow Date: Sat, 21 Aug 2021 17:12:02 +0100 Subject: [PATCH 1/3] implement input_inhibit protocol, needed for swaylock this is in "it appears to work" state, though I blindly copy-pasted a little more code than I'm happy with, so might benefit from a review --- include/labwc.h | 12 +++ protocols/meson.build | 1 + protocols/wlr-input-inhibitor-unstable-v1.xml | 67 ++++++++++++++ src/cursor.c | 12 ++- src/desktop.c | 6 ++ src/keyboard.c | 33 +++++-- src/server.c | 89 +++++++++++++++++++ 7 files changed, 212 insertions(+), 8 deletions(-) create mode 100644 protocols/wlr-input-inhibitor-unstable-v1.xml diff --git a/include/labwc.h b/include/labwc.h index 367838c8..39613272 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -65,6 +65,7 @@ struct seat { /* if set, views cannot receive focus */ struct wlr_layer_surface_v1 *focused_layer; + struct wl_client *active_client_while_inhibited; struct wl_list inputs; struct wl_listener new_input; @@ -99,6 +100,10 @@ struct server { struct wl_listener new_xwayland_surface; #endif + struct wlr_input_inhibit_manager *input_inhibit; + struct wl_listener input_inhibit_activate; + struct wl_listener input_inhibit_deactivate; + struct wl_list views; struct wl_list unmanaged_surfaces; @@ -369,4 +374,11 @@ void action(struct server *server, const char *action, const char *command); /* update onscreen display 'alt-tab' texture */ void osd_update(struct server *server); +/* wlroots "input inhibitor" extension (required for swaylock) blocks + * any client other than the requesting client from receiving events + */ +bool input_inhibit_blocks_surface(struct seat *seat, + struct wl_resource *resource); + + #endif /* __LABWC_H */ diff --git a/protocols/meson.build b/protocols/meson.build index e4841906..efd39e98 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -16,6 +16,7 @@ wayland_scanner_server = generator( server_protocols = [ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], ['wlr-layer-shell-unstable-v1.xml'], + ['wlr-input-inhibitor-unstable-v1.xml'], ] server_protos_src = [] diff --git a/protocols/wlr-input-inhibitor-unstable-v1.xml b/protocols/wlr-input-inhibitor-unstable-v1.xml new file mode 100644 index 00000000..b62d1bb4 --- /dev/null +++ b/protocols/wlr-input-inhibitor-unstable-v1.xml @@ -0,0 +1,67 @@ + + + + Copyright © 2018 Drew DeVault + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + Clients can use this interface to prevent input events from being sent to + any surfaces but its own, which is useful for example in lock screen + software. It is assumed that access to this interface will be locked down + to whitelisted clients by the compositor. + + + + + Activates the input inhibitor. As long as the inhibitor is active, the + compositor will not send input events to other clients. + + + + + + + + + + + + While this resource exists, input to clients other than the owner of the + inhibitor resource will not receive input events. The client that owns + this resource will receive all input events normally. The compositor will + also disable all of its own input processing (such as keyboard shortcuts) + while the inhibitor is active. + + The compositor may continue to send input events to selected clients, + such as an on-screen keyboard (via the input-method protocol). + + + + + Destroy the inhibitor and allow other clients to receive input. + + + + diff --git a/src/cursor.c b/src/cursor.c index 4f7c3830..70709593 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -105,6 +105,15 @@ set_cursor(struct server *server, const char *cursor_name) server->seat.xcursor_manager, cursor_name, server->seat.cursor); } +bool input_inhibit_blocks_surface(struct seat *seat, + struct wl_resource *resource) +{ + struct wl_client * inhibiting_client = + seat->active_client_while_inhibited; + return (inhibiting_client != NULL) && + inhibiting_client != wl_resource_get_client(resource); +} + static void process_cursor_motion(struct server *server, uint32_t time) { @@ -161,7 +170,8 @@ process_cursor_motion(struct server *server, uint32_t time) /* Required for iconify/maximize/close button mouse-over deco */ damage_all_outputs(server); - if (surface) { + if (surface && + ! input_inhibit_blocks_surface(&server->seat, surface->resource)) { bool focus_changed = wlr_seat->pointer_state.focused_surface != surface; /* diff --git a/src/desktop.c b/src/desktop.c index 488a6db7..4c122fb0 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -76,6 +76,9 @@ desktop_set_focus_view_only(struct seat *seat, struct view *view) if (!view || view->minimized || !view->mapped) { return; } + if(input_inhibit_blocks_surface(seat, view->surface->resource)) + return; + struct wlr_surface *prev_surface; prev_surface = seat->seat->keyboard_state.focused_surface; if (prev_surface == view->surface) { @@ -96,6 +99,9 @@ desktop_focus_view(struct seat *seat, struct view *view) seat_focus_surface(seat, NULL); return; } + if(input_inhibit_blocks_surface(seat, view->surface->resource)) + return; + if (view->minimized) { /* this will unmap and then focus */ view_minimize(view, false); diff --git a/src/keyboard.c b/src/keyboard.c index 7bf5ac51..3111a979 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -41,14 +41,12 @@ handle_keybinding(struct server *server, uint32_t modifiers, xkb_keysym_t sym) return false; } -static void -keyboard_key_notify(struct wl_listener *listener, void *data) +static bool +handle_compositor_keybindings(struct wl_listener *listener, + struct wlr_event_keyboard_key *event) { - /* This event is raised when a key is pressed or released. */ struct seat *seat = wl_container_of(listener, seat, keyboard_key); struct server *server = seat->server; - struct wlr_event_keyboard_key *event = data; - struct wlr_seat *wlr_seat = server->seat.seat; struct wlr_input_device *device = seat->keyboard_group->input_device; /* Translate libinput keycode -> xkbcommon */ @@ -68,13 +66,14 @@ keyboard_key_notify(struct wl_listener *listener, void *data) /* end cycle */ desktop_focus_view(&server->seat, server->cycle_view); server->cycle_view = NULL; + /* XXX should we handled=true here? */ } else if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { /* cycle to next */ server->cycle_view = desktop_cycle_view(server, server->cycle_view); osd_update(server); damage_all_outputs(server); - return; + return true; } } @@ -95,8 +94,28 @@ keyboard_key_notify(struct wl_listener *listener, void *data) } } } + return handled; +} +static void +keyboard_key_notify(struct wl_listener *listener, void *data) +{ + /* XXX need to check if input inhibited before doing any + * compositor bindings + */ + + /* This event is raised when a key is pressed or released. */ + struct seat *seat = wl_container_of(listener, seat, keyboard_key); + struct server *server = seat->server; + struct wlr_event_keyboard_key *event = data; + struct wlr_seat *wlr_seat = server->seat.seat; + struct wlr_input_device *device = seat->keyboard_group->input_device; + + bool handled = false; + + if(!seat->active_client_while_inhibited) + /* ignore labwc keybindings if input is inhibited */ + handled = handle_compositor_keybindings(listener, event); - /* Otherwise, pass it to the client. */ if (!handled) { wlr_seat_set_keyboard(wlr_seat, device); wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, diff --git a/src/server.c b/src/server.c index 45e30ef2..af5ce566 100644 --- a/src/server.c +++ b/src/server.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include "config/rcxml.h" @@ -78,6 +79,81 @@ drop_permissions(void) } } +static void seat_clear_touch_points(struct seat *seat, + struct wl_client *active_client){ + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + struct wlr_touch_point *point; + wl_list_for_each(point, &seat->seat->touch_state.touch_points, link) { + if (point->client->client != active_client) { + wlr_seat_touch_point_clear_focus(seat->seat, + now.tv_nsec / 1000, point->touch_id); + } + } +} + + +static void seat_inhibit_input(struct seat *seat, struct wl_client *active_client) +{ + seat->active_client_while_inhibited = active_client; + + if(seat->focused_layer && + (wl_resource_get_client(seat->focused_layer->resource) != + active_client)) + { + wlr_log(WLR_INFO, "defocus layer"); + seat_set_focus_layer(seat, NULL); /* ? */ + } + struct wlr_surface * previous_kb_surface = seat->seat->keyboard_state.focused_surface; + if (previous_kb_surface && + wl_resource_get_client(previous_kb_surface->resource) != active_client) { + wlr_log(WLR_INFO, "defocus surface"); + seat_focus_surface(seat, NULL); /* keyboard focus */ + } + + struct wlr_seat_client * previous_ptr_client = seat->seat->pointer_state.focused_client; + if (previous_ptr_client && + (previous_ptr_client->client != active_client)) { + wlr_log(WLR_INFO, "defocus ptr"); + wlr_seat_pointer_clear_focus(seat->seat); + } + seat_clear_touch_points(seat, active_client); +} + +static void seat_disinhibit_input(struct seat *seat) +{ + seat->active_client_while_inhibited = NULL; + // Triggers a refocus of the topmost surface layer if necessary + // TODO: Make layer surface focus per-output based on cursor position +/* + struct roots_output *output; + wl_list_for_each(output, &seat->input->server->desktop->outputs, link) { + arrange_layers(output); +*/ +} + + + +static void handle_input_inhibit(struct wl_listener *listener, void *data) { + wlr_log(WLR_INFO, "activate input inhibit"); + + struct server *server = wl_container_of( + listener, server, input_inhibit_activate); + + seat_inhibit_input(&server->seat, + server->input_inhibit->active_client); +} + +static void handle_input_disinhibit(struct wl_listener *listener, void *data) { + wlr_log(WLR_INFO, "deactivate input inhibit"); + + struct server *server = wl_container_of( + listener, server, input_inhibit_deactivate); + + seat_disinhibit_input(&server->seat); +} + + void server_init(struct server *server) { @@ -203,6 +279,19 @@ server_init(struct server *server) wlr_data_control_manager_v1_create(server->wl_display); wlr_gamma_control_manager_v1_create(server->wl_display); + // struct wlr_input_inhibit_manager *input_inhibit_mgr = NULL; + server->input_inhibit = wlr_input_inhibit_manager_create(server->wl_display); + if (!server->input_inhibit) { + wlr_log(WLR_ERROR, "unable to create the input inhibit manager"); + exit(EXIT_FAILURE); + } + + wl_signal_add(&server->input_inhibit->events.activate, &server->input_inhibit_activate); + server->input_inhibit_activate.notify = handle_input_inhibit; + + wl_signal_add(&server->input_inhibit->events.deactivate, &server->input_inhibit_deactivate); + server->input_inhibit_deactivate.notify = handle_input_disinhibit; + server->foreign_toplevel_manager = wlr_foreign_toplevel_manager_v1_create(server->wl_display); From eb2d0918ba3338e1fedc5d18f0aade873db21863 Mon Sep 17 00:00:00 2001 From: Daniel Barlow Date: Mon, 23 Aug 2021 20:33:42 +0100 Subject: [PATCH 2/3] cleanup per PR review comments --- src/server.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/server.c b/src/server.c index af5ce566..3c21eedd 100644 --- a/src/server.c +++ b/src/server.c @@ -79,20 +79,6 @@ drop_permissions(void) } } -static void seat_clear_touch_points(struct seat *seat, - struct wl_client *active_client){ - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - struct wlr_touch_point *point; - wl_list_for_each(point, &seat->seat->touch_state.touch_points, link) { - if (point->client->client != active_client) { - wlr_seat_touch_point_clear_focus(seat->seat, - now.tv_nsec / 1000, point->touch_id); - } - } -} - - static void seat_inhibit_input(struct seat *seat, struct wl_client *active_client) { seat->active_client_while_inhibited = active_client; @@ -101,23 +87,19 @@ static void seat_inhibit_input(struct seat *seat, struct wl_client *active_clie (wl_resource_get_client(seat->focused_layer->resource) != active_client)) { - wlr_log(WLR_INFO, "defocus layer"); - seat_set_focus_layer(seat, NULL); /* ? */ + seat_set_focus_layer(seat, NULL); } struct wlr_surface * previous_kb_surface = seat->seat->keyboard_state.focused_surface; if (previous_kb_surface && wl_resource_get_client(previous_kb_surface->resource) != active_client) { - wlr_log(WLR_INFO, "defocus surface"); seat_focus_surface(seat, NULL); /* keyboard focus */ } struct wlr_seat_client * previous_ptr_client = seat->seat->pointer_state.focused_client; if (previous_ptr_client && (previous_ptr_client->client != active_client)) { - wlr_log(WLR_INFO, "defocus ptr"); wlr_seat_pointer_clear_focus(seat->seat); } - seat_clear_touch_points(seat, active_client); } static void seat_disinhibit_input(struct seat *seat) From ab9838888ded2bb44fddfb75450bedc2f4019518 Mon Sep 17 00:00:00 2001 From: Daniel Barlow Date: Tue, 24 Aug 2021 21:24:08 +0100 Subject: [PATCH 3/3] implement commented-out bit in seat_disinhibit_input --- include/labwc.h | 2 ++ src/server.c | 7 +++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index 39613272..71f74263 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -344,6 +344,8 @@ void cursor_init(struct seat *seat); void keyboard_init(struct seat *seat); +void arrange_layers(struct output *output); + void seat_init(struct server *server); void seat_finish(struct server *server); void seat_focus_surface(struct seat *seat, struct wlr_surface *surface); diff --git a/src/server.c b/src/server.c index 3c21eedd..4542264a 100644 --- a/src/server.c +++ b/src/server.c @@ -107,11 +107,10 @@ static void seat_disinhibit_input(struct seat *seat) seat->active_client_while_inhibited = NULL; // Triggers a refocus of the topmost surface layer if necessary // TODO: Make layer surface focus per-output based on cursor position -/* - struct roots_output *output; - wl_list_for_each(output, &seat->input->server->desktop->outputs, link) { + + struct output *output; + wl_list_for_each(output, &seat->server->outputs, link) arrange_layers(output); -*/ }