From: John Lindgren Date: Sat, 20 Jul 2024 15:25:10 +0000 (-0400) Subject: xwayland: use wlr_xwayland_surface_offer_focus() X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=c00baa16517a0b8135630219dc013a03212ab227;p=proto%2Flabwc.git xwayland: 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. v2: add caution not to use view_offer_focus() directly v3: remove obsolete comment --- diff --git a/include/view.h b/include/view.h index dc309cdd..f551f854 100644 --- a/include/view.h +++ b/include/view.h @@ -147,6 +147,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); /* returns true if view reserves space at screen edge */ bool (*has_strut_partial)(struct view *self); /* returns true if view declared itself a window type */ @@ -479,6 +480,12 @@ enum view_edge view_edge_invert(enum view_edge edge); */ bool view_is_focusable(struct view *view); +/* + * For use by desktop_focus_view() only - please do not call directly. + * See the description of VIEW_WANTS_FOCUS_OFFER for more information. + */ +void view_offer_focus(struct view *view); + void mappable_connect(struct mappable *mappable, struct wlr_surface *surface, wl_notify_func_t notify_map, wl_notify_func_t notify_unmap); void mappable_disconnect(struct mappable *mappable); diff --git a/include/xwayland.h b/include/xwayland.h index 31d923a1..0a5f7c10 100644 --- a/include/xwayland.h +++ b/include/xwayland.h @@ -69,6 +69,7 @@ struct xwayland_view { struct wl_listener set_override_redirect; struct wl_listener set_strut_partial; struct wl_listener set_window_type; + struct wl_listener focus_in; struct wl_listener map_request; /* Not (yet) implemented */ diff --git a/src/desktop.c b/src/desktop.c index 67ec8825..226b0225 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -77,16 +77,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 c520a947..a9797fef 100644 --- a/src/view.c +++ b/src/view.c @@ -388,6 +388,15 @@ view_is_focusable(struct view *view) return (view->mapped || view->minimized); } +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 cbe86128..c64dd3e8 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -94,11 +94,6 @@ xwayland_view_wants_focus(struct view *view) * Globally Active client windows may receive a WM_TAKE_FOCUS * message from the window manager. If they want the focus, they * should respond with a SetInputFocus request. - * - * [Currently, labwc does not fully support clients voluntarily - * taking focus via the WM_TAKE_FOCUS + SetInputFocus mechanism. - * Instead, we try to guess whether the window wants focus based - * on some heuristics -- see below.] */ case WLR_ICCCM_INPUT_MODEL_GLOBAL: /* @@ -135,6 +130,12 @@ xwayland_view_has_strut_partial(struct view *view) return (bool)xsurface->strut_partial; } +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) { @@ -331,6 +332,7 @@ handle_destroy(struct wl_listener *listener, void *data) wl_list_remove(&xwayland_view->set_override_redirect.link); wl_list_remove(&xwayland_view->set_strut_partial.link); wl_list_remove(&xwayland_view->set_window_type.link); + wl_list_remove(&xwayland_view->focus_in.link); wl_list_remove(&xwayland_view->map_request.link); view_destroy(view); @@ -559,6 +561,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); + } +} + /* * Sets the initial geometry of maximized/fullscreen views before * actually mapping them, so that they can do their initial layout and @@ -916,6 +930,7 @@ static const struct view_impl xwayland_view_impl = { .append_children = xwayland_view_append_children, .get_size_hints = xwayland_view_get_size_hints, .wants_focus = xwayland_view_wants_focus, + .offer_focus = xwayland_view_offer_focus, .has_strut_partial = xwayland_view_has_strut_partial, .contains_window_type = xwayland_view_contains_window_type, .get_pid = xwayland_view_get_pid, @@ -964,6 +979,7 @@ xwayland_view_create(struct server *server, CONNECT_SIGNAL(xsurface, xwayland_view, set_override_redirect); CONNECT_SIGNAL(xsurface, xwayland_view, set_strut_partial); CONNECT_SIGNAL(xsurface, xwayland_view, set_window_type); + CONNECT_SIGNAL(xsurface, xwayland_view, focus_in); CONNECT_SIGNAL(xsurface, xwayland_view, map_request); view_init(view);