]> git.mdlowis.com Git - proto/labwc.git/commitdiff
xwayland: use wlr_xwayland_surface_offer_focus()
authorJohn Lindgren <john@jlindgren.net>
Sat, 20 Jul 2024 15:25:10 +0000 (11:25 -0400)
committerConsolatis <35009135+Consolatis@users.noreply.github.com>
Wed, 21 May 2025 18:30:19 +0000 (20:30 +0200)
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

include/view.h
include/xwayland.h
src/desktop.c
src/view.c
src/xwayland.c

index dc309cddfe294e28aacc3a88b3aa77baeb5f59a1..f551f854562cefc5a6ed1dbe22699925aed964b3 100644 (file)
@@ -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);
index 31d923a1121b3aba4437279f03ad338d1f29caf7..0a5f7c105610ce925a7b55033251dc263b772c12 100644 (file)
@@ -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 */
index 67ec882586dbcd581ae19d2ab82e72f199d71571..226b0225f9e2751aff1cc61a35ce94a9704a7d40 100644 (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) {
index c520a947de8a97f7a373f0ef22703d3426e869a9..a9797fef3807c503ffcae21111ab82faffb2b45b 100644 (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.
index cbe861288083903b4d74153b5088b1fab9e68670..c64dd3e87c0c3fba5e3c3776c801bc991e034cfa 100644 (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);