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
This commit is contained in:
John Lindgren 2023-09-23 11:51:47 -04:00 committed by Johan Malm
parent d503370a77
commit 7e72bf975f
10 changed files with 29 additions and 13 deletions

View file

@ -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);

View file

@ -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 {

View file

@ -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 {

View file

@ -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);

View file

@ -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

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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