From 7e72bf975fb65c8290b398d21b2ad9d87a22880f Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sat, 23 Sep 2023 11:51:47 -0400 Subject: [PATCH] view/xwayland: avoid focusing views that don't want focus XWayland views can self-declare that they don't want keyboard focus via the ICCCM WM_HINTS property. Most of the logic is already in place to avoid giving focus to such views (e.g. taskbars). Add a couple of missing pieces to make this work: - Hook up view_isfocusable() to look at WM_HINTS for XWayland views - Adjust desktop_focus_topmost_mapped_view() to skip unfocusable views --- include/labwc.h | 4 ++-- include/view.h | 2 ++ src/desktop.c | 8 ++++---- src/seat.c | 2 +- src/session-lock.c | 2 +- src/view.c | 3 +++ src/workspaces.c | 2 +- src/xdg.c | 2 +- src/xwayland-unmanaged.c | 4 ++-- src/xwayland.c | 13 ++++++++++++- 10 files changed, 29 insertions(+), 13 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index 78280b24..41c9a668 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -378,7 +378,7 @@ void foreign_toplevel_update_outputs(struct view *view); void desktop_focus_view(struct view *view, bool raise); void desktop_arrange_all_views(struct server *server); void desktop_focus_output(struct output *output); -struct view *desktop_topmost_mapped_view(struct server *server); +struct view *desktop_topmost_focusable_view(struct server *server); enum lab_cycle_dir { LAB_CYCLE_DIR_NONE, @@ -393,7 +393,7 @@ enum lab_cycle_dir { */ struct view *desktop_cycle_view(struct server *server, struct view *start_view, enum lab_cycle_dir dir); -void desktop_focus_topmost_mapped_view(struct server *server); +void desktop_focus_topmost_view(struct server *server); void keyboard_cancel_keybind_repeat(struct keyboard *keyboard); void keyboard_key_notify(struct wl_listener *listener, void *data); diff --git a/include/view.h b/include/view.h index d5eb5838..4a408a84 100644 --- a/include/view.h +++ b/include/view.h @@ -69,6 +69,8 @@ struct view_impl { struct view *(*get_root)(struct view *self); void (*append_children)(struct view *self, struct wl_array *children); struct view_size_hints (*get_size_hints)(struct view *self); + /* if not implemented, view is assumed to want focus */ + bool (*wants_focus)(struct view *self); }; struct view { diff --git a/src/desktop.c b/src/desktop.c index 4d2deba1..7376555e 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -190,7 +190,7 @@ desktop_cycle_view(struct server *server, struct view *start_view, } struct view * -desktop_topmost_mapped_view(struct server *server) +desktop_topmost_focusable_view(struct server *server) { struct view *view; struct wl_list *node_list; @@ -202,7 +202,7 @@ desktop_topmost_mapped_view(struct server *server) continue; } view = node_view_from_node(node); - if (view->mapped) { + if (view->mapped && view_isfocusable(view)) { return view; } } @@ -210,9 +210,9 @@ desktop_topmost_mapped_view(struct server *server) } void -desktop_focus_topmost_mapped_view(struct server *server) +desktop_focus_topmost_view(struct server *server) { - struct view *view = desktop_topmost_mapped_view(server); + struct view *view = desktop_topmost_focusable_view(server); if (view) { desktop_focus_view(view, /*raise*/ true); } else { diff --git a/src/seat.c b/src/seat.c index c16f86dc..bb6729a0 100644 --- a/src/seat.c +++ b/src/seat.c @@ -496,7 +496,7 @@ seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer) { if (!layer) { seat->focused_layer = NULL; - desktop_focus_topmost_mapped_view(seat->server); + desktop_focus_topmost_view(seat->server); return; } seat_focus(seat, layer->surface); diff --git a/src/session-lock.c b/src/session-lock.c index 4e09e1ee..fe920f41 100644 --- a/src/session-lock.c +++ b/src/session-lock.c @@ -227,7 +227,7 @@ handle_unlock(struct wl_listener *listener, void *data) { struct session_lock *lock = wl_container_of(listener, lock, unlock); session_lock_destroy(lock); - desktop_focus_topmost_mapped_view(g_server); + desktop_focus_topmost_view(g_server); } static void diff --git a/src/view.c b/src/view.c index 6e41f382..743c9290 100644 --- a/src/view.c +++ b/src/view.c @@ -118,6 +118,9 @@ view_isfocusable(struct view *view) if (!view->surface) { return false; } + if (view->impl->wants_focus && !view->impl->wants_focus(view)) { + return false; + } return (view->mapped || view->minimized); } diff --git a/src/workspaces.c b/src/workspaces.c index 19606ffa..bd9bdab0 100644 --- a/src/workspaces.c +++ b/src/workspaces.c @@ -289,7 +289,7 @@ workspaces_switch_to(struct workspace *target, bool update_focus) if (update_focus) { struct view *view = server->focused_view; if (!view || !view_is_always_on_top(view)) { - desktop_focus_topmost_mapped_view(server); + desktop_focus_topmost_view(server); } } diff --git a/src/xdg.c b/src/xdg.c index 65f164d3..f916087d 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -554,7 +554,7 @@ xdg_toplevel_view_unmap(struct view *view, bool client_request) view->mapped = false; wlr_scene_node_set_enabled(&view->scene_tree->node, false); wl_list_remove(&view->commit.link); - desktop_focus_topmost_mapped_view(view->server); + desktop_focus_topmost_view(view->server); } } diff --git a/src/xwayland-unmanaged.c b/src/xwayland-unmanaged.c index 7f7454a2..8f8faf79 100644 --- a/src/xwayland-unmanaged.c +++ b/src/xwayland-unmanaged.c @@ -88,7 +88,7 @@ focus_next_surface(struct server *server, struct wlr_xwayland_surface *xsurface) * to the topmost mapped view. This fixes dmenu * not giving focus back when closed with ESC. */ - desktop_focus_topmost_mapped_view(server); + desktop_focus_topmost_view(server); } static void @@ -166,7 +166,7 @@ unmanaged_handle_request_activate(struct wl_listener *listener, void *data) * Validate that the unmanaged surface trying to grab focus is actually * a child of the topmost mapped view before granting the request. */ - struct view *view = desktop_topmost_mapped_view(server); + struct view *view = desktop_topmost_focusable_view(server); if (view && view->type == LAB_XWAYLAND_VIEW) { struct wlr_xwayland_surface *surf = wlr_xwayland_surface_from_wlr_surface(view->surface); diff --git a/src/xwayland.c b/src/xwayland.c index 4423345b..57094d7f 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -30,6 +30,16 @@ xwayland_view_get_size_hints(struct view *view) }; } +static bool +xwayland_view_wants_focus(struct view *view) +{ + xcb_icccm_wm_hints_t *hints = xwayland_surface_from_view(view)->hints; + if (!hints) { + return true; + } + return (bool)hints->input; +} + static struct wlr_xwayland_surface * top_parent_of(struct view *view) { @@ -505,7 +515,7 @@ xwayland_view_unmap(struct view *view, bool client_request) view->mapped = false; wl_list_remove(&view->commit.link); wlr_scene_node_set_enabled(&view->scene_tree->node, false); - desktop_focus_topmost_mapped_view(view->server); + desktop_focus_topmost_view(view->server); /* * If the view was explicitly unmapped by the client (rather @@ -634,6 +644,7 @@ static const struct view_impl xwayland_view_impl = { .get_root = xwayland_view_get_root, .append_children = xwayland_view_append_children, .get_size_hints = xwayland_view_get_size_hints, + .wants_focus = xwayland_view_wants_focus, }; void