]> git.sur5r.net Git - i3/i3/blobdiff - src/tree.c
format **/*.c with clang-format-3.5
[i3/i3] / src / tree.c
index 3d598d50ab8014410ad9a5c38eb956564f98cd6a..da73e14d76fc12b996a76ed3283cdb7410e62f28 100644 (file)
@@ -76,23 +76,22 @@ bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry) {
 
     /* TODO: refactor the following */
     croot = con_new(NULL, NULL);
-    croot->rect = (Rect){
+    croot->rect = (Rect) {
         geometry->x,
         geometry->y,
         geometry->width,
-        geometry->height
-    };
+        geometry->height};
     focused = croot;
 
-    tree_append_json(globbed);
+    tree_append_json(focused, globbed, NULL);
 
-    printf("appended tree, using new root\n");
+    DLOG("appended tree, using new root\n");
     croot = TAILQ_FIRST(&(croot->nodes_head));
-    printf("new root = %p\n", croot);
+    DLOG("new root = %p\n", croot);
     Con *out = TAILQ_FIRST(&(croot->nodes_head));
-    printf("out = %p\n", out);
+    DLOG("out = %p\n", out);
     Con *ws = TAILQ_FIRST(&(out->nodes_head));
-    printf("ws = %p\n", ws);
+    DLOG("ws = %p\n", ws);
 
     /* For in-place restarting into v4.2, we need to make sure the new
      * pseudo-output __i3 is present. */
@@ -119,12 +118,11 @@ void tree_init(xcb_get_geometry_reply_t *geometry) {
     croot->name = "root";
     croot->type = CT_ROOT;
     croot->layout = L_SPLITH;
-    croot->rect = (Rect){
+    croot->rect = (Rect) {
         geometry->x,
         geometry->y,
         geometry->width,
-        geometry->height
-    };
+        geometry->height};
 
     _create___i3();
 }
@@ -169,7 +167,7 @@ Con *tree_open_con(Con *con, i3Window *window) {
 static bool _is_con_mapped(Con *con) {
     Con *child;
 
-    TAILQ_FOREACH(child, &(con->nodes_head), nodes)
+    TAILQ_FOREACH (child, &(con->nodes_head), nodes)
         if (_is_con_mapped(child))
             return true;
 
@@ -200,6 +198,13 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool
         was_mapped = _is_con_mapped(con);
     }
 
+    /* remove the urgency hint of the workspace (if set) */
+    if (con->urgent) {
+        con->urgent = false;
+        con_update_parents_urgency(con);
+        workspace_update_urgent_flag(con_get_workspace(con));
+    }
+
     /* Get the container which is next focused */
     Con *next = con_next_focused(con);
     DLOG("next = %p, focused = %p\n", next, focused);
@@ -209,7 +214,7 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool
     bool abort_kill = false;
     /* We cannot use TAILQ_FOREACH because the children get deleted
      * in their parent’s nodes_head */
-    for (child = TAILQ_FIRST(&(con->nodes_head)); child; ) {
+    for (child = TAILQ_FIRST(&(con->nodes_head)); child;) {
         nextchild = TAILQ_NEXT(child, nodes);
         DLOG("killing child=%p\n", child);
         if (!tree_close(child, kill_window, true, false))
@@ -228,7 +233,12 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool
             return false;
         } else {
             xcb_void_cookie_t cookie;
-            /* un-parent the window */
+            /* Ignore any further events by clearing the event mask,
+             * unmap the window,
+             * then reparent it to the root window. */
+            xcb_change_window_attributes(conn, con->window->id,
+                                         XCB_CW_EVENT_MASK, (uint32_t[]) {XCB_NONE});
+            xcb_unmap_window(conn, con->window->id);
             cookie = xcb_reparent_window(conn, con->window->id, root, 0, 0);
 
             /* Ignore X11 errors for the ReparentWindow request.
@@ -237,9 +247,9 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool
 
             /* We are no longer handling this window, thus set WM_STATE to
              * WM_STATE_WITHDRAWN (see ICCCM 4.1.3.1) */
-            long data[] = { XCB_ICCCM_WM_STATE_WITHDRAWN, XCB_NONE };
+            long data[] = {XCB_ICCCM_WM_STATE_WITHDRAWN, XCB_NONE};
             cookie = xcb_change_property(conn, XCB_PROP_MODE_REPLACE,
-                        con->window->id, A_WM_STATE, A_WM_STATE, 32, 2, data);
+                                         con->window->id, A_WM_STATE, A_WM_STATE, 32, 2, data);
 
             /* Ignore X11 errors for the ReparentWindow request.
              * X11 Errors are returned when the window was already destroyed */
@@ -248,18 +258,32 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool
         FREE(con->window->class_class);
         FREE(con->window->class_instance);
         i3string_free(con->window->name);
-        free(con->window);
+        FREE(con->window->ran_assignments);
+        FREE(con->window);
     }
 
-    /* kill the X11 part of this container */
-    x_con_kill(con);
+    Con *ws = con_get_workspace(con);
+
+    /* Figure out which container to focus next before detaching 'con'. */
+    if (con_is_floating(con)) {
+        if (con == focused) {
+            DLOG("This is the focused container, i need to find another one to focus. I start looking at ws = %p\n", ws);
+            next = con_next_focused(parent);
+
+            dont_kill_parent = true;
+            DLOG("Alright, focusing %p\n", next);
+        } else {
+            next = NULL;
+        }
+    }
 
+    /* Detach the container so that it will not be rendered anymore. */
     con_detach(con);
 
     /* disable urgency timer, if needed */
     if (con->urgency_timer != NULL) {
         DLOG("Removing urgency timer of con %p\n", con);
-        workspace_update_urgent_flag(con_get_workspace(con));
+        workspace_update_urgent_flag(ws);
         ev_timer_stop(main_loop, con->urgency_timer);
         FREE(con->urgency_timer);
     }
@@ -270,21 +294,24 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool
         con_fix_percent(parent);
     }
 
+    /* Render the tree so that the surrounding containers take up the space
+     * which 'con' does no longer occupy. If we don’t render here, there will
+     * be a gap in our containers and that could trigger an EnterNotify for an
+     * underlying container, see ticket #660.
+     *
+     * Rendering has to be avoided when dont_kill_parent is set (when
+     * tree_close calls itself recursively) because the tree is in a
+     * non-renderable state during that time. */
+    if (!dont_kill_parent)
+        tree_render();
+
+    /* kill the X11 part of this container */
+    x_con_kill(con);
+
     if (con_is_floating(con)) {
-        Con *ws = con_get_workspace(con);
         DLOG("Container was floating, killing floating container\n");
         tree_close(parent, DONT_KILL_WINDOW, false, (con == focused));
         DLOG("parent container killed\n");
-        if (con == focused) {
-            DLOG("This is the focused container, i need to find another one to focus. I start looking at ws = %p\n", ws);
-            /* go down the focus stack as far as possible */
-            next = con_descend_focused(ws);
-
-            dont_kill_parent = true;
-            DLOG("Alright, focusing %p\n", next);
-        } else {
-            next = NULL;
-        }
     }
 
     free(con->name);
@@ -308,10 +335,10 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool
             } else {
                 if (!force_set_focus && con != focused)
                     DLOG("not changing focus, the container was not focused before\n");
-                else con_focus(next);
+                else
+                    con_focus(next);
             }
-        }
-        else {
+        } else {
             DLOG("not focusing because we're not killing anybody\n");
         }
     } else {
@@ -331,15 +358,24 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool
  */
 void tree_close_con(kill_window_t kill_window) {
     assert(focused != NULL);
-    if (focused->type == CT_WORKSPACE) {
-        LOG("Cannot close workspace\n");
-        return;
-    }
 
     /* There *should* be no possibility to focus outputs / root container */
     assert(focused->type != CT_OUTPUT);
     assert(focused->type != CT_ROOT);
 
+    if (focused->type == CT_WORKSPACE) {
+        DLOG("Workspaces cannot be close, closing all children instead\n");
+        Con *child, *nextchild;
+        for (child = TAILQ_FIRST(&(focused->focus_head)); child;) {
+            nextchild = TAILQ_NEXT(child, focused);
+            DLOG("killing child=%p\n", child);
+            tree_close(child, kill_window, false, false);
+            child = nextchild;
+        }
+
+        return;
+    }
+
     /* Kill con */
     tree_close(focused, kill_window, false, false);
 }
@@ -350,13 +386,23 @@ void tree_close_con(kill_window_t kill_window) {
  *
  */
 void tree_split(Con *con, orientation_t orientation) {
-    /* for a workspace, we just need to change orientation */
-    if (con->type == CT_WORKSPACE) {
-        DLOG("Workspace, simply changing orientation to %d\n", orientation);
-        con->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV;
+    if (con_is_floating(con)) {
+        DLOG("Floating containers can't be split.\n");
         return;
     }
 
+    if (con->type == CT_WORKSPACE) {
+        if (con_num_children(con) < 2) {
+            DLOG("Just changing orientation of workspace\n");
+            con->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV;
+            return;
+        } else {
+            /* if there is more than one container on the workspace
+             * move them into a new container and handle this instead */
+            con = workspace_encapsulate(con);
+        }
+    }
+
     Con *parent = con->parent;
 
     /* Force re-rendering to make the indicator border visible. */
@@ -382,7 +428,6 @@ void tree_split(Con *con, orientation_t orientation) {
     TAILQ_REPLACE(&(parent->focus_head), con, new, focused);
     new->parent = parent;
     new->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV;
-    new->split = true;
 
     /* 3: swap 'percent' (resize factor) */
     new->percent = con->percent;
@@ -397,9 +442,16 @@ void tree_split(Con *con, orientation_t orientation) {
  *
  */
 bool level_up(void) {
+    /* Skip over floating containers and go directly to the grandparent
+     * (which should always be a workspace) */
+    if (focused->parent->type == CT_FLOATING_CON) {
+        con_focus(focused->parent->parent);
+        return true;
+    }
+
     /* We can focus up to the workspace, but not any higher in the tree */
     if ((focused->parent->type != CT_CON &&
-        focused->parent->type != CT_WORKSPACE) ||
+         focused->parent->type != CT_WORKSPACE) ||
         focused->type == CT_WORKSPACE) {
         ELOG("'focus parent': Focus is already on the workspace, cannot go higher than that.\n");
         return false;
@@ -416,9 +468,19 @@ bool level_down(void) {
     /* Go down the focus stack of the current node */
     Con *next = TAILQ_FIRST(&(focused->focus_head));
     if (next == TAILQ_END(&(focused->focus_head))) {
-        printf("cannot go down\n");
+        DLOG("cannot go down\n");
         return false;
+    } else if (next->type == CT_FLOATING_CON) {
+        /* Floating cons shouldn't be directly focused; try immediately
+         * going to the grandchild of the focused con. */
+        Con *child = TAILQ_FIRST(&(next->focus_head));
+        if (child == TAILQ_END(&(next->focus_head))) {
+            DLOG("cannot go down\n");
+            return false;
+        } else
+            next = TAILQ_FIRST(&(next->focus_head));
     }
+
     con_focus(next);
     return true;
 }
@@ -427,12 +489,12 @@ static void mark_unmapped(Con *con) {
     Con *current;
 
     con->mapped = false;
-    TAILQ_FOREACH(current, &(con->nodes_head), nodes)
+    TAILQ_FOREACH (current, &(con->nodes_head), nodes)
         mark_unmapped(current);
     if (con->type == CT_WORKSPACE) {
         /* We need to call mark_unmapped on floating nodes aswell since we can
          * make containers floating. */
-        TAILQ_FOREACH(current, &(con->floating_head), floating_windows)
+        TAILQ_FOREACH (current, &(con->floating_head), floating_windows)
             mark_unmapped(current);
     }
 }
@@ -463,9 +525,20 @@ void tree_render(void) {
  *
  */
 static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) {
+    /* When dealing with fullscreen containers, it's necessary to go up to the
+     * workspace level, because 'focus $dir' will start at the con's real
+     * position in the tree, and it may not be possible to get to the edge
+     * normally due to fullscreen focusing restrictions. */
+    if (con->fullscreen_mode == CF_OUTPUT && con->type != CT_WORKSPACE)
+        con = con_get_workspace(con);
+
     /* Stop recursing at workspaces after attempting to switch to next
      * workspace if possible. */
     if (con->type == CT_WORKSPACE) {
+        if (con_get_fullscreen_con(con, CF_GLOBAL)) {
+            DLOG("Cannot change workspace while in global fullscreen mode.\n");
+            return false;
+        }
         Output *current_output = get_output_containing(con->rect.x, con->rect.y);
         Output *next_output;
 
@@ -500,6 +573,13 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
             return false;
 
         workspace_show(workspace);
+
+        /* If a workspace has an active fullscreen container, one of its
+         * children should always be focused. The above workspace_show()
+         * should be adequate for that, so return. */
+        if (con_get_fullscreen_con(workspace, CF_OUTPUT))
+            return true;
+
         Con *focus = con_descend_direction(workspace, direction);
         if (focus) {
             con_focus(focus);
@@ -516,13 +596,15 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
             Con *next;
             if (way == 'n')
                 next = TAILQ_NEXT(con, floating_windows);
-            else next = TAILQ_PREV(con, floating_head, floating_windows);
+            else
+                next = TAILQ_PREV(con, floating_head, floating_windows);
 
             /* If there is no next/previous container, wrap */
             if (!next) {
                 if (way == 'n')
                     next = TAILQ_FIRST(&(parent->floating_head));
-                else next = TAILQ_LAST(&(parent->floating_head), floating_head);
+                else
+                    next = TAILQ_LAST(&(parent->floating_head), floating_head);
             }
 
             /* Still no next/previous container? bail out */
@@ -555,7 +637,8 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
     Con *next;
     if (way == 'n')
         next = TAILQ_NEXT(current, nodes);
-    else next = TAILQ_PREV(current, nodes_head, nodes);
+    else
+        next = TAILQ_PREV(current, nodes_head, nodes);
 
     if (!next) {
         if (!config.force_focus_wrapping) {
@@ -571,7 +654,8 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
 
         if (way == 'n')
             next = TAILQ_FIRST(&(parent->nodes_head));
-        else next = TAILQ_LAST(&(parent->nodes_head), nodes_head);
+        else
+            next = TAILQ_LAST(&(parent->nodes_head), nodes_head);
     }
 
     /* Don't violate fullscreen focus restrictions. */
@@ -626,8 +710,10 @@ void tree_flatten(Con *con) {
 
     /* The child must have a different orientation than the con but the same as
      * the con’s parent to be redundant */
-    if (!con->split ||
-        !child->split ||
+    if (!con_is_split(con) ||
+        !con_is_split(child) ||
+        (con->layout != L_SPLITH && con->layout != L_SPLITV) ||
+        (child->layout != L_SPLITH && child->layout != L_SPLITV) ||
         con_orientation(con) == con_orientation(child) ||
         con_orientation(child) != con_orientation(parent))
         goto recurse;