From 8f7bb145b72209724f05b0182dee33c0e47d2357 Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Fri, 29 Apr 2022 23:40:16 -0400 Subject: [PATCH] Rework session lock keyboard focus handling When removing outputs, it is possible to end up in a situation where none of the session lock client's surfaces have keyboard focus, resulting in it not receiving keyboard events. Track the focused surface and update it as needed on surface destroy. --- include/sway/input/seat.h | 4 ---- include/sway/server.h | 1 + sway/input/seat.c | 9 --------- sway/lock.c | 30 +++++++++++++++++++++++++++++- 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index c2041742..e3a46872 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -201,10 +201,6 @@ struct sway_workspace *seat_get_last_known_workspace(struct sway_seat *seat); struct sway_container *seat_get_focused_container(struct sway_seat *seat); -// Force focus to a particular surface that is not part of the workspace -// hierarchy (used for lockscreen) -void sway_force_focus(struct wlr_surface *surface); - /** * Return the last container to be focused for the seat (or the most recently * opened if no container has received focused) that is a child of the given diff --git a/include/sway/server.h b/include/sway/server.h index 055c067d..6a5a60c8 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -96,6 +96,7 @@ struct sway_server { struct wlr_session_lock_manager_v1 *manager; struct wlr_session_lock_v1 *lock; + struct wlr_surface *focused; struct wl_listener lock_new_surface; struct wl_listener lock_unlock; struct wl_listener lock_destroy; diff --git a/sway/input/seat.c b/sway/input/seat.c index 987e1c9f..a7408287 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -214,15 +214,6 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) { } } -void sway_force_focus(struct wlr_surface *surface) { - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - seat_keyboard_notify_enter(seat, surface); - seat_tablet_pads_notify_enter(seat, surface); - sway_input_method_relay_set_focus(&seat->im_relay, surface); - } -} - void seat_for_each_node(struct sway_seat *seat, void (*f)(struct sway_node *node, void *data), void *data) { struct sway_seat_node *current = NULL; diff --git a/sway/lock.c b/sway/lock.c index 04f80079..3c7c06cf 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -17,9 +17,20 @@ struct sway_session_lock_surface { struct wl_listener output_commit; }; +static void set_lock_focused_surface(struct wlr_surface *focused) { + server.session_lock.focused = focused; + + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + seat_set_focus_surface(seat, focused, false); + } +} + static void handle_surface_map(struct wl_listener *listener, void *data) { struct sway_session_lock_surface *surf = wl_container_of(listener, surf, map); - sway_force_focus(surf->surface); + if (server.session_lock.focused == NULL) { + set_lock_focused_surface(surf->surface); + } output_damage_whole(surf->output); } @@ -48,6 +59,21 @@ static void handle_output_commit(struct wl_listener *listener, void *data) { static void handle_surface_destroy(struct wl_listener *listener, void *data) { struct sway_session_lock_surface *surf = wl_container_of(listener, surf, destroy); + + // Move the seat focus to another surface if one is available + if (server.session_lock.focused == surf->surface) { + struct wlr_surface *next_focus = NULL; + + struct wlr_session_lock_surface_v1 *other; + wl_list_for_each(other, &server.session_lock.lock->surfaces, link) { + if (other != surf->lock_surface && other->mapped) { + next_focus = other->surface; + break; + } + } + set_lock_focused_surface(next_focus); + } + wl_list_remove(&surf->map.link); wl_list_remove(&surf->destroy.link); wl_list_remove(&surf->surface_commit.link); @@ -88,6 +114,7 @@ static void handle_unlock(struct wl_listener *listener, void *data) { sway_log(SWAY_DEBUG, "session unlocked"); server.session_lock.locked = false; server.session_lock.lock = NULL; + server.session_lock.focused = NULL; wl_list_remove(&server.session_lock.lock_new_surface.link); wl_list_remove(&server.session_lock.lock_unlock.link); @@ -115,6 +142,7 @@ static void handle_unlock(struct wl_listener *listener, void *data) { static void handle_abandon(struct wl_listener *listener, void *data) { sway_log(SWAY_INFO, "session lock abandoned"); server.session_lock.lock = NULL; + server.session_lock.focused = NULL; wl_list_remove(&server.session_lock.lock_new_surface.link); wl_list_remove(&server.session_lock.lock_unlock.link);