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
This commit is contained in:
John Lindgren 2024-07-20 11:25:10 -04:00 committed by Consolatis
parent 95552c261d
commit c00baa1651
5 changed files with 51 additions and 15 deletions

View file

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

View file

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

View file

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