From dc83b158e12ae33f03165cfd64a50aa7f0a52e26 Mon Sep 17 00:00:00 2001
From: Ryan Dwyer <ryandwyer1@gmail.com>
Date: Fri, 25 May 2018 15:39:14 +1000
Subject: Fix issues with sticky containers and workspaces

* Attach sticky containers to new workspaces when switching
* Fire the close event *before* we start destroying the workspace to
prevent a crash

Because the sticky container now follows the visible workspace, this
simplifies the rendering and container_at logic.
---
 sway/tree/container.c | 45 ++++++++++++++++++---------------------------
 sway/tree/workspace.c | 35 ++++++++++++++++++++++++++++++++---
 2 files changed, 50 insertions(+), 30 deletions(-)

(limited to 'sway/tree')

diff --git a/sway/tree/container.c b/sway/tree/container.c
index fd7ee2c3..532722e8 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -64,16 +64,6 @@ void container_create_notify(struct sway_container *container) {
 	}
 }
 
-static void container_close_notify(struct sway_container *container) {
-	if (container == NULL) {
-		return;
-	}
-	// TODO send ipc event type based on the container type
-	if (container->type == C_VIEW || container->type == C_WORKSPACE) {
-		ipc_event_window(container, "close");
-	}
-}
-
 static void container_update_textures_recursive(struct sway_container *con) {
 	container_update_title_textures(con);
 
@@ -143,7 +133,6 @@ static void _container_destroy(struct sway_container *cont) {
 	}
 
 	wl_signal_emit(&cont->events.destroy, cont);
-	container_close_notify(cont);
 
 	struct sway_container *parent = cont->parent;
 	if (cont->children != NULL && cont->children->length) {
@@ -151,6 +140,7 @@ static void _container_destroy(struct sway_container *cont) {
 		// container_remove_child, which removes child from this container
 		while (cont->children != NULL && cont->children->length > 0) {
 			struct sway_container *child = cont->children->items[0];
+			ipc_event_window(child, "close");
 			container_remove_child(child);
 			_container_destroy(child);
 		}
@@ -188,12 +178,11 @@ static struct sway_container *container_workspace_destroy(
 		return NULL;
 	}
 
+	wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
+	ipc_event_window(workspace, "close");
+
 	struct sway_container *parent = workspace->parent;
-	if (workspace_is_empty(workspace)) {
-		// destroy the WS if there are no children
-		wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
-		ipc_event_workspace(workspace, NULL, "empty");
-	} else if (output) {
+	if (!workspace_is_empty(workspace) && output) {
 		// Move children to a different workspace on this output
 		struct sway_container *new_workspace = NULL;
 		for (int i = 0; i < output->children->length; i++) {
@@ -357,10 +346,12 @@ struct sway_container *container_destroy(struct sway_container *con) {
 			if (con->children->length) {
 				for (int i = 0; i < con->children->length; ++i) {
 					struct sway_container *child = con->children->items[0];
+					ipc_event_window(child, "close");
 					container_remove_child(child);
 					container_add_child(parent, child);
 				}
 			}
+			ipc_event_window(con, "close");
 			_container_destroy(con);
 			break;
 		case C_VIEW:
@@ -635,20 +626,20 @@ struct sway_container *floating_container_at(double lx, double ly,
 		for (int j = 0; j < output->children->length; ++j) {
 			struct sway_container *workspace = output->children->items[j];
 			struct sway_workspace *ws = workspace->sway_workspace;
-			bool ws_is_visible = workspace_is_visible(workspace);
+			if (!workspace_is_visible(workspace)) {
+				continue;
+			}
 			for (int k = 0; k < ws->floating->children->length; ++k) {
 				struct sway_container *floater =
 					ws->floating->children->items[k];
-				if (ws_is_visible || floater->is_sticky) {
-					struct wlr_box box = {
-						.x = floater->x,
-						.y = floater->y,
-						.width = floater->width,
-						.height = floater->height,
-					};
-					if (wlr_box_contains_point(&box, lx, ly)) {
-						return container_at(floater, lx, ly, surface, sx, sy);
-					}
+				struct wlr_box box = {
+					.x = floater->x,
+					.y = floater->y,
+					.width = floater->width,
+					.height = floater->height,
+				};
+				if (wlr_box_contains_point(&box, lx, ly)) {
+					return container_at(floater, lx, ly, surface, sx, sy);
 				}
 			}
 		}
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 37d4a06a..f78ae9a5 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -387,7 +387,21 @@ bool workspace_switch(struct sway_container *workspace) {
 		strcpy(prev_workspace_name, active_ws->name);
 	}
 
-	// TODO: Deal with sticky containers
+	// Move sticky containers to new workspace
+	struct sway_container *next_output = workspace->parent;
+	struct sway_container *next_output_prev_ws =
+		seat_get_active_child(seat, next_output);
+	struct sway_container *floating =
+		next_output_prev_ws->sway_workspace->floating;
+	bool has_sticky = false;
+	for (int i = 0; i < floating->children->length; ++i) {
+		struct sway_container *floater = floating->children->items[i];
+		if (floater->is_sticky) {
+			has_sticky = true;
+			container_remove_child(floater);
+			container_add_child(workspace->sway_workspace->floating, floater);
+		}
+	}
 
 	wlr_log(L_DEBUG, "Switching to workspace %p:%s",
 		workspace, workspace->name);
@@ -395,6 +409,16 @@ bool workspace_switch(struct sway_container *workspace) {
 	if (next == NULL) {
 		next = workspace;
 	}
+	if (has_sticky) {
+		// If there's a sticky container, we might be setting focus to the same
+		// container that's already focused, so seat_set_focus is effectively a
+		// no op. We therefore need to send the IPC event and clean up the old
+		// workspace here.
+		ipc_event_workspace(active_ws, workspace, "focus");
+		if (!workspace_is_visible(active_ws) && workspace_is_empty(active_ws)) {
+			container_destroy(active_ws);
+		}
+	}
 	seat_set_focus(seat, next);
 	struct sway_container *output = container_parent(workspace, C_OUTPUT);
 	arrange_output(output);
@@ -418,8 +442,13 @@ bool workspace_is_empty(struct sway_container *ws) {
 	if (ws->children->length) {
 		return false;
 	}
-	if (ws->sway_workspace->floating->children->length) {
-		return false;
+	// Sticky views are not considered to be part of this workspace
+	struct sway_container *floating = ws->sway_workspace->floating;
+	for (int i = 0; i < floating->children->length; ++i) {
+		struct sway_container *floater = floating->children->items[i];
+		if (!floater->is_sticky) {
+			return false;
+		}
 	}
 	return true;
 }
-- 
cgit v1.2.3