]> git.sur5r.net Git - i3/i3/blobdiff - src/con.c
Force container to be redrawn upon moving to another container. (#2769)
[i3/i3] / src / con.c
index 51b2a3f606e5c9ad533d4fc9d27d16ba1e7e93d2..864125961b6bda2322b0e5687552d236b738a884 100644 (file)
--- a/src/con.c
+++ b/src/con.c
@@ -1,5 +1,3 @@
-#undef I3__FILE__
-#define I3__FILE__ "con.c"
 /*
  * vim:ts=4:sw=4:expandtab
  *
@@ -12,6 +10,7 @@
  *
  */
 #include "all.h"
+
 #include "yajl_utils.h"
 
 static void con_on_remove_child(Con *con);
@@ -68,7 +67,7 @@ Con *con_new_skeleton(Con *parent, i3Window *window) {
  */
 Con *con_new(Con *parent, i3Window *window) {
     Con *new = con_new_skeleton(parent, window);
-    x_con_init(new, new->depth);
+    x_con_init(new);
     return new;
 }
 
@@ -411,7 +410,8 @@ Con *con_parent_with_orientation(Con *con, orientation_t orientation) {
 struct bfs_entry {
     Con *con;
 
-    TAILQ_ENTRY(bfs_entry) entries;
+    TAILQ_ENTRY(bfs_entry)
+    entries;
 };
 
 /*
@@ -423,7 +423,9 @@ Con *con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode) {
 
     /* TODO: is breadth-first-search really appropriate? (check as soon as
      * fullscreen levels and fullscreen for containers is implemented) */
-    TAILQ_HEAD(bfs_head, bfs_entry) bfs_head = TAILQ_HEAD_INITIALIZER(bfs_head);
+    TAILQ_HEAD(bfs_head, bfs_entry)
+    bfs_head = TAILQ_HEAD_INITIALIZER(bfs_head);
+
     struct bfs_entry *entry = smalloc(sizeof(struct bfs_entry));
     entry->con = con;
     TAILQ_INSERT_TAIL(&bfs_head, entry, entries);
@@ -617,6 +619,7 @@ void con_mark(Con *con, const char *mark, mark_mode_t mode) {
     mark_t *new = scalloc(1, sizeof(mark_t));
     new->name = sstrdup(mark);
     TAILQ_INSERT_TAIL(&(con->marks_head), new, marks);
+    ipc_send_window_event("mark", con);
 
     con->mark_changed = true;
 }
@@ -645,6 +648,8 @@ void con_unmark(Con *con, const char *name) {
                 FREE(mark->name);
                 TAILQ_REMOVE(&(current->marks_head), mark, marks);
                 FREE(mark);
+
+                ipc_send_window_event("mark", current);
             }
 
             current->mark_changed = true;
@@ -668,6 +673,8 @@ void con_unmark(Con *con, const char *name) {
             FREE(mark->name);
             TAILQ_REMOVE(&(current->marks_head), mark, marks);
             FREE(mark);
+
+            ipc_send_window_event("mark", current);
             break;
         }
     }
@@ -727,6 +734,49 @@ int con_num_children(Con *con) {
     return children;
 }
 
+/**
+ * Returns the number of visible non-floating children of this container.
+ * For example, if the container contains a hsplit which has two children,
+ * this will return 2 instead of 1.
+ */
+int con_num_visible_children(Con *con) {
+    if (con == NULL)
+        return 0;
+
+    int children = 0;
+    Con *current = NULL;
+    TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
+        /* Visible leaf nodes are a child. */
+        if (!con_is_hidden(current) && con_is_leaf(current))
+            children++;
+        /* All other containers need to be recursed. */
+        else
+            children += con_num_visible_children(current);
+    }
+
+    return children;
+}
+
+/*
+ * Count the number of windows (i.e., leaf containers).
+ *
+ */
+int con_num_windows(Con *con) {
+    if (con == NULL)
+        return 0;
+
+    if (con_has_managed_window(con))
+        return 1;
+
+    int num = 0;
+    Con *current = NULL;
+    TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
+        num += con_num_windows(current);
+    }
+
+    return num;
+}
+
 /*
  * Updates the percent attribute of the children of the given container. This
  * function needs to be called when a window is added or removed from a
@@ -1077,9 +1127,13 @@ static bool _con_move_to_con(Con *con, Con *target, bool behind_focused, bool fi
         con_set_urgency(con, true);
     }
 
+    /* Ensure the container will be redrawn. */
+    FREE(con->deco_render_params);
+
     CALL(parent, on_remove_child);
 
     ipc_send_window_event("move", con);
+    ewmh_update_wm_desktop();
     return true;
 }
 
@@ -1156,6 +1210,19 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
     _con_move_to_con(con, target, true, fix_coordinates, dont_warp, ignore_focus);
 }
 
+/*
+ * Moves the given container to the currently focused container on the
+ * visible workspace on the given output.
+ *
+ */
+void con_move_to_output(Con *con, Output *output) {
+    Con *ws = NULL;
+    GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child));
+    assert(ws != NULL);
+    DLOG("Moving con %p to output %s\n", con, output->name);
+    con_move_to_workspace(con, ws, false, false, false);
+}
+
 /*
  * Returns the orientation of the given container (for stacked containers,
  * vertical orientation is used regardless of the actual orientation of the
@@ -1410,6 +1477,12 @@ Con *con_descend_direction(Con *con, direction_t direction) {
  *
  */
 Rect con_border_style_rect(Con *con) {
+    if (config.hide_edge_borders == HEBM_SMART && con_num_visible_children(con_get_workspace(con)) <= 1) {
+        if (!con_is_floating(con)) {
+            return (Rect){0, 0, 0, 0};
+        }
+    }
+
     adjacent_t borders_to_hide = ADJ_NONE;
     int border_width = con->current_border_width;
     DLOG("The border width for con is set to: %d\n", con->current_border_width);
@@ -1569,12 +1642,14 @@ void con_set_layout(Con *con, layout_t layout) {
      * whole workspace into stacked/tabbed mode. To do this and still allow
      * intuitive operations (like level-up and then opening a new window), we
      * need to create a new split container. */
-    if (con->type == CT_WORKSPACE &&
-        (layout == L_STACKED || layout == L_TABBED)) {
+    if (con->type == CT_WORKSPACE) {
         if (con_num_children(con) == 0) {
-            DLOG("Setting workspace_layout to %d\n", layout);
-            con->workspace_layout = layout;
-        } else {
+            layout_t ws_layout = (layout == L_STACKED || layout == L_TABBED) ? layout : L_DEFAULT;
+            DLOG("Setting workspace_layout to %d\n", ws_layout);
+            con->workspace_layout = ws_layout;
+            DLOG("Setting layout to %d\n", layout);
+            con->layout = layout;
+        } else if (layout == L_STACKED || layout == L_TABBED) {
             DLOG("Creating new split container\n");
             /* 1: create a new split container */
             Con *new = con_new(NULL, NULL);
@@ -1649,28 +1724,64 @@ void con_toggle_layout(Con *con, const char *toggle_mode) {
         parent = con->parent;
     DLOG("con_toggle_layout(%p, %s), parent = %p\n", con, toggle_mode, parent);
 
-    if (strcmp(toggle_mode, "split") == 0) {
-        /* Toggle between splits. When the current layout is not a split
-         * layout, we just switch back to last_split_layout. Otherwise, we
-         * change to the opposite split layout. */
-        if (parent->layout != L_SPLITH && parent->layout != L_SPLITV)
-            con_set_layout(con, parent->last_split_layout);
-        else {
-            if (parent->layout == L_SPLITH)
-                con_set_layout(con, L_SPLITV);
-            else
-                con_set_layout(con, L_SPLITH);
+    const char delim[] = " ";
+
+    if (strcasecmp(toggle_mode, "split") == 0 || strstr(toggle_mode, delim)) {
+        /* L_DEFAULT is used as a placeholder value to distinguish if
+         * the first layout has already been saved. (it can never be L_DEFAULT) */
+        layout_t new_layout = L_DEFAULT;
+        bool current_layout_found = false;
+        char *tm_dup = sstrdup(toggle_mode);
+        char *cur_tok = strtok(tm_dup, delim);
+
+        for (layout_t layout; cur_tok != NULL; cur_tok = strtok(NULL, delim)) {
+            if (strcasecmp(cur_tok, "split") == 0) {
+                /* Toggle between splits. When the current layout is not a split
+                 * layout, we just switch back to last_split_layout. Otherwise, we
+                 * change to the opposite split layout. */
+                if (parent->layout != L_SPLITH && parent->layout != L_SPLITV) {
+                    layout = parent->last_split_layout;
+                } else {
+                    layout = (parent->layout == L_SPLITH) ? L_SPLITV : L_SPLITH;
+                }
+            } else {
+                bool success = layout_from_name(cur_tok, &layout);
+                if (!success || layout == L_DEFAULT) {
+                    ELOG("The token '%s' was not recognized and has been skipped.\n", cur_tok);
+                    continue;
+                }
+            }
+
+            /* If none of the specified layouts match the current,
+             * fall back to the first layout in the list */
+            if (new_layout == L_DEFAULT) {
+                new_layout = layout;
+            }
+
+            /* We found the active layout in the last iteration, so
+             * now let's activate the current layout (next in list) */
+            if (current_layout_found) {
+                new_layout = layout;
+                free(tm_dup);
+                break;
+            }
+
+            if (parent->layout == layout) {
+                current_layout_found = true;
+            }
         }
-    } else {
+
+        con_set_layout(con, new_layout);
+    } else if (strcasecmp(toggle_mode, "all") == 0 || strcasecmp(toggle_mode, "default") == 0) {
         if (parent->layout == L_STACKED)
             con_set_layout(con, L_TABBED);
         else if (parent->layout == L_TABBED) {
-            if (strcmp(toggle_mode, "all") == 0)
+            if (strcasecmp(toggle_mode, "all") == 0)
                 con_set_layout(con, L_SPLITH);
             else
                 con_set_layout(con, parent->last_split_layout);
         } else if (parent->layout == L_SPLITH || parent->layout == L_SPLITV) {
-            if (strcmp(toggle_mode, "all") == 0) {
+            if (strcasecmp(toggle_mode, "all") == 0) {
                 /* When toggling through all modes, we toggle between
                  * splith/splitv, whereas normally we just directly jump to
                  * stacked. */
@@ -1885,6 +1996,13 @@ bool con_has_urgent_child(Con *con) {
 void con_update_parents_urgency(Con *con) {
     Con *parent = con->parent;
 
+    /* Urgency hints should not be set on any container higher up in the
+     * hierarchy than the workspace level. Unfortunately, since the content
+     * container has type == CT_CON, that’s not easy to verify in the loop
+     * below, so we need another condition to catch that case: */
+    if (con->type == CT_WORKSPACE)
+        return;
+
     bool new_urgency_value = con->urgent;
     while (parent && parent->type != CT_WORKSPACE && parent->type != CT_DOCKAREA) {
         if (new_urgency_value) {
@@ -1992,6 +2110,7 @@ char *con_get_tree_representation(Con *con) {
                   (TAILQ_FIRST(&(con->nodes_head)) == child ? "" : " "), child_txt);
         free(buf);
         buf = tmp_buf;
+        free(child_txt);
     }
 
     /* 3) close the brackets */