diff --git a/include/labwc.h b/include/labwc.h index b9eeb8b9..7714411c 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; @@ -345,6 +350,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); @@ -375,4 +382,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..4542264a 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,62 @@ drop_permissions(void) } } +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)) + { + 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) { + 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_seat_pointer_clear_focus(seat->seat); + } +} + +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 output *output; + wl_list_for_each(output, &seat->server->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 +260,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);