From: Johan Malm Date: Sat, 28 Jan 2023 22:34:27 +0000 (+0000) Subject: layer: improve keyboard-interactivity X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=6f3043b08d0363cb40b1ada7f0cc5a885618563c;p=proto%2Flabwc.git layer: improve keyboard-interactivity - Process layer-shell keyboard interactivity in the map and commit handlers only, rather than in layers_arrange(). This allows handling of the layer-surface that emitted the event rather than iterating over all surfaces in the output layer-tree, and therefore avoids having to guess which surface should have keyboard preference (and it might not be the last one added to the list which was the assumption previously). - Prevent seat_focus_surface() from setting keyboard focus if a layer-shell surface with exclusive keyboard-interactivity has the focus. - Set cursor_context type for layer-surfaces without node-descriptors in order to set keyboard focus correctly in cursor_button_press(). Tested satisfactorily with xfce4-panel and gtk-layer-demo. Fixes #725 and #704 --- diff --git a/src/desktop.c b/src/desktop.c index ce17d8d3..0e982725 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -323,6 +323,7 @@ get_cursor_context(struct server *server) ret.type = LAB_SSD_ROOT; return ret; } + #if HAVE_XWAYLAND if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_surface *surface = lab_wlr_surface_from_node(node); @@ -382,6 +383,9 @@ get_cursor_context(struct server *server) if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_surface *surface = lab_wlr_surface_from_node(node); if (surface) { + if (wlr_surface_is_layer_surface(surface)) { + ret.type = LAB_SSD_LAYER_SURFACE; + } if (is_layer_descendant(node)) { /* * layer-shell subsurfaces need to be diff --git a/src/layers.c b/src/layers.c index 4859c402..bd0b0e17 100644 --- a/src/layers.c +++ b/src/layers.c @@ -75,38 +75,6 @@ layers_arrange(struct output *output) } output->usable_area = usable_area; - - /* Find topmost keyboard interactive layer, if such a layer exists */ - uint32_t layers_above_views[] = { - ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, - ZWLR_LAYER_SHELL_V1_LAYER_TOP, - }; - size_t nlayers = sizeof(layers_above_views) / sizeof(layers_above_views[0]); - struct lab_layer_surface *topmost = NULL; - struct wlr_scene_node *node; - for (size_t i = 0; i < nlayers; ++i) { - struct wlr_scene_tree *tree = output->layer_tree[layers_above_views[i]]; - /* Iterate in reverse to give most recent node preference */ - wl_list_for_each_reverse(node, &tree->children, link) { - struct lab_layer_surface *surface = node_layer_surface_from_node(node); - struct wlr_scene_layer_surface_v1 *scene = surface->scene_layer_surface; - if (scene->layer_surface->current.keyboard_interactive) { - topmost = surface; - break; - } - } - if (topmost) { - break; - } - } - struct seat *seat = &output->server->seat; - if (topmost) { - seat_set_focus_layer(seat, - topmost->scene_layer_surface->layer_surface); - } else if (seat->focused_layer && - !seat->focused_layer->current.keyboard_interactive) { - seat_set_focus_layer(seat, NULL); - } } static void @@ -118,6 +86,48 @@ handle_output_destroy(struct wl_listener *listener, void *data) wlr_layer_surface_v1_destroy(layer->scene_layer_surface->layer_surface); } +static void +process_keyboard_interactivity(struct lab_layer_surface *layer) +{ + struct wlr_layer_surface_v1 *layer_surface = layer->scene_layer_surface->layer_surface; + struct seat *seat = &layer->server->seat; + + if (layer_surface->current.keyboard_interactive + && layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { + /* + * Give keyboard focus to surface if + * - keyboard-interactivity is 'exclusive' or 'on-demand'; and + * - surface is in top/overlay layers; and + * - currently focused layer has a lower precedence + * + * In other words, when dealing with two surfaces with + * exclusive/on-demand keyboard-interactivity (firstly the + * currently focused 'focused_layer' and secondly the + * 'layer_surface' for which we're just responding to a + * map/commit event), the following logic applies: + * + * | focused_layer | layer_surface | who gets keyboard focus | + * |---------------|---------------|-------------------------| + * | overlay | top | focused_layer | + * | overlay | overlay | layer_surface | + * | top | top | layer_surface | + * | top | overlay | layer_surface | + */ + + if (!seat->focused_layer || seat->focused_layer->current.layer + <= layer_surface->current.layer) { + seat_set_focus_layer(seat, layer_surface); + } + } else if (seat->focused_layer + && !seat->focused_layer->current.keyboard_interactive) { + /* + * Clear focus if keyboard-interactivity has been set to + * ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE + */ + seat_set_focus_layer(seat, NULL); + } +} + static void handle_surface_commit(struct wl_listener *listener, void *data) { @@ -140,6 +150,10 @@ handle_surface_commit(struct wl_listener *listener, void *data) wlr_scene_node_reparent(&layer->scene_layer_surface->tree->node, output->layer_tree[layer_surface->current.layer]); } + /* Process keyboard-interactivity change */ + if (committed & WLR_LAYER_SURFACE_V1_STATE_KEYBOARD_INTERACTIVITY) { + process_keyboard_interactivity(layer); + } if (committed || layer->mapped != layer_surface->mapped) { layer->mapped = layer_surface->mapped; @@ -158,6 +172,12 @@ 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 + * focus to. + */ + wl_list_remove(&layer->map.link); wl_list_remove(&layer->unmap.link); wl_list_remove(&layer->surface_commit.link); @@ -197,6 +217,8 @@ handle_map(struct wl_listener *listener, void *data) * automatically based on the position of the surface and outputs in * the scene. See wlr_scene_surface_create() documentation. */ + + process_keyboard_interactivity(layer); } static void diff --git a/src/seat.c b/src/seat.c index 9380fb11..4d35809b 100644 --- a/src/seat.c +++ b/src/seat.c @@ -440,8 +440,8 @@ seat_reconfigure(struct server *server) } } -void -seat_focus_surface(struct seat *seat, struct wlr_surface *surface) +static void +seat_focus(struct seat *seat, struct wlr_surface *surface) { if (!surface) { wlr_seat_keyboard_notify_clear_focus(seat->seat); @@ -469,6 +469,17 @@ seat_focus_surface(struct seat *seat, struct wlr_surface *surface) constrain_cursor(server, constraint); } +void +seat_focus_surface(struct seat *seat, struct wlr_surface *surface) +{ + /* Respect layer-shell exlusive keyboard-interactivity. */ + if (seat->focused_layer && seat->focused_layer->current.keyboard_interactive + == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) { + return; + } + seat_focus(seat, surface); +} + void seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer) { @@ -477,7 +488,7 @@ seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer) desktop_focus_topmost_mapped_view(seat->server); return; } - seat_focus_surface(seat, layer->surface); + seat_focus(seat, layer->surface); if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { seat->focused_layer = layer; }