layer: change focus better on 'none' keyboard-interactivity request

...and on unmap.

Add `try_to_focus_next_layer_or_toplevel()` which does the following
(in order of precedence):

- Give focus to last added overlay/top layer-shell client with exclusive
  interactivity on the output nearest the pointer (normally the one where
  the users is currently working). The reason for not considering clients
  on all outputs is that giving focus to a client on another output may be
  confusing to the user.

- Give focus to topmost toplevel if one exists (this was done previously
  anyway).
This commit is contained in:
Johan Malm 2024-03-09 15:03:26 +00:00 committed by Johan Malm
parent 06bf71162e
commit cb361fbb55

View file

@ -118,6 +118,62 @@ handle_output_destroy(struct wl_listener *listener, void *data)
wlr_layer_surface_v1_destroy(layer->scene_layer_surface->layer_surface);
}
static inline bool
has_exclusive_interactivity(struct wlr_scene_layer_surface_v1 *scene)
{
return scene->layer_surface->current.keyboard_interactive
== ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
}
/*
* Try to transfer focus to other layer-shell clients with exclusive focus on
* the output nearest to the cursor. If none exist (which is likely to generally
* be the case) just unset layer focus and try to give it to the topmost
* toplevel if one exists.
*/
static void
try_to_focus_next_layer_or_toplevel(struct server *server)
{
struct seat *seat = &server->seat;
struct output *output = output_nearest_to_cursor(server);
enum zwlr_layer_shell_v1_layer overlay = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY;
enum zwlr_layer_shell_v1_layer top = ZWLR_LAYER_SHELL_V1_LAYER_TOP;
for (size_t i = overlay; i >= top; i--) {
struct wlr_scene_tree *tree = output->layer_tree[i];
struct wlr_scene_node *node;
/*
* In wlr_scene.c they were added at end of list so we
* iterate in reverse to process last client first.
*/
wl_list_for_each_reverse(node, &tree->children, link) {
struct lab_layer_surface *layer = node_layer_surface_from_node(node);
struct wlr_scene_layer_surface_v1 *scene = layer->scene_layer_surface;
struct wlr_layer_surface_v1 *layer_surface = scene->layer_surface;
/*
* In case we have just come from the unmap handler and
* the commit has not yet been processed.
*/
if (!layer_surface->surface->mapped) {
continue;
}
if (has_exclusive_interactivity(scene)) {
wlr_log(WLR_DEBUG, "focus next exclusive layer client");
seat_set_focus_layer(seat, layer_surface);
return;
}
}
}
/*
* Unfocus the current layer-surface and focus the topmost toplevel if
* one exists on the current workspace.
*/
if (seat->focused_layer) {
seat_set_focus_layer(seat, NULL);
}
}
static bool
focused_layer_has_exclusive_interactivity(struct seat *seat)
{
@ -166,14 +222,7 @@ layer_try_set_focus(struct seat *seat, struct wlr_layer_surface_v1 *layer_surfac
case ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE:
wlr_log(WLR_DEBUG, "interactive-none '%p'", layer_surface);
if (seat->focused_layer == layer_surface) {
wlr_log(WLR_DEBUG, "unset focus '%p'", layer_surface);
/*
* TODO: consider transferring focus other layer-shell
* clients with exclusive focus (that in the case of
* multiple clients we could have stolen it from and
* arguably should give it back to).
*/
seat_set_focus_layer(seat, NULL);
try_to_focus_next_layer_or_toplevel(seat->server);
}
break;
}
@ -223,7 +272,6 @@ handle_node_destroy(struct wl_listener *listener, void *data)
{
struct lab_layer_surface *layer =
wl_container_of(listener, layer, node_destroy);
/*
* TODO: Determine if this layer is being used by an exclusive client.
* If it is, try and find another layer owned by this client to pass
@ -250,7 +298,7 @@ handle_unmap(struct wl_listener *listener, void *data)
}
struct seat *seat = &layer->server->seat;
if (seat->focused_layer == layer_surface) {
seat_set_focus_layer(seat, NULL);
try_to_focus_next_layer_or_toplevel(layer->server);
}
}