]> git.mdlowis.com Git - proto/labwc.git/commitdiff
layer: improve keyboard-interactivity
authorJohan Malm <jgm323@gmail.com>
Sat, 28 Jan 2023 22:34:27 +0000 (22:34 +0000)
committerJohan Malm <johanmalm@users.noreply.github.com>
Sun, 29 Jan 2023 14:31:49 +0000 (14:31 +0000)
- 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

src/desktop.c
src/layers.c
src/seat.c

index ce17d8d3f56d91e0be93536aa416cf0a2ed89d5a..0e9827259096a3cd06beee2772db3d9ce3e57386 100644 (file)
@@ -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
index 4859c402b640a59a4d91b14faa84fdecc50ea2ae..bd0b0e17db2597dd26cba04f143e063634639a11 100644 (file)
@@ -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
index 9380fb11cb0c0627d73f28b81a5444a70b3478f0..4d35809b65e050b70aad29094709832dba5388bc 100644 (file)
@@ -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;
        }