diff --git a/include/view.h b/include/view.h index 0fb10a22..12b5d109 100644 --- a/include/view.h +++ b/include/view.h @@ -36,6 +36,21 @@ enum view_edge { VIEW_EDGE_CENTER, }; +enum view_wants_focus { + /* View does not want focus */ + VIEW_WANTS_FOCUS_NEVER = 0, + /* View wants focus */ + VIEW_WANTS_FOCUS_ALWAYS, + /* + * View should be offered focus and may accept or decline + * (a.k.a. ICCCM Globally Active input model). Labwc generally + * avoids focusing these views automatically (e.g. when another + * view on top is closed) but they may be focused by user action + * (e.g. mouse click). + */ + VIEW_WANTS_FOCUS_OFFER, +}; + struct view; struct wlr_surface; @@ -72,8 +87,8 @@ struct view_impl { /* determines if view and surface are owned by the same process */ bool (*is_related)(struct view *self, struct wlr_surface *surface); 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); + /* if not implemented, VIEW_WANTS_FOCUS_ALWAYS is assumed */ + enum view_wants_focus (*wants_focus)(struct view *self); }; struct view { @@ -273,6 +288,8 @@ struct view *view_next(struct wl_list *head, struct view *view, void view_array_append(struct server *server, struct wl_array *views, enum lab_view_criteria criteria); +enum view_wants_focus view_wants_focus(struct view *view); + /** * view_is_focusable() - Check whether or not a view can be focused * @view: view to be checked diff --git a/src/view.c b/src/view.c index 7a390d11..dcca4bcd 100644 --- a/src/view.c +++ b/src/view.c @@ -143,6 +143,16 @@ view_array_append(struct server *server, struct wl_array *views, } } +enum view_wants_focus +view_wants_focus(struct view *view) +{ + assert(view); + if (view->impl->wants_focus) { + return view->impl->wants_focus(view); + } + return VIEW_WANTS_FOCUS_ALWAYS; +} + bool view_is_focusable(struct view *view) { @@ -150,7 +160,7 @@ view_is_focusable(struct view *view) if (!view->surface) { return false; } - if (view->impl->wants_focus && !view->impl->wants_focus(view)) { + if (view_wants_focus(view) != VIEW_WANTS_FOCUS_ALWAYS) { return false; } return (view->mapped || view->minimized); diff --git a/src/xwayland.c b/src/xwayland.c index e2c74fb1..a8109bfe 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -30,29 +30,61 @@ xwayland_view_get_size_hints(struct view *view) }; } -static bool +static enum view_wants_focus xwayland_view_wants_focus(struct view *view) { - xcb_icccm_wm_hints_t *hints = xwayland_surface_from_view(view)->hints; - if (!hints) { - return true; - } + struct wlr_xwayland_surface *xsurface = + xwayland_surface_from_view(view); + + switch (wlr_xwayland_icccm_input_model(xsurface)) { /* - * Paraphrased from ICCCM section 4.1.7 (Input Focus): + * Abbreviated from ICCCM section 4.1.7 (Input Focus): * - * Clients set the input field of WM_HINTS to True to indicate - * that they require window manager assistance in acquiring the - * input focus. Clients set the input field to False to request - * that the window manager not set the input focus to their - * top-level window. + * Passive Input - The client expects keyboard input but never + * explicitly sets the input focus. + * Locally Active Input - The client expects keyboard input and + * explicitly sets the input focus, but it only does so when one + * of its windows already has the focus. * - * Clients that use XSetInputFocus() to explicitly set the input - * focus should set the WM_TAKE_FOCUS atom in WM_PROTOCOLS. - * Currently, labwc does not support this method of taking focus - * and thus ignores WM_TAKE_FOCUS. These views can still be - * focused by explicit user action (e.g. clicking in them). + * Passive and Locally Active clients set the input field of + * WM_HINTS to True, which indicates that they require window + * manager assistance in acquiring the input focus. */ - return (bool)hints->input; + case WLR_ICCCM_INPUT_MODEL_PASSIVE: + case WLR_ICCCM_INPUT_MODEL_LOCAL: + return VIEW_WANTS_FOCUS_ALWAYS; + + /* + * Globally Active Input - The client expects keyboard input and + * explicitly sets the input focus, even when it is in windows + * the client does not own. ... It wants to prevent the window + * manager from setting the input focus to any of its windows + * [because it may or may not want focus]. + * + * 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 + * and avoids automatically focusing Globally Active windows. + * This may change in future.] + */ + case WLR_ICCCM_INPUT_MODEL_GLOBAL: + return VIEW_WANTS_FOCUS_OFFER; + + /* + * No Input - The client never expects keyboard input. + * + * No Input and Globally Active clients set the input field to + * False, which requests that the window manager not set the + * input focus to their top-level window. + */ + case WLR_ICCCM_INPUT_MODEL_NONE: + break; + } + + return VIEW_WANTS_FOCUS_NEVER; } static struct wlr_xwayland_surface *