diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index 90371282..db1d7bd5 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -418,9 +418,11 @@ void wlr_xdg_toplevel_send_close(struct wlr_xdg_toplevel *toplevel); /** * Sets the parent of this toplevel. Parent can be NULL. + * + * Returns true on success, false if setting the parent would create a loop. */ -void wlr_xdg_toplevel_set_parent(struct wlr_xdg_toplevel *toplevel, - struct wlr_xdg_toplevel *parent); +bool wlr_xdg_toplevel_set_parent(struct wlr_xdg_toplevel *toplevel, + struct wlr_xdg_toplevel *parent); /** * Request that this popup closes. diff --git a/protocol/meson.build b/protocol/meson.build index a6e5b465..35aee359 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -1,5 +1,5 @@ wayland_protos = dependency('wayland-protocols', - version: '>=1.26', + version: '>=1.27', fallback: 'wayland-protocols', default_options: ['tests=false'], ) diff --git a/types/wlr_xdg_foreign_v1.c b/types/wlr_xdg_foreign_v1.c index 8213c70b..9cca7714 100644 --- a/types/wlr_xdg_foreign_v1.c +++ b/types/wlr_xdg_foreign_v1.c @@ -107,6 +107,14 @@ static void xdg_imported_handle_set_parent_of(struct wl_client *client, child->xdg_surface_destroy.notify = handle_child_xdg_surface_destroy; child->xdg_toplevel_set_parent.notify = handle_xdg_toplevel_set_parent; + if (!wlr_xdg_toplevel_set_parent(child_toplevel, surface->toplevel)) { + wl_resource_post_error(surface->toplevel->resource, + XDG_TOPLEVEL_ERROR_INVALID_PARENT, + "a toplevel cannot be a parent of itself or its ancestor"); + free(child); + return; + } + wlr_xdg_toplevel_set_parent(child_toplevel, surface->toplevel); wl_signal_add(&child_toplevel->base->events.destroy, &child->xdg_surface_destroy); diff --git a/types/wlr_xdg_foreign_v2.c b/types/wlr_xdg_foreign_v2.c index 1b79147f..43e65e0e 100644 --- a/types/wlr_xdg_foreign_v2.c +++ b/types/wlr_xdg_foreign_v2.c @@ -113,6 +113,14 @@ static void xdg_imported_handle_set_parent_of(struct wl_client *client, child->xdg_surface_destroy.notify = handle_child_xdg_surface_destroy; child->xdg_toplevel_set_parent.notify = handle_xdg_toplevel_set_parent; + if (!wlr_xdg_toplevel_set_parent(child_toplevel, surface->toplevel)) { + wl_resource_post_error(surface->toplevel->resource, + XDG_TOPLEVEL_ERROR_INVALID_PARENT, + "a toplevel cannot be a parent of itself or its ancestor"); + free(child); + return; + } + wlr_xdg_toplevel_set_parent(child_toplevel, surface->toplevel); wl_signal_add(&child_toplevel->base->events.destroy, &child->xdg_surface_destroy); diff --git a/types/xdg_shell/wlr_xdg_toplevel.c b/types/xdg_shell/wlr_xdg_toplevel.c index f84f41af..420b1de4 100644 --- a/types/xdg_shell/wlr_xdg_toplevel.c +++ b/types/xdg_shell/wlr_xdg_toplevel.c @@ -149,16 +149,27 @@ struct wlr_xdg_toplevel *wlr_xdg_toplevel_from_resource( static void handle_parent_unmap(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel *toplevel = wl_container_of(listener, toplevel, parent_unmap); - wlr_xdg_toplevel_set_parent(toplevel, toplevel->parent->parent); + if (!wlr_xdg_toplevel_set_parent(toplevel, toplevel->parent->parent)) { + assert(0 && "Unreachable"); + } } -void wlr_xdg_toplevel_set_parent(struct wlr_xdg_toplevel *toplevel, +bool wlr_xdg_toplevel_set_parent(struct wlr_xdg_toplevel *toplevel, struct wlr_xdg_toplevel *parent) { - if (toplevel->parent) { + // Check for a loop + struct wlr_xdg_toplevel *iter = parent; + while (iter != NULL) { + if (iter == toplevel) { + return false; + } + iter = iter->parent; + } + + if (toplevel->parent != NULL) { wl_list_remove(&toplevel->parent_unmap.link); } - if (parent && parent->base->mapped) { + if (parent != NULL && parent->base->mapped) { toplevel->parent = parent; toplevel->parent_unmap.notify = handle_parent_unmap; wl_signal_add(&toplevel->parent->base->events.unmap, @@ -168,6 +179,7 @@ void wlr_xdg_toplevel_set_parent(struct wlr_xdg_toplevel *toplevel, } wl_signal_emit_mutable(&toplevel->events.set_parent, NULL); + return true; } static void xdg_toplevel_handle_set_parent(struct wl_client *client, @@ -180,7 +192,10 @@ static void xdg_toplevel_handle_set_parent(struct wl_client *client, parent = wlr_xdg_toplevel_from_resource(parent_resource); } - wlr_xdg_toplevel_set_parent(toplevel, parent); + if (!wlr_xdg_toplevel_set_parent(toplevel, parent)) { + wl_resource_post_error(resource, XDG_TOPLEVEL_ERROR_INVALID_PARENT, + "a toplevel cannot be a parent of itself or its ancestor"); + } } static void xdg_toplevel_handle_set_title(struct wl_client *client,