From f6e3527767fffe81fa05073ae73893bb89a45d4f Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sat, 14 Oct 2023 16:29:13 -0400 Subject: [PATCH] desktop: allow re-focus between "globally active" views of the same PID Commit 7e72bf975fb6 changed behavior to not automatically focus xwayland views using the "Globally Active" input model (WM_HINTS.inputs = false but WM_TAKE_FOCUS listed in WM_PROTOCOLS). One undesired side effect of this change is that when a dialog is closed, the parent window is not re-focused if "Globally Active". This issue is seen for example with JDownloader. It can be solved taking a similar approach to what is done for unmanaged xwayland views: allow automatic re-focus between views sharing the same PID. Note that it's difficult to completely solve all of the focus issues with Globally Active views without proper WM_TAKE_FOCUS support. Implementing proper support is difficult since it requires wlroots changes and would also mean waiting for a message round-trip in desktop_focus_topmost_view(). Fixes (partially): 7e72bf975fb65c8290b398d21b2ad9d87a22880f ("view/xwayland: avoid focusing views that don't want focus") --- include/view.h | 13 ++++++++++++- src/desktop.c | 4 +++- src/view.c | 18 +++++++++++++++--- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/include/view.h b/include/view.h index 12b5d109..b3cc0368 100644 --- a/include/view.h +++ b/include/view.h @@ -290,6 +290,14 @@ void view_array_append(struct server *server, struct wl_array *views, enum view_wants_focus view_wants_focus(struct view *view); +/** + * view_is_focusable_from() - variant of view_is_focusable() + * that takes into account the previously focused surface + * @view: view to be checked + * @prev_surface: previously focused surface + */ +bool view_is_focusable_from(struct view *view, struct wlr_surface *prev); + /** * view_is_focusable() - Check whether or not a view can be focused * @view: view to be checked @@ -302,7 +310,10 @@ enum view_wants_focus view_wants_focus(struct view *view); * The only views that are allowed to be focusd are those that have a surface * and have been mapped at some point since creation. */ -bool view_is_focusable(struct view *view); +static inline bool +view_is_focusable(struct view *view) { + return view_is_focusable_from(view, NULL); +} void view_toggle_keybinds(struct view *view); diff --git a/src/desktop.c b/src/desktop.c index 607da49c..ab057fbe 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -192,6 +192,8 @@ desktop_cycle_view(struct server *server, struct view *start_view, struct view * desktop_topmost_focusable_view(struct server *server) { + struct wlr_surface *prev = + server->seat.seat->keyboard_state.focused_surface; struct view *view; struct wl_list *node_list; struct wlr_scene_node *node; @@ -202,7 +204,7 @@ desktop_topmost_focusable_view(struct server *server) continue; } view = node_view_from_node(node); - if (view->mapped && view_is_focusable(view)) { + if (view->mapped && view_is_focusable_from(view, prev)) { return view; } } diff --git a/src/view.c b/src/view.c index dcca4bcd..813566bd 100644 --- a/src/view.c +++ b/src/view.c @@ -154,16 +154,28 @@ view_wants_focus(struct view *view) } bool -view_is_focusable(struct view *view) +view_is_focusable_from(struct view *view, struct wlr_surface *prev) { assert(view); if (!view->surface) { return false; } - if (view_wants_focus(view) != VIEW_WANTS_FOCUS_ALWAYS) { + if (!view->mapped && !view->minimized) { return false; } - return (view->mapped || view->minimized); + enum view_wants_focus wants_focus = view_wants_focus(view); + /* + * Consider "offer focus" (Globally Active) views as focusable + * only if another surface from the same application already had + * focus. The goal is to allow focusing a parent window when a + * dialog/popup is closed, but still avoid focusing standalone + * panels/toolbars/notifications. Note that we are basically + * guessing whether Globally Active views want focus, and will + * probably be wrong some of the time. + */ + return (wants_focus == VIEW_WANTS_FOCUS_ALWAYS + || (wants_focus == VIEW_WANTS_FOCUS_OFFER + && prev && view_is_related(view, prev))); } /**