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")
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
* 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);
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;
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;
}
}
}
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)));
}
/**