From 9914784594713f184c58e37def1d136c6af394a9 Mon Sep 17 00:00:00 2001
From: Brian Ashworth <bosrsf04@gmail.com>
Date: Wed, 14 Aug 2019 20:36:08 -0400
Subject: wlr_xdg_toplevel: reparent on parent unmap

From the xdg-shell specification:
	If the parent is unmapped then its children are managed as
	though the parent of the now-unmapped parent has become the
	parent of this surface. If no parent exists for the now-unmapped
	parent then the children are managed as though they have no
	parent surface.
---
 include/wlr/types/wlr_xdg_shell.h  |  4 +++-
 types/xdg_shell/wlr_xdg_surface.c  |  4 ++++
 types/xdg_shell/wlr_xdg_toplevel.c | 31 +++++++++++++++++++++++++++++--
 3 files changed, 36 insertions(+), 3 deletions(-)

diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h
index e8c12ae9..70572bf5 100644
--- a/include/wlr/types/wlr_xdg_shell.h
+++ b/include/wlr/types/wlr_xdg_shell.h
@@ -117,9 +117,11 @@ struct wlr_xdg_toplevel_state {
 struct wlr_xdg_toplevel {
 	struct wl_resource *resource;
 	struct wlr_xdg_surface *base;
-	struct wlr_xdg_surface *parent;
 	bool added;
 
+	struct wlr_xdg_surface *parent;
+	struct wl_listener parent_unmap;
+
 	struct wlr_xdg_toplevel_state client_pending;
 	struct wlr_xdg_toplevel_state server_pending;
 	struct wlr_xdg_toplevel_state current;
diff --git a/types/xdg_shell/wlr_xdg_surface.c b/types/xdg_shell/wlr_xdg_surface.c
index e3f13e0e..fcd3e0c9 100644
--- a/types/xdg_shell/wlr_xdg_surface.c
+++ b/types/xdg_shell/wlr_xdg_surface.c
@@ -41,6 +41,10 @@ void unmap_xdg_surface(struct wlr_xdg_surface *surface) {
 
 	switch (surface->role) {
 	case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
+		if (surface->toplevel->parent) {
+			wl_list_remove(&surface->toplevel->parent_unmap.link);
+			surface->toplevel->parent = NULL;
+		}
 		free(surface->toplevel->title);
 		surface->toplevel->title = NULL;
 		free(surface->toplevel->app_id);
diff --git a/types/xdg_shell/wlr_xdg_toplevel.c b/types/xdg_shell/wlr_xdg_toplevel.c
index 96bb0537..3524bd34 100644
--- a/types/xdg_shell/wlr_xdg_toplevel.c
+++ b/types/xdg_shell/wlr_xdg_toplevel.c
@@ -207,6 +207,34 @@ struct wlr_xdg_surface *wlr_xdg_surface_from_toplevel_resource(
 	return wl_resource_get_user_data(resource);
 }
 
+static void set_parent(struct wlr_xdg_surface *surface,
+		struct wlr_xdg_surface *parent);
+
+static void handle_parent_unmap(struct wl_listener *listener, void *data) {
+	struct wlr_xdg_toplevel *toplevel =
+		wl_container_of(listener, toplevel, parent_unmap);
+	set_parent(toplevel->base, toplevel->parent->toplevel->parent);
+}
+
+static void set_parent(struct wlr_xdg_surface *surface,
+		struct wlr_xdg_surface *parent) {
+	assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
+	assert(!parent || parent->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
+
+	if (surface->toplevel->parent) {
+		wl_list_remove(&surface->toplevel->parent_unmap.link);
+	}
+
+	surface->toplevel->parent = parent;
+	if (surface->toplevel->parent) {
+		surface->toplevel->parent_unmap.notify = handle_parent_unmap;
+		wl_signal_add(&surface->toplevel->parent->events.unmap,
+				&surface->toplevel->parent_unmap);
+	}
+
+	wlr_signal_emit_safe(&surface->toplevel->events.set_parent, surface);
+}
+
 static void xdg_toplevel_handle_set_parent(struct wl_client *client,
 		struct wl_resource *resource, struct wl_resource *parent_resource) {
 	struct wlr_xdg_surface *surface =
@@ -217,8 +245,7 @@ static void xdg_toplevel_handle_set_parent(struct wl_client *client,
 		parent = wlr_xdg_surface_from_toplevel_resource(parent_resource);
 	}
 
-	surface->toplevel->parent = parent;
-	wlr_signal_emit_safe(&surface->toplevel->events.set_parent, surface);
+	set_parent(surface, parent);
 }
 
 static void xdg_toplevel_handle_set_title(struct wl_client *client,
-- 
cgit v1.2.3