From f769203238e62bb909ea1e035e41ba1e586c8876 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Sun, 14 Sep 2025 16:13:08 +0200 Subject: [PATCH 1/5] Fixes session lock surfaces not being able to switch focused output --- include/sway/server.h | 2 ++ sway/input/seat.c | 18 ++++++++++++++++++ sway/lock.c | 17 +++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/include/sway/server.h b/include/sway/server.h index f50d48f05..a5b6d3320 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -178,6 +178,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_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 1b63f625b..ba64babe3 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1273,6 +1273,24 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { seat_set_workspace_focus(seat, node); } if (server.session_lock.lock) { + struct sway_output *output = NULL; + if (node != NULL) { + switch (node->type) { + case N_OUTPUT: + output = node->sway_output; + break; + case N_WORKSPACE: + output = node->sway_workspace->output; + break; + default: + break; + } + } + if (output != NULL) { + sway_session_lock_focus_output(server.session_lock.lock, output); + return; + } + // Fallback to the previously focused lock surface seat_set_focus_surface(seat, server.session_lock.lock->focused, false); } } diff --git a/sway/lock.c b/sway/lock.c index c8975c747..a75c1e7e2 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -59,6 +59,23 @@ static void refocus_output(struct sway_session_lock_output *output) { } } +void sway_session_lock_focus_output(struct sway_session_lock *lock, + 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) { + // Set the focus for all seats + focus_surface(lock, 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) { From 1737a58b01922584863b0eaa9c4735fa96527619 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:27:04 +0200 Subject: [PATCH 2/5] Initially focus the lock surface that has the current output focus --- sway/lock.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sway/lock.c b/sway/lock.c index a75c1e7e2..2b9b74b03 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 { @@ -78,7 +79,15 @@ void sway_session_lock_focus_output(struct sway_session_lock *lock, 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) { + + struct sway_seat *seat = input_manager_current_seat(); + struct sway_workspace *focused_ws = seat_get_focused_workspace(seat); + struct sway_output *focused_output = NULL; + if (focused_ws != NULL) { + focused_output = focused_ws->output; + } + + if (surf->lock->focused == NULL || focused_output == surf->output) { focus_surface(surf->lock, surf->surface->surface); } cursor_rebase_all(); From 47a0b18d2a00c8e42816325310798869f1401c07 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Fri, 15 May 2026 11:10:00 +0200 Subject: [PATCH 3/5] Simplified seat logic for focusing a new lock surface --- sway/input/seat.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/sway/input/seat.c b/sway/input/seat.c index 24e663f1a..ca6c2ec2e 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1266,20 +1266,9 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { seat_set_workspace_focus(seat, node); } if (server.session_lock.lock) { - struct sway_output *output = NULL; - if (node != NULL) { - switch (node->type) { - case N_OUTPUT: - output = node->sway_output; - break; - case N_WORKSPACE: - output = node->sway_workspace->output; - break; - default: - break; - } - } - if (output != NULL) { + // 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, output); return; } From bafbcdd139e19b29a49682525dfa4ccc4256cb69 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Fri, 15 May 2026 12:26:39 +0200 Subject: [PATCH 4/5] Only set initial focus on focused output --- sway/lock.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sway/lock.c b/sway/lock.c index 6f7d4c206..43384fdb5 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -82,12 +82,12 @@ static void handle_surface_map(struct wl_listener *listener, void *data) { struct sway_seat *seat = input_manager_current_seat(); struct sway_workspace *focused_ws = seat_get_focused_workspace(seat); - struct sway_output *focused_output = NULL; - if (focused_ws != NULL) { - focused_output = focused_ws->output; - } + struct sway_output *focused_output = focused_ws ? focused_ws->output : NULL; - if (surf->lock->focused == NULL || focused_output == surf->output) { + // Only set the initial focus surface when it's on the focused output. + // Fallback to the first mapped surface if no focused output can be found. + if (focused_output == surf->output + || (!focused_output && surf->lock->focused == NULL)) { focus_surface(surf->lock, surf->surface->surface); } cursor_rebase_all(); From c2ae62e21c1a7e4ae6815b64413b96c807c6e669 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Fri, 15 May 2026 13:02:30 +0200 Subject: [PATCH 5/5] Set lock focus per-seat --- include/sway/input/seat.h | 1 + include/sway/server.h | 3 +- sway/input/seat.c | 4 +-- sway/lock.c | 68 ++++++++++++++++++++++----------------- 4 files changed, 43 insertions(+), 33 deletions(-) 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 64419de03..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 @@ -182,7 +181,7 @@ void sway_session_lock_add_output(struct sway_session_lock *lock, 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_output *output); + 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 ca6c2ec2e..e7a95ea64 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1269,11 +1269,11 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { // 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, 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, server.session_lock.lock->focused, false); + seat_set_focus_surface(seat, seat->focused_lock, false); } } diff --git a/sway/lock.c b/sway/lock.c index 43384fdb5..d4d59adf7 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -30,38 +30,39 @@ 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_output *output) { + 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) { @@ -70,8 +71,7 @@ void sway_session_lock_focus_output(struct sway_session_lock *lock, } if (candidate->surface->surface->mapped) { - // Set the focus for all seats - focus_surface(lock, candidate->surface->surface); + focus_surface(lock, seat, candidate->surface->surface); break; } } @@ -80,15 +80,18 @@ void sway_session_lock_focus_output(struct sway_session_lock *lock, static void handle_surface_map(struct wl_listener *listener, void *data) { struct sway_session_lock_output *surf = wl_container_of(listener, surf, surface_map); - struct sway_seat *seat = input_manager_current_seat(); - struct sway_workspace *focused_ws = seat_get_focused_workspace(seat); - struct sway_output *focused_output = focused_ws ? focused_ws->output : NULL; + 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 surface when it's on the focused output. - // Fallback to the first mapped surface if no focused output can be found. - if (focused_output == surf->output - || (!focused_output && surf->lock->focused == NULL)) { - focus_surface(surf->lock, surf->surface->surface); + // 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(); } @@ -224,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; } @@ -245,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) {