From a8da208453ccc748bf331e83956b85d43ef0f05f Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sun, 23 Jun 2019 14:53:59 +0200 Subject: [PATCH] desktop-shell: make sure child window stays active If a xdg_toplevel surface has a child (or multiple), the desktop shell still allows to activate the parent. This can be problematic with modal dialogs such as message boxes which then are hidden behind the main window, which might be non-responsive to inputs at this this point. The protocol specifies set_parent as follows: "Set the 'parent' of this surface. This surface should be stacked above the parent surface and all other ancestor surfaces." Track parent/child relationship in desktop-shell. Follow the protocol recommendation and make sure the child stays stacked above the parent. Fixes: #231 Signed-off-by: Stefan Agner --- desktop-shell/shell.c | 53 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index a05a5fe0..7d9d703c 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -104,6 +104,9 @@ struct shell_surface { struct desktop_shell *shell; + struct wl_list children_list; + struct wl_list children_link; + int32_t saved_x, saved_y; bool saved_position_valid; bool saved_rotation_valid; @@ -2416,6 +2419,13 @@ desktop_surface_added(struct weston_desktop_surface *desktop_surface, wl_list_init(&shsurf->workspace_transform.link); + /* + * initialize list as well as link. The latter allows to use + * wl_list_remove() even when this surface is not in another list. + */ + wl_list_init(&shsurf->children_list); + wl_list_init(&shsurf->children_link); + weston_desktop_surface_set_user_data(desktop_surface, shsurf); weston_desktop_surface_set_activated(desktop_surface, shsurf->focus_count > 0); @@ -2427,12 +2437,19 @@ desktop_surface_removed(struct weston_desktop_surface *desktop_surface, { struct shell_surface *shsurf = weston_desktop_surface_get_user_data(desktop_surface); + struct shell_surface *shsurf_child, *tmp; struct weston_surface *surface = weston_desktop_surface_get_surface(desktop_surface); if (!shsurf) return; + wl_list_for_each_safe(shsurf_child, tmp, &shsurf->children_list, children_link) { + wl_list_remove(&shsurf_child->children_link); + wl_list_init(&shsurf_child->children_link); + } + wl_list_remove(&shsurf->children_link); + wl_signal_emit(&shsurf->destroy_signal, shsurf); if (shsurf->fullscreen.black_view) @@ -2747,6 +2764,20 @@ desktop_surface_resize(struct weston_desktop_surface *desktop_surface, wl_resource_post_no_memory(resource); } +static void +desktop_surface_set_parent(struct weston_desktop_surface *desktop_surface, + struct weston_desktop_surface *parent, + void *shell) +{ + struct shell_surface *shsurf = + weston_desktop_surface_get_user_data(desktop_surface); + struct shell_surface *shsurf_parent = + weston_desktop_surface_get_user_data(parent); + + wl_list_insert(shsurf_parent->children_list.prev, + &shsurf->children_link); +} + static void desktop_surface_fullscreen_requested(struct weston_desktop_surface *desktop_surface, bool fullscreen, @@ -2929,6 +2960,7 @@ static const struct weston_desktop_api shell_desktop_api = { .committed = desktop_surface_committed, .move = desktop_surface_move, .resize = desktop_surface_resize, + .set_parent = desktop_surface_set_parent, .fullscreen_requested = desktop_surface_fullscreen_requested, .maximized_requested = desktop_surface_maximized_requested, .minimized_requested = desktop_surface_minimized_requested, @@ -3777,6 +3809,18 @@ lower_fullscreen_layer(struct desktop_shell *shell, } } +static struct shell_surface *get_last_child(struct shell_surface *shsurf) +{ + struct shell_surface *shsurf_child; + + wl_list_for_each_reverse(shsurf_child, &shsurf->children_list, children_link) { + if (weston_view_is_mapped(shsurf_child->view)) + return shsurf_child; + } + + return NULL; +} + void activate(struct desktop_shell *shell, struct weston_view *view, struct weston_seat *seat, uint32_t flags) @@ -3786,12 +3830,19 @@ activate(struct desktop_shell *shell, struct weston_view *view, struct focus_state *state; struct workspace *ws; struct weston_surface *old_es; - struct shell_surface *shsurf; + struct shell_surface *shsurf, *shsurf_child; main_surface = weston_surface_get_main_surface(es); shsurf = get_shell_surface(main_surface); assert(shsurf); + shsurf_child = get_last_child(shsurf); + if (shsurf_child) { + /* Activate last xdg child instead of parent. */ + activate(shell, shsurf_child->view, seat, flags); + return; + } + /* Only demote fullscreen surfaces on the output of activated shsurf. * Leave fullscreen surfaces on unrelated outputs alone. */ if (shsurf->output)