From 22b193254ca18e30d6145a381d959370febc5290 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Mon, 9 Oct 2023 23:25:53 -0400 Subject: [PATCH] experimental: use wlr_xwayland_surface_offer_focus() Offer focus by sending WM_TAKE_FOCUS to a client window supporting it. The client may accept or ignore the offer. If it accepts, the surface will emit a focus_in signal notifying the compositor that it has received focus. The compositor should then call wlr_xwayland_surface_activate(surface, true). This is a more compatible method of giving focus to windows using the Globally Active input model (see wlr_xwayland_icccm_input_model()) than calling wlr_xwayland_surface_activate() unconditionally, since there is no reliable way to know in advance whether these windows want to be focused. --- include/view.h | 3 +++ include/xwayland.h | 1 + src/desktop.c | 23 +++++++++++++---------- src/view.c | 9 +++++++++ src/xwayland.c | 21 +++++++++++++++++++++ 5 files changed, 47 insertions(+), 10 deletions(-) diff --git a/include/view.h b/include/view.h index 0cd17192..9be632a7 100644 --- a/include/view.h +++ b/include/view.h @@ -94,6 +94,7 @@ struct view_impl { struct view_size_hints (*get_size_hints)(struct view *self); /* if not implemented, VIEW_WANTS_FOCUS_ALWAYS is assumed */ enum view_wants_focus (*wants_focus)(struct view *self); + void (*offer_focus)(struct view *self); }; struct view { @@ -320,6 +321,8 @@ view_is_focusable(struct view *view) { return view_is_focusable_from(view, NULL); } +void view_offer_focus(struct view *view); + void view_toggle_keybinds(struct view *view); void view_set_activated(struct view *view, bool activated); diff --git a/include/xwayland.h b/include/xwayland.h index d8dfb68a..660e6f5d 100644 --- a/include/xwayland.h +++ b/include/xwayland.h @@ -34,6 +34,7 @@ struct xwayland_view { struct wl_listener set_decorations; struct wl_listener set_override_redirect; struct wl_listener set_strut_partial; + struct wl_listener focus_in; /* Not (yet) implemented */ /* struct wl_listener set_role; */ diff --git a/src/desktop.c b/src/desktop.c index ab057fbe..c7718efc 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -67,16 +67,19 @@ desktop_focus_view(struct view *view, bool raise) workspaces_switch_to(view->workspace, /*update_focus*/ false); } - /* - * Give input focus, even if the view claims not to want it (see - * view->impl->wants_focus). This is a workaround for so-called - * "globally active" X11 views (MATLAB known to be one such) - * that expect to be able to control focus themselves, but can't - * under labwc since it's disallowed at the wlroots level. - */ - struct seat *seat = &view->server->seat; - if (view->surface != seat->seat->keyboard_state.focused_surface) { - seat_focus_surface(seat, view->surface); + switch (view_wants_focus(view)) { + case VIEW_WANTS_FOCUS_ALWAYS: + ; /* works around "a label can only be part of a statement" */ + struct seat *seat = &view->server->seat; + if (view->surface != seat->seat->keyboard_state.focused_surface) { + seat_focus_surface(seat, view->surface); + } + break; + case VIEW_WANTS_FOCUS_OFFER: + view_offer_focus(view); + break; + case VIEW_WANTS_FOCUS_NEVER: + break; } if (raise) { diff --git a/src/view.c b/src/view.c index 106a2aa7..17bd5ab3 100644 --- a/src/view.c +++ b/src/view.c @@ -178,6 +178,15 @@ view_is_focusable_from(struct view *view, struct wlr_surface *prev) && prev && view_is_related(view, prev))); } +void +view_offer_focus(struct view *view) +{ + assert(view); + if (view->impl->offer_focus) { + view->impl->offer_focus(view); + } +} + /** * All view_apply_xxx_geometry() functions must *not* modify * any state besides repositioning or resizing the view. diff --git a/src/xwayland.c b/src/xwayland.c index 0f289a30..7d6570eb 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -100,6 +100,12 @@ xwayland_view_wants_focus(struct view *view) return VIEW_WANTS_FOCUS_NEVER; } +static void +xwayland_view_offer_focus(struct view *view) +{ + wlr_xwayland_surface_offer_focus(xwayland_surface_from_view(view)); +} + static struct wlr_xwayland_surface * top_parent_of(struct view *view) { @@ -280,6 +286,7 @@ handle_destroy(struct wl_listener *listener, void *data) wl_list_remove(&xwayland_view->set_decorations.link); wl_list_remove(&xwayland_view->set_override_redirect.link); wl_list_remove(&xwayland_view->set_strut_partial.link); + wl_list_remove(&xwayland_view->focus_in.link); view_destroy(view); } @@ -467,6 +474,18 @@ handle_set_strut_partial(struct wl_listener *listener, void *data) } } +static void +handle_focus_in(struct wl_listener *listener, void *data) +{ + struct xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, focus_in); + struct view *view = &xwayland_view->base; + struct seat *seat = &view->server->seat; + if (view->surface != seat->seat->keyboard_state.focused_surface) { + seat_focus_surface(seat, view->surface); + } +} + static void set_initial_position(struct view *view, struct wlr_xwayland_surface *xwayland_surface) @@ -790,6 +809,7 @@ static const struct view_impl xwayland_view_impl = { .is_related = xwayland_view_is_related, .get_size_hints = xwayland_view_get_size_hints, .wants_focus = xwayland_view_wants_focus, + .offer_focus = xwayland_view_offer_focus, }; void @@ -834,6 +854,7 @@ xwayland_view_create(struct server *server, CONNECT_SIGNAL(xsurface, xwayland_view, set_decorations); CONNECT_SIGNAL(xsurface, xwayland_view, set_override_redirect); CONNECT_SIGNAL(xsurface, xwayland_view, set_strut_partial); + CONNECT_SIGNAL(xsurface, xwayland_view, focus_in); wl_list_insert(&view->server->views, &view->link);