desktop: allow re-focus between "globally active" views of the same PID

Commit 7e72bf975f 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): 7e72bf975f
("view/xwayland: avoid focusing views that don't want focus")
This commit is contained in:
John Lindgren 2023-10-14 16:29:13 -04:00 committed by Johan Malm
parent dd7f563a50
commit f6e3527767
3 changed files with 30 additions and 5 deletions

View file

@ -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); 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_is_focusable() - Check whether or not a view can be focused
* @view: view to be checked * @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 * The only views that are allowed to be focusd are those that have a surface
* and have been mapped at some point since creation. * 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); void view_toggle_keybinds(struct view *view);

View file

@ -192,6 +192,8 @@ desktop_cycle_view(struct server *server, struct view *start_view,
struct view * struct view *
desktop_topmost_focusable_view(struct server *server) desktop_topmost_focusable_view(struct server *server)
{ {
struct wlr_surface *prev =
server->seat.seat->keyboard_state.focused_surface;
struct view *view; struct view *view;
struct wl_list *node_list; struct wl_list *node_list;
struct wlr_scene_node *node; struct wlr_scene_node *node;
@ -202,7 +204,7 @@ desktop_topmost_focusable_view(struct server *server)
continue; continue;
} }
view = node_view_from_node(node); view = node_view_from_node(node);
if (view->mapped && view_is_focusable(view)) { if (view->mapped && view_is_focusable_from(view, prev)) {
return view; return view;
} }
} }

View file

@ -154,16 +154,28 @@ view_wants_focus(struct view *view)
} }
bool bool
view_is_focusable(struct view *view) view_is_focusable_from(struct view *view, struct wlr_surface *prev)
{ {
assert(view); assert(view);
if (!view->surface) { if (!view->surface) {
return false; return false;
} }
if (view_wants_focus(view) != VIEW_WANTS_FOCUS_ALWAYS) { if (!view->mapped && !view->minimized) {
return false; 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)));
} }
/** /**