]> git.sur5r.net Git - i3/i3/blobdiff - src/commands.c
Merge branch 'next'
[i3/i3] / src / commands.c
index 6f0d0c5e1ff4cad38a760e42f23ab3257ef259ac..2ca8387c91e7387ec0835672c4a5866caa28c89e 100644 (file)
@@ -1,3 +1,5 @@
+#undef I3__FILE__
+#define I3__FILE__ "commands.c"
 /*
  * vim:ts=4:sw=4:expandtab
  *
@@ -36,7 +38,6 @@
     } \
 } while (0)
 
-static owindows_head owindows;
 
 /*
  * Returns true if a is definitely greater than b (using the given epsilon)
@@ -55,19 +56,19 @@ static Output *get_output_from_string(Output *current_output, const char *output
     Output *output;
 
     if (strcasecmp(output_str, "left") == 0) {
-        output = get_output_next(D_LEFT, current_output);
+        output = get_output_next(D_LEFT, current_output, CLOSEST_OUTPUT);
         if (!output)
             output = get_output_most(D_RIGHT, current_output);
     } else if (strcasecmp(output_str, "right") == 0) {
-        output = get_output_next(D_RIGHT, current_output);
+        output = get_output_next(D_RIGHT, current_output, CLOSEST_OUTPUT);
         if (!output)
             output = get_output_most(D_LEFT, current_output);
     } else if (strcasecmp(output_str, "up") == 0) {
-        output = get_output_next(D_UP, current_output);
+        output = get_output_next(D_UP, current_output, CLOSEST_OUTPUT);
         if (!output)
             output = get_output_most(D_DOWN, current_output);
     } else if (strcasecmp(output_str, "down") == 0) {
-        output = get_output_next(D_DOWN, current_output);
+        output = get_output_next(D_DOWN, current_output, CLOSEST_OUTPUT);
         if (!output)
             output = get_output_most(D_UP, current_output);
     } else output = get_output_by_name(output_str);
@@ -97,6 +98,29 @@ static bool maybe_back_and_forth(struct CommandResult *cmd_output, char *name) {
     return true;
 }
 
+/*
+ * Return the passed workspace unless it is the current one and auto back and
+ * forth is enabled, in which case the back_and_forth workspace is returned.
+ */
+static Con *maybe_auto_back_and_forth_workspace(Con *workspace) {
+    Con *current, *baf;
+
+    if (!config.workspace_auto_back_and_forth)
+        return workspace;
+
+    current = con_get_workspace(focused);
+
+    if (current == workspace) {
+        baf = workspace_back_and_forth_get();
+        if (baf != NULL) {
+            DLOG("Substituting workspace with back_and_forth, as it is focused.\n");
+            return baf;
+        }
+    }
+
+    return workspace;
+}
+
 // This code is commented out because we might recycle it for popping up error
 // messages on parser errors.
 #if 0
@@ -197,6 +221,20 @@ void cmd_MIGRATION_start_nagbar(void) {
  * Criteria functions.
  ******************************************************************************/
 
+/*
+ * Helper data structure for an operation window (window on which the operation
+ * will be performed). Used to build the TAILQ owindows.
+ *
+ */
+typedef struct owindow {
+    Con *con;
+    TAILQ_ENTRY(owindow) owindows;
+} owindow;
+
+typedef TAILQ_HEAD(owindows_head, owindow) owindows_head;
+
+static owindows_head owindows;
+
 /*
  * Initializes the specified 'Match' data structure and the initial state of
  * commands.c for matching target windows of a command.
@@ -363,7 +401,8 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) {
      *  when criteria was specified but didn't match any window or
      *  when criteria wasn't specified and we don't have any window focused. */
     if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
-        (match_is_empty(current_match) && focused->type == CT_WORKSPACE)) {
+        (match_is_empty(current_match) && focused->type == CT_WORKSPACE &&
+        !con_has_children(focused))) {
         ysuccess(false);
         return;
     }
@@ -398,6 +437,38 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) {
     ysuccess(true);
 }
 
+/**
+ * Implementation of 'move [window|container] [to] workspace back_and_forth'.
+ *
+ */
+void cmd_move_con_to_workspace_back_and_forth(I3_CMD) {
+    owindow *current;
+    Con *ws;
+
+    ws = workspace_back_and_forth_get();
+
+    if (ws == NULL) {
+        y(map_open);
+        ystr("success");
+        y(bool, false);
+        ystr("error");
+        ystr("No workspace was previously active.");
+        y(map_close);
+        return;
+    }
+
+    HANDLE_EMPTY_MATCH;
+
+    TAILQ_FOREACH(current, &owindows, owindows) {
+        DLOG("matching: %p / %s\n", current->con, current->con->name);
+        con_move_to_workspace(current->con, ws, true, false);
+    }
+
+    cmd_output->needs_tree_render = true;
+    // XXX: default reply for now, make this a better reply
+    ysuccess(true);
+}
+
 /*
  * Implementation of 'move [window|container] [to] workspace <name>'.
  *
@@ -414,8 +485,13 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
     /* We have nothing to move:
      *  when criteria was specified but didn't match any window or
      *  when criteria wasn't specified and we don't have any window focused. */
-    if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
-        (match_is_empty(current_match) && focused->type == CT_WORKSPACE)) {
+    if (!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) {
+        ELOG("No windows match your criteria, cannot move.\n");
+        ysuccess(false);
+        return;
+    }
+    else if (match_is_empty(current_match) && focused->type == CT_WORKSPACE &&
+        !con_has_children(focused)) {
         ysuccess(false);
         return;
     }
@@ -424,6 +500,8 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
     /* get the workspace */
     Con *ws = workspace_get(name, NULL);
 
+    ws = maybe_auto_back_and_forth_workspace(ws);
+
     HANDLE_EMPTY_MATCH;
 
     TAILQ_FOREACH(current, &owindows, owindows) {
@@ -437,7 +515,7 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
 }
 
 /*
- * Implementation of 'move [window|container] [to] workspace number <number>'.
+ * Implementation of 'move [window|container] [to] workspace number <name>'.
  *
  */
 void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
@@ -447,12 +525,13 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
      *  when criteria was specified but didn't match any window or
      *  when criteria wasn't specified and we don't have any window focused. */
     if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
-        (match_is_empty(current_match) && focused->type == CT_WORKSPACE)) {
+        (match_is_empty(current_match) && focused->type == CT_WORKSPACE &&
+        !con_has_children(focused))) {
         ysuccess(false);
         return;
     }
 
-    LOG("should move window to workspace with number %d\n", which);
+    LOG("should move window to workspace %s\n", which);
     /* get the workspace */
     Con *output, *workspace = NULL;
 
@@ -461,8 +540,8 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
     if (parsed_num == LONG_MIN ||
         parsed_num == LONG_MAX ||
         parsed_num < 0 ||
-        *endptr != '\0') {
-        LOG("Could not parse \"%s\" as a number.\n", which);
+        endptr == which) {
+        LOG("Could not parse initial part of \"%s\" as a number.\n", which);
         y(map_open);
         ystr("success");
         y(bool, false);
@@ -478,16 +557,11 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
             child->num == parsed_num);
 
     if (!workspace) {
-        y(map_open);
-        ystr("success");
-        y(bool, false);
-        ystr("error");
-        // TODO: better error message
-        ystr("No such workspace");
-        y(map_close);
-        return;
+        workspace = workspace_get(which, NULL);
     }
 
+    workspace = maybe_auto_back_and_forth_workspace(workspace);
+
     HANDLE_EMPTY_MATCH;
 
     TAILQ_FOREACH(current, &owindows, owindows) {
@@ -502,23 +576,37 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
 
 static void cmd_resize_floating(I3_CMD, char *way, char *direction, Con *floating_con, int px) {
     LOG("floating resize\n");
+    Rect old_rect = floating_con->rect;
+
     if (strcmp(direction, "up") == 0) {
-        floating_con->rect.y -= px;
         floating_con->rect.height += px;
-    } else if (strcmp(direction, "down") == 0) {
+    } else if (strcmp(direction, "down") == 0 || strcmp(direction, "height") == 0) {
         floating_con->rect.height += px;
     } else if (strcmp(direction, "left") == 0) {
-        floating_con->rect.x -= px;
         floating_con->rect.width += px;
     } else {
         floating_con->rect.width += px;
     }
+
+    floating_check_size(floating_con);
+
+    /* Did we actually resize anything or did the size constraints prevent us?
+     * If we could not resize, exit now to not move the window. */
+    if (memcmp(&old_rect, &(floating_con->rect), sizeof(Rect)) == 0)
+        return;
+
+    if (strcmp(direction, "up") == 0) {
+        floating_con->rect.y -= px;
+    } else if (strcmp(direction, "left") == 0) {
+        floating_con->rect.x -= px;
+    }
 }
 
-static void cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int ppt) {
+static bool cmd_resize_tiling_direction(I3_CMD, Con *current, char *way, char *direction, int ppt) {
     LOG("tiling resize\n");
     /* get the appropriate current container (skip stacked/tabbed cons) */
-    Con *current = focused;
+    Con *other = NULL;
+    double percentage = 0;
     while (current->parent->layout == L_STACKED ||
            current->parent->layout == L_TABBED)
         current = current->parent;
@@ -527,40 +615,50 @@ static void cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int
     orientation_t search_orientation =
         (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0 ? HORIZ : VERT);
 
-    while (current->type != CT_WORKSPACE &&
-           current->type != CT_FLOATING_CON &&
-           current->parent->orientation != search_orientation)
-        current = current->parent;
+    do {
+        if (con_orientation(current->parent) != search_orientation) {
+            current = current->parent;
+            continue;
+        }
 
-    /* get the default percentage */
-    int children = con_num_children(current->parent);
-    Con *other;
-    LOG("ins. %d children\n", children);
-    double percentage = 1.0 / children;
-    LOG("default percentage = %f\n", percentage);
+        /* get the default percentage */
+        int children = con_num_children(current->parent);
+        LOG("ins. %d children\n", children);
+        percentage = 1.0 / children;
+        LOG("default percentage = %f\n", percentage);
 
-    orientation_t orientation = current->parent->orientation;
+        orientation_t orientation = con_orientation(current->parent);
 
-    if ((orientation == HORIZ &&
-         (strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) ||
-        (orientation == VERT &&
-         (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0))) {
-        LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n",
-            (orientation == HORIZ ? "horizontal" : "vertical"));
-        ysuccess(false);
-        return;
-    }
+        if ((orientation == HORIZ &&
+             (strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) ||
+            (orientation == VERT &&
+             (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0))) {
+            LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n",
+                (orientation == HORIZ ? "horizontal" : "vertical"));
+            ysuccess(false);
+            return false;
+        }
 
-    if (strcmp(direction, "up") == 0 || strcmp(direction, "left") == 0) {
-        other = TAILQ_PREV(current, nodes_head, nodes);
-    } else {
-        other = TAILQ_NEXT(current, nodes);
-    }
-    if (other == TAILQ_END(workspaces)) {
-        LOG("No other container in this direction found, cannot resize.\n");
+        if (strcmp(direction, "up") == 0 || strcmp(direction, "left") == 0) {
+            other = TAILQ_PREV(current, nodes_head, nodes);
+        } else {
+            other = TAILQ_NEXT(current, nodes);
+        }
+        if (other == TAILQ_END(workspaces)) {
+            LOG("No other container in this direction found, trying to look further up in the tree...\n");
+            current = current->parent;
+            continue;
+        }
+        break;
+    } while (current->type != CT_WORKSPACE &&
+             current->type != CT_FLOATING_CON);
+
+    if (other == NULL) {
+        LOG("No other container in this direction found, trying to look further up in the tree...\n");
         ysuccess(false);
-        return;
+        return false;
     }
+
     LOG("other->percent = %f\n", other->percent);
     LOG("current->percent before = %f\n", current->percent);
     if (current->percent == 0.0)
@@ -582,12 +680,13 @@ static void cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int
     } else {
         LOG("Not resizing, already at minimum size\n");
     }
+
+    return true;
 }
 
-static void cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, int ppt) {
+static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, char *way, char *direction, int ppt) {
     LOG("width/height resize\n");
     /* get the appropriate current container (skip stacked/tabbed cons) */
-    Con *current = focused;
     while (current->parent->layout == L_STACKED ||
            current->parent->layout == L_TABBED)
         current = current->parent;
@@ -598,7 +697,7 @@ static void cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i
 
     while (current->type != CT_WORKSPACE &&
            current->type != CT_FLOATING_CON &&
-           current->parent->orientation != search_orientation)
+           con_orientation(current->parent) != search_orientation)
         current = current->parent;
 
     /* get the default percentage */
@@ -607,7 +706,7 @@ static void cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i
     double percentage = 1.0 / children;
     LOG("default percentage = %f\n", percentage);
 
-    orientation_t orientation = current->parent->orientation;
+    orientation_t orientation = con_orientation(current->parent);
 
     if ((orientation == HORIZ &&
          strcmp(direction, "height") == 0) ||
@@ -616,13 +715,13 @@ static void cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i
         LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n",
             (orientation == HORIZ ? "horizontal" : "vertical"));
         ysuccess(false);
-        return;
+        return false;
     }
 
     if (children == 1) {
         LOG("This is the only container, cannot resize.\n");
         ysuccess(false);
-        return;
+        return false;
     }
 
     /* Ensure all the other children have a percentage set. */
@@ -645,13 +744,13 @@ static void cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i
         if (!definitelyGreaterThan(child->percent - subtract_percent, 0.05, DBL_EPSILON)) {
             LOG("Not resizing, already at minimum size (child %p would end up with a size of %.f\n", child, child->percent - subtract_percent);
             ysuccess(false);
-            return;
+            return false;
         }
     }
     if (!definitelyGreaterThan(new_current_percent, 0.05, DBL_EPSILON)) {
         LOG("Not resizing, already at minimum size\n");
         ysuccess(false);
-        return;
+        return false;
     }
 
     current->percent += ((double)ppt / 100.0);
@@ -663,6 +762,8 @@ static void cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i
         child->percent -= subtract_percent;
         LOG("child->percent after (%p) = %f\n", child, child->percent);
     }
+
+    return true;
 }
 
 /*
@@ -680,14 +781,23 @@ void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resiz
         ppt *= -1;
     }
 
-    Con *floating_con;
-    if ((floating_con = con_inside_floating(focused))) {
-        cmd_resize_floating(current_match, cmd_output, way, direction, floating_con, px);
-    } else {
-        if (strcmp(direction, "width") == 0 ||
-            strcmp(direction, "height") == 0)
-            cmd_resize_tiling_width_height(current_match, cmd_output, way, direction, ppt);
-        else cmd_resize_tiling_direction(current_match, cmd_output, way, direction, ppt);
+    HANDLE_EMPTY_MATCH;
+
+    owindow *current;
+    TAILQ_FOREACH(current, &owindows, owindows) {
+        Con *floating_con;
+        if ((floating_con = con_inside_floating(current->con))) {
+            cmd_resize_floating(current_match, cmd_output, way, direction, floating_con, px);
+        } else {
+            if (strcmp(direction, "width") == 0 ||
+                strcmp(direction, "height") == 0) {
+                if (!cmd_resize_tiling_width_height(current_match, cmd_output, current->con, way, direction, ppt))
+                    return;
+            } else {
+                if (!cmd_resize_tiling_direction(current_match, cmd_output, current->con, way, direction, ppt))
+                    return;
+            }
+        }
     }
 
     cmd_output->needs_tree_render = true;
@@ -696,11 +806,11 @@ void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resiz
 }
 
 /*
- * Implementation of 'border normal|none|1pixel|toggle'.
+ * Implementation of 'border normal|none|1pixel|toggle|pixel'.
  *
  */
-void cmd_border(I3_CMD, char *border_style_str) {
-    DLOG("border style should be changed to %s\n", border_style_str);
+void cmd_border(I3_CMD, char *border_style_str, char *border_width ) {
+    DLOG("border style should be changed to %s with border width %s\n", border_style_str, border_width);
     owindow *current;
 
     HANDLE_EMPTY_MATCH;
@@ -708,23 +818,39 @@ void cmd_border(I3_CMD, char *border_style_str) {
     TAILQ_FOREACH(current, &owindows, owindows) {
         DLOG("matching: %p / %s\n", current->con, current->con->name);
         int border_style = current->con->border_style;
+        char *end;
+        int tmp_border_width = -1;
+        tmp_border_width = strtol(border_width, &end, 10);
+        if (end == border_width) {
+            /* no valid digits found */
+            tmp_border_width = -1;
+        }
         if (strcmp(border_style_str, "toggle") == 0) {
             border_style++;
             border_style %= 3;
+            if (border_style == BS_NORMAL)
+                tmp_border_width = 2;
+            else if (border_style == BS_NONE)
+                tmp_border_width = 0;
+            else if (border_style == BS_PIXEL)
+                tmp_border_width = 1;
         } else {
             if (strcmp(border_style_str, "normal") == 0)
                 border_style = BS_NORMAL;
-            else if (strcmp(border_style_str, "none") == 0)
+            else if (strcmp(border_style_str, "pixel") == 0)
+                border_style = BS_PIXEL;
+            else if (strcmp(border_style_str, "1pixel") == 0){
+                border_style = BS_PIXEL;
+                tmp_border_width = 1;
+            } else if (strcmp(border_style_str, "none") == 0)
                 border_style = BS_NONE;
-            else if (strcmp(border_style_str, "1pixel") == 0)
-                border_style = BS_1PIXEL;
             else {
                 ELOG("BUG: called with border_style=%s\n", border_style_str);
                 ysuccess(false);
                 return;
             }
         }
-        con_set_border_style(current->con, border_style);
+        con_set_border_style(current->con, border_style, tmp_border_width);
     }
 
     cmd_output->needs_tree_render = true;
@@ -786,7 +912,7 @@ void cmd_workspace(I3_CMD, char *which) {
 }
 
 /*
- * Implementation of 'workspace number <number>'
+ * Implementation of 'workspace number <name>'
  *
  */
 void cmd_workspace_number(I3_CMD, char *which) {
@@ -797,8 +923,8 @@ void cmd_workspace_number(I3_CMD, char *which) {
     if (parsed_num == LONG_MIN ||
         parsed_num == LONG_MAX ||
         parsed_num < 0 ||
-        *endptr != '\0') {
-        LOG("Could not parse \"%s\" as a number.\n", which);
+        endptr == which) {
+        LOG("Could not parse initial part of \"%s\" as a number.\n", which);
         y(map_open);
         ystr("success");
         y(bool, false);
@@ -815,17 +941,13 @@ void cmd_workspace_number(I3_CMD, char *which) {
             child->num == parsed_num);
 
     if (!workspace) {
-        LOG("There is no workspace with number %d, creating a new one.\n", parsed_num);
+        LOG("There is no workspace with number %ld, creating a new one.\n", parsed_num);
         ysuccess(true);
-        /* terminate the which string after the endposition of the number */
-        *endptr = '\0';
-        if (maybe_back_and_forth(cmd_output, which))
-            return;
         workspace_show_by_name(which);
         cmd_output->needs_tree_render = true;
         return;
     }
-    if (maybe_back_and_forth(cmd_output, which))
+    if (maybe_back_and_forth(cmd_output, workspace->name))
         return;
     workspace_show(workspace);
 
@@ -930,13 +1052,13 @@ void cmd_move_con_to_output(I3_CMD, char *name) {
 
     // TODO: clean this up with commands.spec as soon as we switched away from the lex/yacc command parser
     if (strcasecmp(name, "up") == 0)
-        output = get_output_next(D_UP, current_output);
+        output = get_output_next(D_UP, current_output, CLOSEST_OUTPUT);
     else if (strcasecmp(name, "down") == 0)
-        output = get_output_next(D_DOWN, current_output);
+        output = get_output_next(D_DOWN, current_output, CLOSEST_OUTPUT);
     else if (strcasecmp(name, "left") == 0)
-        output = get_output_next(D_LEFT, current_output);
+        output = get_output_next(D_LEFT, current_output, CLOSEST_OUTPUT);
     else if (strcasecmp(name, "right") == 0)
-        output = get_output_next(D_RIGHT, current_output);
+        output = get_output_next(D_RIGHT, current_output, CLOSEST_OUTPUT);
     else
         output = get_output_by_name(name);
 
@@ -1008,9 +1130,14 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
     TAILQ_FOREACH(current, &owindows, owindows) {
         Output *current_output = get_output_containing(current->con->rect.x,
                                                        current->con->rect.y);
+        if (!current_output) {
+            ELOG("Cannot get current output. This is a bug in i3.\n");
+            ysuccess(false);
+            return;
+        }
         Output *output = get_output_from_string(current_output, name);
         if (!output) {
-            LOG("No such output\n");
+            ELOG("Could not get output from string \"%s\"\n", name);
             ysuccess(false);
             return;
         }
@@ -1018,8 +1145,12 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
         Con *content = output_get_content(output->con);
         LOG("got output %p with content %p\n", output, content);
 
+        Con *previously_visible_ws = TAILQ_FIRST(&(content->nodes_head));
+        LOG("Previously visible workspace = %p / %s\n", previously_visible_ws, previously_visible_ws->name);
+
         Con *ws = con_get_workspace(current->con);
         LOG("should move workspace %p / %s\n", ws, ws->name);
+        bool workspace_was_visible = workspace_is_visible(ws);
 
         if (con_num_children(ws->parent) == 1) {
             LOG("Creating a new workspace to replace \"%s\" (last on its output).\n", ws->name);
@@ -1054,9 +1185,9 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
             /* notify the IPC listeners */
             ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
         }
+        DLOG("Detaching\n");
 
         /* detach from the old output and attach to the new output */
-        bool workspace_was_visible = workspace_is_visible(ws);
         Con *old_content = ws->parent;
         con_detach(ws);
         if (workspace_was_visible) {
@@ -1078,6 +1209,22 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
             /* Focus the moved workspace on the destination output. */
             workspace_show(ws);
         }
+
+        /* NB: We cannot simply work with previously_visible_ws since it might
+         * have been cleaned up by workspace_show() already, depending on the
+         * focus order/number of other workspaces on the output.
+         * Instead, we loop through the available workspaces and only work with
+         * previously_visible_ws if we still find it. */
+        TAILQ_FOREACH(ws, &(content->nodes_head), nodes) {
+            if (ws != previously_visible_ws)
+                continue;
+
+            /* Call the on_remove_child callback of the workspace which previously
+             * was visible on the destination output. Since it is no longer
+             * visible, it might need to get cleaned up. */
+            CALL(previously_visible_ws, on_remove_child);
+            break;
+        }
     }
 
     cmd_output->needs_tree_render = true;
@@ -1090,9 +1237,17 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
  *
  */
 void cmd_split(I3_CMD, char *direction) {
+    owindow *current;
     /* TODO: use matches */
     LOG("splitting in direction %c\n", direction[0]);
-    tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ));
+    if (match_is_empty(current_match))
+        tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ));
+    else {
+        TAILQ_FOREACH(current, &owindows, owindows) {
+            DLOG("matching: %p / %s\n", current->con, current->con->name);
+            tree_split(current->con, (direction[0] == 'v' ? VERT : HORIZ));
+        }
+    }
 
     cmd_output->needs_tree_render = true;
     // XXX: default reply for now, make this a better reply
@@ -1100,7 +1255,7 @@ void cmd_split(I3_CMD, char *direction) {
 }
 
 /*
- * Implementaiton of 'kill [window|client]'.
+ * Implementation of 'kill [window|client]'.
  *
  */
 void cmd_kill(I3_CMD, char *kill_mode_str) {
@@ -1155,14 +1310,6 @@ void cmd_exec(I3_CMD, char *nosn, char *command) {
  *
  */
 void cmd_focus_direction(I3_CMD, char *direction) {
-    if (focused &&
-        focused->type != CT_WORKSPACE &&
-        focused->fullscreen_mode != CF_NONE) {
-        LOG("Cannot change focus while in fullscreen mode.\n");
-        ysuccess(false);
-        return;
-    }
-
     DLOG("direction = *%s*\n", direction);
 
     if (strcmp(direction, "left") == 0)
@@ -1189,14 +1336,6 @@ void cmd_focus_direction(I3_CMD, char *direction) {
  *
  */
 void cmd_focus_window_mode(I3_CMD, char *window_mode) {
-    if (focused &&
-        focused->type != CT_WORKSPACE &&
-        focused->fullscreen_mode != CF_NONE) {
-        LOG("Cannot change focus while in fullscreen mode.\n");
-        ysuccess(false);
-        return;
-    }
-
     DLOG("window_mode = %s\n", window_mode);
 
     Con *ws = con_get_workspace(focused);
@@ -1238,7 +1377,7 @@ void cmd_focus_level(I3_CMD, char *level) {
             if (con_fullscreen_permits_focusing(focused->parent))
                 success = level_up();
             else
-                LOG("Currently in fullscreen, not going up\n");
+                ELOG("'focus parent': Currently in fullscreen, not going up\n");
         }
     }
 
@@ -1377,21 +1516,35 @@ void cmd_move_direction(I3_CMD, char *direction, char *move_px) {
 }
 
 /*
- * Implementation of 'layout default|stacked|stacking|tabbed'.
+ * Implementation of 'layout default|stacked|stacking|tabbed|splitv|splith'.
  *
  */
 void cmd_layout(I3_CMD, char *layout_str) {
     if (strcmp(layout_str, "stacking") == 0)
         layout_str = "stacked";
-    DLOG("changing layout to %s\n", layout_str);
     owindow *current;
-    int layout = (strcmp(layout_str, "default") == 0 ? L_DEFAULT :
-                  (strcmp(layout_str, "stacked") == 0 ? L_STACKED :
-                   L_TABBED));
+    int layout;
+    /* default is a special case which will be handled in con_set_layout(). */
+    if (strcmp(layout_str, "default") == 0)
+        layout = L_DEFAULT;
+    else if (strcmp(layout_str, "stacked") == 0)
+        layout = L_STACKED;
+    else if (strcmp(layout_str, "tabbed") == 0)
+        layout = L_TABBED;
+    else if (strcmp(layout_str, "splitv") == 0)
+        layout = L_SPLITV;
+    else if (strcmp(layout_str, "splith") == 0)
+        layout = L_SPLITH;
+    else {
+        ELOG("Unknown layout \"%s\", this is a mismatch between code and parser spec.\n", layout_str);
+        return;
+    }
+
+    DLOG("changing layout to %s (%d)\n", layout_str, layout);
 
     /* check if the match is empty, not if the result is empty */
     if (match_is_empty(current_match))
-        con_set_layout(focused->parent, layout);
+        con_set_layout(focused, layout);
     else {
         TAILQ_FOREACH(current, &owindows, owindows) {
             DLOG("matching: %p / %s\n", current->con, current->con->name);
@@ -1405,23 +1558,52 @@ void cmd_layout(I3_CMD, char *layout_str) {
 }
 
 /*
- * Implementaiton of 'exit'.
+ * Implementation of 'layout toggle [all|split]'.
+ *
+ */
+void cmd_layout_toggle(I3_CMD, char *toggle_mode) {
+    owindow *current;
+
+    if (toggle_mode == NULL)
+        toggle_mode = "default";
+
+    DLOG("toggling layout (mode = %s)\n", toggle_mode);
+
+    /* check if the match is empty, not if the result is empty */
+    if (match_is_empty(current_match))
+        con_toggle_layout(focused, toggle_mode);
+    else {
+        TAILQ_FOREACH(current, &owindows, owindows) {
+            DLOG("matching: %p / %s\n", current->con, current->con->name);
+            con_toggle_layout(current->con, toggle_mode);
+        }
+    }
+
+    cmd_output->needs_tree_render = true;
+    // XXX: default reply for now, make this a better reply
+    ysuccess(true);
+}
+
+/*
+ * Implementation of 'exit'.
  *
  */
 void cmd_exit(I3_CMD) {
     LOG("Exiting due to user command.\n");
+    xcb_disconnect(conn);
     exit(0);
 
     /* unreached */
 }
 
 /*
- * Implementaiton of 'reload'.
+ * Implementation of 'reload'.
  *
  */
 void cmd_reload(I3_CMD) {
     LOG("reloading\n");
     kill_configerror_nagbar(false);
+    kill_commanderror_nagbar(false);
     load_configuration(conn, NULL, true);
     x_set_i3_atoms();
     /* Send an IPC event just in case the ws names have changed */
@@ -1432,7 +1614,7 @@ void cmd_reload(I3_CMD) {
 }
 
 /*
- * Implementaiton of 'restart'.
+ * Implementation of 'restart'.
  *
  */
 void cmd_restart(I3_CMD) {
@@ -1444,12 +1626,13 @@ void cmd_restart(I3_CMD) {
 }
 
 /*
- * Implementaiton of 'open'.
+ * Implementation of 'open'.
  *
  */
 void cmd_open(I3_CMD) {
     LOG("opening new container\n");
     Con *con = tree_open_con(NULL, NULL);
+    con->layout = L_SPLITH;
     con_focus(con);
 
     y(map_open);
@@ -1632,16 +1815,24 @@ void cmd_scratchpad_show(I3_CMD) {
 }
 
 /*
- * Implementation of 'rename workspace <name> to <name>'
+ * Implementation of 'rename workspace [<name>] to <name>'
  *
  */
 void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
-    LOG("Renaming workspace \"%s\" to \"%s\"\n", old_name, new_name);
+    if (old_name) {
+        LOG("Renaming workspace \"%s\" to \"%s\"\n", old_name, new_name);
+    } else {
+        LOG("Renaming current workspace to \"%s\"\n", new_name);
+    }
 
     Con *output, *workspace = NULL;
-    TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
-        GREP_FIRST(workspace, output_get_content(output),
-            !strcasecmp(child->name, old_name));
+    if (old_name) {
+        TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
+            GREP_FIRST(workspace, output_get_content(output),
+                !strcasecmp(child->name, old_name));
+    } else {
+        workspace = con_get_workspace(focused);
+    }
 
     if (!workspace) {
         // TODO: we should include the old workspace name here and use yajl for