From 35ab3590fc0c1fd0adf8ea17a2c4162940995653 Mon Sep 17 00:00:00 2001
From: Daniel Mueller <deso@posteo.net>
Date: Thu, 21 Dec 2017 22:22:13 -0800
Subject: [PATCH 11/14] Close empty workspaces on i3 restart

The change to no longer destroy empty workspaces combined with i3's
ability to create them easily dynamically can lead to situations where
the user created undesired workspaces or no longer needs some of them.
Having no operation at all for cleaning up is not an ideal state to be
in.
To that end, this change adds functionality to destroy empty workspaces
again. As before we preserve the invariant that all workspaces (empty or
not) before the last non-empty one or the focused one (whichever is
further in the back) are left untouched.
In order to trigger the clean we piggy back on the i3 restart
functionality. Despite it being a relatively heavy operation (but still
reasonably fast), hooking in there is a very elegant way of achieving
this goal. Implementing a new command, as another possible approach, for
example, just leads to greater divergence from vanilla i3 in terms of
the configuration file.
---
 include/con.h |  6 ++++++
 src/con.c     | 42 ++++++++++++++++++++++++++++++++++++++++++
 src/tree.c    |  1 +
 3 files changed, 49 insertions(+)

diff --git a/include/con.h b/include/con.h
index 631071..23476f 100644
--- a/include/con.h
+++ b/include/con.h
@@ -587,3 +587,9 @@ void con_merge_into(Con *old, Con *new);
  *
  */
 bool con_inside_stacked_or_tabbed(Con *con);
+
+/**
+ * Closes workspaces that are empty.
+ *
+ */
+void con_close_empty_workspaces(Con *con);
diff --git a/src/con.c b/src/con.c
index c37d07..d4405d 100644
--- a/src/con.c
+++ b/src/con.c
@@ -2741,3 +2741,45 @@ bool con_inside_stacked_or_tabbed(Con *con) {
     }
     return con_inside_stacked_or_tabbed(con->parent);
 }
+
+/*
+ * Closes all workspaces that are empty starting from the last non-empty
+ * one or the focused one, whichever is the one further in the back.
+ */
+void con_close_empty_workspaces(Con *con) {
+    if (con->type != CT_WORKSPACE) {
+        Con *child;
+        Con *last_non_empty = TAILQ_FIRST(&(con->nodes_head));
+        /*
+         * Find the last workspace that still has children (or is
+         * focused). We only cleanup workspaces that follow it.
+         */
+        TAILQ_FOREACH(child, &(con->nodes_head), nodes)
+        if (child->type == CT_WORKSPACE &&
+            ((con_num_children(child) > 0) || workspace_is_visible(child))) {
+            last_non_empty = child;
+        }
+
+        for (child = last_non_empty; child != NULL;) {
+            Con *nextchild = TAILQ_NEXT(child, nodes);
+            con_close_empty_workspaces(child);
+            child = nextchild;
+        }
+        return;
+    }
+
+    if (TAILQ_EMPTY(&(con->nodes_head)) && TAILQ_EMPTY(&(con->floating_head))) {
+        /* check if this workspace is currently visible */
+        if (!workspace_is_visible(con)) {
+            yajl_gen gen = ipc_marshal_workspace_event("empty", con, NULL);
+            tree_close_internal(con, DONT_KILL_WINDOW, false);
+
+            const unsigned char *payload;
+            ylength length;
+            y(get_buf, &payload, &length);
+            ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
+
+            y(free);
+        }
+    }
+}
diff --git a/src/tree.c b/src/tree.c
index f5105e..6664de 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -113,6 +113,7 @@ bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry) {
         TAILQ_INSERT_HEAD(&(croot->nodes_head), __i3, nodes);
     }
 
+    con_close_empty_workspaces(croot);
     restore_open_placeholder_windows(croot);
     result = true;
 
-- 
2.52.0

