From 9782ffa868173a13bb61d40388ec4d8460d6a462 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Fri, 13 Jun 2025 11:00:26 -0400 Subject: [PATCH] desktop: give focus to a modal dialog rather than its parent Fixes: #2722 --- include/view.h | 9 +++++++++ src/desktop.c | 42 +++++++++++++++++++++++++++--------------- src/view.c | 30 ++++++++++++++++++++++++++++++ src/xwayland.c | 7 +++++++ 4 files changed, 73 insertions(+), 15 deletions(-) diff --git a/include/view.h b/include/view.h index f3a0a9c2..2ea83cd7 100644 --- a/include/view.h +++ b/include/view.h @@ -156,6 +156,7 @@ struct view_impl { void (*minimize)(struct view *view, bool minimize); struct view *(*get_root)(struct view *self); void (*append_children)(struct view *self, struct wl_array *children); + bool (*is_modal_dialog)(struct view *self); 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); @@ -612,6 +613,14 @@ void view_move_to_front(struct view *view); void view_move_to_back(struct view *view); struct view *view_get_root(struct view *view); void view_append_children(struct view *view, struct wl_array *children); + +/** + * view_get_modal_dialog() - returns any modal dialog found among this + * view's children or siblings (or possibly this view itself). Applies + * only to xwayland views and always returns NULL for xdg-shell views. + */ +struct view *view_get_modal_dialog(struct view *view); + bool view_on_output(struct view *view, struct output *output); /** diff --git a/src/desktop.c b/src/desktop.c index df179eb2..28af6cc6 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -39,6 +39,25 @@ desktop_arrange_all_views(struct server *server) } } +static void +set_or_offer_focus(struct view *view) +{ + struct seat *seat = &view->server->seat; + switch (view_wants_focus(view)) { + case VIEW_WANTS_FOCUS_ALWAYS: + if (view->surface != seat->seat->keyboard_state.focused_surface) { + seat_focus_surface(seat, view->surface); + } + break; + case VIEW_WANTS_FOCUS_LIKELY: + case VIEW_WANTS_FOCUS_UNLIKELY: + view_offer_focus(view); + break; + case VIEW_WANTS_FOCUS_NEVER: + break; + } +} + void desktop_focus_view(struct view *view, bool raise) { @@ -77,24 +96,17 @@ desktop_focus_view(struct view *view, bool raise) workspaces_switch_to(view->workspace, /*update_focus*/ false); } - struct seat *seat = &view->server->seat; - switch (view_wants_focus(view)) { - case VIEW_WANTS_FOCUS_ALWAYS: - if (view->surface != seat->seat->keyboard_state.focused_surface) { - seat_focus_surface(seat, view->surface); - } - break; - case VIEW_WANTS_FOCUS_LIKELY: - case VIEW_WANTS_FOCUS_UNLIKELY: - view_offer_focus(view); - break; - case VIEW_WANTS_FOCUS_NEVER: - break; - } - if (raise) { view_move_to_front(view); } + + /* + * If any child/sibling of the view is a modal dialog, focus + * the dialog instead. It does not need to be raised separately + * since view_move_to_front() raises all sibling views together. + */ + struct view *dialog = view_get_modal_dialog(view); + set_or_offer_focus(dialog ? dialog : view); } /* TODO: focus layer-shell surfaces also? */ diff --git a/src/view.c b/src/view.c index 2c4560be..27a1d535 100644 --- a/src/view.c +++ b/src/view.c @@ -2343,6 +2343,36 @@ view_append_children(struct view *view, struct wl_array *children) } } +struct view * +view_get_modal_dialog(struct view *view) +{ + assert(view); + if (!view->impl->is_modal_dialog) { + return NULL; + } + /* check view itself first */ + if (view->impl->is_modal_dialog(view)) { + return view; + } + + /* check sibling views */ + struct view *dialog = NULL; + struct view *root = view_get_root(view); + struct wl_array children; + struct view **child; + + wl_array_init(&children); + view_append_children(root, &children); + wl_array_for_each(child, &children) { + if (view->impl->is_modal_dialog(*child)) { + dialog = *child; + break; + } + } + wl_array_release(&children); + return dialog; +} + bool view_has_strut_partial(struct view *view) { diff --git a/src/xwayland.c b/src/xwayland.c index dfc38add..0185e61d 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -933,6 +933,12 @@ xwayland_view_append_children(struct view *self, struct wl_array *children) } } +static bool +xwayland_view_is_modal_dialog(struct view *self) +{ + return xwayland_surface_from_view(self)->modal; +} + static void xwayland_view_set_activated(struct view *view, bool activated) { @@ -978,6 +984,7 @@ static const struct view_impl xwayland_view_impl = { .minimize = xwayland_view_minimize, .get_root = xwayland_view_get_root, .append_children = xwayland_view_append_children, + .is_modal_dialog = xwayland_view_is_modal_dialog, .get_size_hints = xwayland_view_get_size_hints, .wants_focus = xwayland_view_wants_focus, .offer_focus = xwayland_view_offer_focus, -- 2.52.0