diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 428f96796..04f0e0ea0 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -99,6 +99,7 @@ struct sway_seat { char *prev_workspace_name; // for workspace back_and_forth struct wlr_layer_surface_v1 *focused_layer; + struct wlr_surface *focused_lock; // If the exclusive layer is set, views cannot receive keyboard focus bool has_exclusive_layer; diff --git a/include/sway/server.h b/include/sway/server.h index 8c8114882..000371995 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -13,7 +13,6 @@ struct sway_transaction; struct sway_session_lock { struct wlr_session_lock_v1 *lock; - struct wlr_surface *focused; bool abandoned; struct wl_list outputs; // struct sway_session_lock_output @@ -181,6 +180,8 @@ void sway_session_lock_add_output(struct sway_session_lock *lock, struct sway_output *output); bool sway_session_lock_has_surface(struct sway_session_lock *lock, struct wlr_surface *surface); +void sway_session_lock_focus_output(struct sway_session_lock *lock, + struct sway_seat* seat, struct sway_output *output); void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data); #if WLR_HAS_XWAYLAND void handle_xwayland_surface(struct wl_listener *listener, void *data); diff --git a/sway/input/seat.c b/sway/input/seat.c index 0434d637c..e7a95ea64 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1266,7 +1266,14 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { seat_set_workspace_focus(seat, node); } if (server.session_lock.lock) { - seat_set_focus_surface(seat, server.session_lock.lock->focused, false); + // Try focusing the current sway_output's lock-surface + struct sway_output *output = node ? node_get_output(node) : NULL; + if (output) { + sway_session_lock_focus_output(server.session_lock.lock, seat, output); + return; + } + // Fallback to the previously focused lock surface + seat_set_focus_surface(seat, seat->focused_lock, false); } } diff --git a/sway/lock.c b/sway/lock.c index b1df65718..d4d59adf7 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -8,6 +8,7 @@ #include "sway/layers.h" #include "sway/output.h" #include "sway/server.h" +#include "sway/tree/workspace.h" #include "sway/lock.h" struct sway_session_lock_output { @@ -29,40 +30,68 @@ struct sway_session_lock_output { }; static void focus_surface(struct sway_session_lock *lock, - struct wlr_surface *focused) { - lock->focused = focused; - - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { + struct sway_seat *seat, struct wlr_surface *focused) { + if (seat) { + seat->focused_lock = focused; seat_set_focus_surface(seat, focused, false); } } static void refocus_output(struct sway_session_lock_output *output) { // Move the seat focus to another surface if one is available - if (output->lock->focused == output->surface->surface) { - struct wlr_surface *next_focus = NULL; + struct wlr_surface *next_focus = NULL; - struct sway_session_lock_output *candidate; - wl_list_for_each(candidate, &output->lock->outputs, link) { - if (candidate == output || !candidate->surface) { - continue; - } - - if (candidate->surface->surface->mapped) { - next_focus = candidate->surface->surface; - break; - } + struct sway_session_lock_output *candidate; + wl_list_for_each(candidate, &output->lock->outputs, link) { + if (candidate == output || !candidate->surface) { + continue; } - focus_surface(output->lock, next_focus); + if (candidate->surface->surface->mapped) { + next_focus = candidate->surface->surface; + break; + } + } + + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + if (seat->focused_lock == output->surface->surface) { + focus_surface(output->lock, seat, next_focus); + } + } +} + +void sway_session_lock_focus_output(struct sway_session_lock *lock, + struct sway_seat* seat, struct sway_output *output) { + // Try focusing the lock surface on the provided output + struct sway_session_lock_output *candidate; + wl_list_for_each(candidate, &lock->outputs, link) { + if (candidate->output != output || !candidate->surface) { + continue; + } + + if (candidate->surface->surface->mapped) { + focus_surface(lock, seat, candidate->surface->surface); + break; + } } } static void handle_surface_map(struct wl_listener *listener, void *data) { struct sway_session_lock_output *surf = wl_container_of(listener, surf, surface_map); - if (surf->lock->focused == NULL) { - focus_surface(surf->lock, surf->surface->surface); + + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + struct sway_workspace *focused_ws = seat_get_focused_workspace(seat); + struct sway_output *focused_output = focused_ws ? focused_ws->output : NULL; + + // Only set the initial focus lock-surface when the surface is on the + // focused output. Fallback to the first mapped surface if no focused + // output can be found. + if (focused_output == surf->output + || (!focused_output && seat->focused_lock == NULL)) { + focus_surface(surf->lock, seat, surf->surface->surface); + } } cursor_rebase_all(); } @@ -198,6 +227,11 @@ static void sway_session_lock_destroy(struct sway_session_lock* lock) { wlr_scene_node_destroy(&lock_output->tree->node); } + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + seat->focused_lock = NULL; + } + if (server.session_lock.lock == lock) { server.session_lock.lock = NULL; } @@ -219,6 +253,8 @@ static void handle_unlock(struct wl_listener *listener, void *data) { struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { + seat->focused_lock = NULL; + // copied from seat_set_focus_layer -- deduplicate? struct sway_node *previous = seat_get_focus_inactive(seat, &root->node); if (previous) {