From 32b865e610dd937af17ce36b8c986e41f55a4627 Mon Sep 17 00:00:00 2001
From: Ryan Dwyer <ryandwyer1@gmail.com>
Date: Sat, 23 Jun 2018 17:47:28 +1000
Subject: Fix crash when deleting last child in a tabbed or stacked container

There was no `current` child because the container was destroyed. This
makes it fall back to looking in the parent's current children list.
---
 include/sway/input/seat.h | 11 +++++++++++
 sway/desktop/output.c     | 28 ++++++++++++++++------------
 sway/input/seat.c         | 12 ++++++++++++
 3 files changed, 39 insertions(+), 12 deletions(-)

diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index 1f7792ba..0e440701 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -118,6 +118,17 @@ struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,
 struct sway_container *seat_get_active_child(struct sway_seat *seat,
 		struct sway_container *container);
 
+/**
+ * Return the immediate child of container which was most recently focused, with
+ * fallback to selecting the child in the parent's `current` (rendered) children
+ * list.
+ *
+ * This is useful for when a tabbed container and its children are destroyed but
+ * still being rendered, and we have to render an appropriate child.
+ */
+struct sway_container *seat_get_active_current_child(struct sway_seat *seat,
+		struct sway_container *container);
+
 /**
  * Iterate over the focus-inactive children of the container calling the
  * function on each.
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 9db95ef5..1ca48975 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -713,7 +713,7 @@ static void render_container_tabbed(struct sway_output *output,
 	}
 	struct sway_seat *seat = input_manager_current_seat(input_manager);
 	struct sway_container *focus = seat_get_focus(seat);
-	struct sway_container *current = seat_get_active_child(seat, con);
+	struct sway_container *current = seat_get_active_current_child(seat, con);
 	struct border_colors *current_colors = NULL;
 	struct sway_container_state *pstate = &con->current;
 
@@ -756,11 +756,13 @@ static void render_container_tabbed(struct sway_output *output,
 	}
 
 	// Render surface and left/right/bottom borders
-	if (current->type == C_VIEW) {
-		render_view(output, damage, current, current_colors);
-	} else {
-		render_container(output, damage, current,
-				parent_focused || current == focus);
+	if (current) {
+		if (current->type == C_VIEW) {
+			render_view(output, damage, current, current_colors);
+		} else {
+			render_container(output, damage, current,
+					parent_focused || current == focus);
+		}
 	}
 }
 
@@ -775,7 +777,7 @@ static void render_container_stacked(struct sway_output *output,
 	}
 	struct sway_seat *seat = input_manager_current_seat(input_manager);
 	struct sway_container *focus = seat_get_focus(seat);
-	struct sway_container *current = seat_get_active_child(seat, con);
+	struct sway_container *current = seat_get_active_current_child(seat, con);
 	struct border_colors *current_colors = NULL;
 	struct sway_container_state *pstate = &con->current;
 
@@ -812,11 +814,13 @@ static void render_container_stacked(struct sway_output *output,
 	}
 
 	// Render surface and left/right/bottom borders
-	if (current->type == C_VIEW) {
-		render_view(output, damage, current, current_colors);
-	} else {
-		render_container(output, damage, current,
-				parent_focused || current == focus);
+	if (current) {
+		if (current->type == C_VIEW) {
+			render_view(output, damage, current, current_colors);
+		} else {
+			render_container(output, damage, current,
+					parent_focused || current == focus);
+		}
 	}
 }
 
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 1ea36466..436d18e2 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -829,6 +829,18 @@ struct sway_container *seat_get_active_child(struct sway_seat *seat,
 	return NULL;
 }
 
+struct sway_container *seat_get_active_current_child(struct sway_seat *seat,
+		struct sway_container *container) {
+	struct sway_container *child = seat_get_active_child(seat, container);
+	if (child) {
+		return child;
+	}
+	if (container->current.children->length == 1) {
+		return container->current.children->items[0];
+	}
+	return NULL;
+}
+
 struct sway_container *seat_get_focus(struct sway_seat *seat) {
 	if (!seat->has_focus) {
 		return NULL;
-- 
cgit v1.2.3