]> git.lizzy.rs Git - bspwm.git/blobdiff - tree.c
Handle min/max window size hints
[bspwm.git] / tree.c
diff --git a/tree.c b/tree.c
index d43b168ef30be9550d2212f2ac8414d7ac2e9a45..f18b6d9fba4aa90f91b41ff6b3bf51e058518104 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -1,6 +1,29 @@
+/* * Copyright (c) 2012-2013 Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
 #include <float.h>
 #include <limits.h>
-#include <math.h>
 #include "bspwm.h"
 #include "desktop.h"
 #include "ewmh.h"
@@ -9,7 +32,6 @@
 #include "query.h"
 #include "settings.h"
 #include "stack.h"
-#include "tag.h"
 #include "window.h"
 #include "tree.h"
 
@@ -22,10 +44,10 @@ void arrange(monitor_t *m, desktop_t *d)
 
     xcb_rectangle_t rect = m->rectangle;
     int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : d->window_gap);
-    rect.x += m->left_padding + wg;
-    rect.y += m->top_padding + wg;
-    rect.width -= m->left_padding + m->right_padding + wg;
-    rect.height -= m->top_padding + m->bottom_padding + wg;
+    rect.x += m->left_padding + d->left_padding + wg;
+    rect.y += m->top_padding + d->top_padding + wg;
+    rect.width -= m->left_padding + d->left_padding + d->right_padding + m->right_padding + wg;
+    rect.height -= m->top_padding + d->top_padding + d->bottom_padding + m->bottom_padding + wg;
     apply_layout(m, d, d->root, rect, rect);
 }
 
@@ -38,14 +60,8 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, x
 
     if (is_leaf(n)) {
 
-        if (is_floating(n->client) && n->client->border_width != d->border_width) {
-            int ds = 2 * (d->border_width - n->client->border_width);
-            n->client->floating_rectangle.width += ds;
-            n->client->floating_rectangle.height += ds;
-        }
-
-        if ((borderless_monocle && is_tiled(n->client) && d->layout == LAYOUT_MONOCLE) ||
-                n->client->fullscreen)
+        if ((borderless_monocle && is_tiled(n->client) && d->layout == LAYOUT_MONOCLE)
+                || n->client->fullscreen)
             n->client->border_width = 0;
         else
             n->client->border_width = d->border_width;
@@ -53,17 +69,18 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, x
         xcb_rectangle_t r;
         if (!n->client->fullscreen) {
             if (!n->client->floating) {
-                /* tiled clients */
-                if (d->layout == LAYOUT_TILED)
+                if (n->client->pseudo_tiled) {
+                /* pseudo-tiled clients */
+                    r = n->client->floating_rectangle;
+                    center_rectangle(&r, rect);
+                } else {
+                    /* tiled clients */
                     r = rect;
-                else if (d->layout == LAYOUT_MONOCLE)
-                    r = root_rect;
-                else
-                    return;
-                int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : d->window_gap);
-                int bleed = wg + 2 * n->client->border_width;
-                r.width = (bleed < r.width ? r.width - bleed : 1);
-                r.height = (bleed < r.height ? r.height - bleed : 1);
+                    int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : d->window_gap);
+                    int bleed = wg + 2 * n->client->border_width;
+                    r.width = (bleed < r.width ? r.width - bleed : 1);
+                    r.height = (bleed < r.height ? r.height - bleed : 1);
+                }
                 n->client->tiled_rectangle = r;
             } else {
                 /* floating clients */
@@ -76,13 +93,13 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, x
 
         window_move_resize(n->client->window, r.x, r.y, r.width, r.height);
         window_border_width(n->client->window, n->client->border_width);
-        window_draw_border(n, n == d->focus, m == mon);
+        window_draw_border(n, d->focus == n, m == mon);
 
     } else {
         xcb_rectangle_t first_rect;
         xcb_rectangle_t second_rect;
 
-        if (n->first_child->vacant || n->second_child->vacant) {
+        if (d->layout == LAYOUT_MONOCLE || n->first_child->vacant || n->second_child->vacant) {
             first_rect = second_rect = rect;
         } else {
             unsigned int fence;
@@ -120,8 +137,6 @@ void insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f)
 
     if (f == NULL) {
         d->root = n;
-        if (is_visible(d, n))
-            d->focus = n;
     } else {
         node_t *c = make_node();
         node_t *p = f->parent;
@@ -130,6 +145,28 @@ void insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f)
             f = p;
             p = f->parent;
         }
+        if (((f->client != NULL && f->client->private) || (p != NULL && p->privacy_level > 0))
+                    && f->split_mode == MODE_AUTOMATIC) {
+            node_t *closest = NULL;
+            node_t *public = NULL;
+            closest_public(d, f, &closest, &public);
+            if (public != NULL) {
+                f = public;
+                p = f->parent;
+            } else {
+                if (closest != NULL) {
+                    f = closest;
+                    p = f->parent;
+                }
+                f->split_mode = MODE_MANUAL;
+                xcb_rectangle_t rect = f->client->tiled_rectangle;
+                f->split_dir = (rect.width >= rect.height ? DIR_LEFT : DIR_UP);
+                if (f->client->private) {
+                    get_opposite(f->split_dir, &f->split_dir);
+                    update_privacy_level(f, false);
+                }
+            }
+        }
         n->parent = c;
         c->birth_rotation = f->birth_rotation;
         switch (f->split_mode) {
@@ -211,18 +248,29 @@ void insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f)
                 break;
         }
         if (f->vacant)
-            update_vacant_state(p);
+            update_vacant_state(f->parent);
+        if (f->client != NULL && f->client->private)
+            update_privacy_level(f, true);
     }
+    if (n->client->private)
+        update_privacy_level(n, true);
+    if (d->focus == NULL)
+        d->focus = n;
     if (n->client->sticky)
         m->num_sticky++;
     put_status();
 }
 
-void pseudo_focus(desktop_t *d, node_t *n)
+void pseudo_focus(monitor_t *m, desktop_t *d, node_t *n)
 {
+    if (n != NULL) {
+        stack(n, STACK_ABOVE);
+        if (d->focus != n) {
+            window_draw_border(d->focus, false, m == mon);
+            window_draw_border(n, true, m == mon);
+        }
+    }
     d->focus = n;
-    if (n != NULL)
-        stack(n);
 }
 
 void focus_node(monitor_t *m, desktop_t *d, node_t *n)
@@ -230,17 +278,17 @@ void focus_node(monitor_t *m, desktop_t *d, node_t *n)
     if (mon->desk != d || n == NULL)
         clear_input_focus();
 
-    if (m->num_sticky > 0 && mon == m && d != mon->desk) {
-        node_t *a = first_extrema(mon->desk->root);
+    if (m->num_sticky > 0 && d != m->desk) {
+        node_t *a = first_extrema(m->desk->root);
         sticky_still = false;
         while (a != NULL) {
-            node_t *b = next_leaf(a, mon->desk->root);
+            node_t *b = next_leaf(a, m->desk->root);
             if (a->client->sticky)
-                transfer_node(mon, mon->desk, a, m, d, d->focus);
+                transfer_node(m, m->desk, a, m, d, d->focus);
             a = b;
         }
         sticky_still = true;
-        if (n == NULL && d->focus != NULL && is_visible(d, d->focus))
+        if (n == NULL && d->focus != NULL)
             n = d->focus;
     }
 
@@ -265,20 +313,21 @@ void focus_node(monitor_t *m, desktop_t *d, node_t *n)
     }
 
     focus_desktop(m, d);
-    pseudo_focus(d, n);
+
+    d->focus = n;
 
     if (n == NULL) {
         history_add(m, d, NULL);
         ewmh_update_active_window();
         return;
+    } else {
+        stack(n, STACK_ABOVE);
     }
 
     PRINTF("focus node %X\n", n->client->window);
 
     n->client->urgent = false;
 
-    if (!is_visible(d, n))
-        tag_node(m, d, n, d, n->client->tags_field | d->tags_field);
     history_add(m, d, n);
     set_input_focus(n);
 
@@ -307,6 +356,7 @@ node_t *make_node(void)
     n->split_mode = MODE_AUTOMATIC;
     n->split_type = TYPE_VERTICAL;
     n->birth_rotation = 0;
+    n->privacy_level = 0;
     n->client = NULL;
     n->vacant = false;
     return n;
@@ -315,25 +365,28 @@ node_t *make_node(void)
 client_t *make_client(xcb_window_t win)
 {
     client_t *c = malloc(sizeof(client_t));
+    c->window = win;
     snprintf(c->class_name, sizeof(c->class_name), "%s", MISSING_VALUE);
+    snprintf(c->instance_name, sizeof(c->instance_name), "%s", MISSING_VALUE);
     c->border_width = BORDER_WIDTH;
-    c->window = win;
-    c->floating = c->transient = c->fullscreen = c->locked = c->sticky = c->urgent = false;
-    c->icccm_focus = false;
+    c->pseudo_tiled = c->floating = c->fullscreen = false;
+    c->locked = c->sticky = c->urgent = c->private = c->icccm_focus = false;
     xcb_icccm_get_wm_protocols_reply_t protocols;
     if (xcb_icccm_get_wm_protocols_reply(dpy, xcb_icccm_get_wm_protocols(dpy, win, ewmh->WM_PROTOCOLS), &protocols, NULL) == 1) {
         if (has_proto(WM_TAKE_FOCUS, &protocols))
             c->icccm_focus = true;
         xcb_icccm_get_wm_protocols_reply_wipe(&protocols);
     }
+    c->num_states = 0;
+    xcb_ewmh_get_atoms_reply_t wm_state;
+    if (xcb_ewmh_get_wm_state_reply(ewmh, xcb_ewmh_get_wm_state(ewmh, win), &wm_state, NULL) == 1) {
+        for (unsigned int i = 0; i < wm_state.atoms_len && i < MAX_STATE; i++)
+            ewmh_wm_state_add(c, wm_state.atoms[i]);
+        xcb_ewmh_get_atoms_reply_wipe(&wm_state);
+    }
     return c;
 }
 
-bool is_visible(desktop_t *d, node_t *n)
-{
-    return (d->tags_field & n->client->tags_field) != 0;
-}
-
 bool is_leaf(node_t *n)
 {
     return (n != NULL && n->first_child == NULL && n->second_child == NULL);
@@ -363,12 +416,6 @@ bool is_second_child(node_t *n)
     return (n != NULL && n->parent != NULL && n->parent->second_child == n);
 }
 
-void change_split_ratio(node_t *n, value_change_t chg)
-{
-    n->split_ratio = pow(n->split_ratio,
-            (chg == CHANGE_INCREASE ? (1 / growth_factor) : growth_factor));
-}
-
 void reset_mode(coordinates_t *loc)
 {
     if (loc->node != NULL) {
@@ -392,27 +439,31 @@ node_t *brother_tree(node_t *n)
         return n->parent->first_child;
 }
 
-node_t *closest_visible(desktop_t *d, node_t *n)
+void closest_public(desktop_t *d, node_t *n, node_t **closest, node_t **public)
 {
     if (n == NULL)
-        return NULL;
+        return;
     node_t *prev = prev_leaf(n, d->root);
     node_t *next = next_leaf(n, d->root);
     while (prev != NULL || next != NULL) {
-        if (prev != NULL) {
-            if (is_visible(d, prev))
-                return prev;
-            else
-                prev = prev_leaf(prev, d->root);
-        }
-        if (next != NULL) {
-            if (is_visible(d, next))
-                return next;
-            else
-                next = next_leaf(next, d->root);
+#define TESTLOOP(n) \
+        if (n != NULL) { \
+            if (is_tiled(n->client)) { \
+                if (n->privacy_level == 0) { \
+                    if (n->parent == NULL || n->parent->privacy_level == 0) { \
+                        *public = n; \
+                        return; \
+                    } else if (*closest == NULL) { \
+                        *closest = n; \
+                    } \
+                } \
+            } \
+            n = n##_leaf(n, d->root); \
         }
+        TESTLOOP(prev)
+        TESTLOOP(next)
+#undef TESTLOOP
     }
-    return NULL;
 }
 
 node_t *first_extrema(node_t *n)
@@ -459,22 +510,22 @@ node_t *prev_leaf(node_t *n, node_t *r)
     return second_extrema(p->parent->first_child);
 }
 
-node_t *next_visible_leaf(desktop_t *d, node_t *n, node_t *r)
+node_t *next_tiled_leaf(desktop_t *d, node_t *n, node_t *r)
 {
     node_t *next = next_leaf(n, r);
-    if (next == NULL || is_visible(d, next))
+    if (next == NULL || is_tiled(next->client))
         return next;
     else
-        return next_visible_leaf(d, next, r);
+        return next_tiled_leaf(d, next, r);
 }
 
-node_t *prev_visible_leaf(desktop_t *d, node_t *n, node_t *r)
+node_t *prev_tiled_leaf(desktop_t *d, node_t *n, node_t *r)
 {
     node_t *prev = prev_leaf(n, r);
-    if (prev == NULL || is_visible(d, prev))
+    if (prev == NULL || is_tiled(prev->client))
         return prev;
     else
-        return prev_visible_leaf(d, prev, r);
+        return prev_tiled_leaf(d, prev, r);
 }
 
 /* bool is_adjacent(node_t *a, node_t *r) */
@@ -531,8 +582,7 @@ node_t *find_fence(node_t *n, direction_t dir)
     return NULL;
 }
 
-
-node_t *nearest_neighbor(desktop_t *d, node_t *n, direction_t dir, client_select_t sel)
+node_t *nearest_neighbor(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, client_select_t sel)
 {
     if (n == NULL || n->client->fullscreen
             || (d->layout == LAYOUT_MONOCLE && is_tiled(n->client)))
@@ -540,13 +590,13 @@ node_t *nearest_neighbor(desktop_t *d, node_t *n, direction_t dir, client_select
 
     node_t *nearest = NULL;
     if (history_aware_focus)
-        nearest = nearest_from_history(d, n, dir, sel);
+        nearest = nearest_from_history(m, d, n, dir, sel);
     if (nearest == NULL)
-        nearest = nearest_from_distance(d, n, dir, sel);
+        nearest = nearest_from_distance(m, d, n, dir, sel);
     return nearest;
 }
 
-node_t *nearest_from_history(desktop_t *d, node_t *n, direction_t dir, client_select_t sel)
+node_t *nearest_from_history(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, client_select_t sel)
 {
     if (n == NULL || !is_tiled(n->client))
         return NULL;
@@ -561,11 +611,13 @@ node_t *nearest_from_history(desktop_t *d, node_t *n, direction_t dir, client_se
 
     node_t *nearest = NULL;
     int min_rank = INT_MAX;
+    coordinates_t ref = {m, d, n};
 
     for (node_t *a = first_extrema(target); a != NULL; a = next_leaf(a, target)) {
         if (a->vacant || !is_adjacent(n, a, dir) || a == n)
             continue;
-        if (!node_matches(n, a, sel))
+        coordinates_t loc = {m, d, a};
+        if (!node_matches(&loc, &ref, sel))
             continue;
 
         int rank = history_rank(d, a);
@@ -578,7 +630,7 @@ node_t *nearest_from_history(desktop_t *d, node_t *n, direction_t dir, client_se
     return nearest;
 }
 
-node_t *nearest_from_distance(desktop_t *d, node_t *n, direction_t dir, client_select_t sel)
+node_t *nearest_from_distance(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, client_select_t sel)
 {
     if (n == NULL)
         return NULL;
@@ -604,11 +656,12 @@ node_t *nearest_from_distance(desktop_t *d, node_t *n, direction_t dir, client_s
     get_side_handle(n->client, dir, &pt);
     get_opposite(dir, &dir2);
     double ds = DBL_MAX;
+    coordinates_t ref = {m, d, n};
 
     for (node_t *a = first_extrema(target); a != NULL; a = next_leaf(a, target)) {
+        coordinates_t loc = {m, d, a};
         if (a == n ||
-                !is_visible(d, a) ||
-                !node_matches(n, a, sel) ||
+                !node_matches(&loc, &ref, sel) ||
                 is_tiled(a->client) != is_tiled(n->client) ||
                 (is_tiled(a->client) && !is_adjacent(n, a, dir)))
             continue;
@@ -650,16 +703,18 @@ int tiled_area(node_t *n)
     return rect.width * rect.height;
 }
 
-node_t *find_biggest(desktop_t *d, node_t *c, client_select_t sel)
+node_t *find_biggest(monitor_t *m, desktop_t *d, node_t *n, client_select_t sel)
 {
     if (d == NULL)
         return NULL;
 
     node_t *r = NULL;
     int r_area = tiled_area(r);
+    coordinates_t ref = {m, d, n};
 
     for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
-        if (!is_visible(d, f) || !is_tiled(f->client) || !node_matches(c, f, sel))
+        coordinates_t loc = {m, d, f};
+        if (!is_tiled(f->client) || !node_matches(&loc, &ref, sel))
             continue;
         int f_area = tiled_area(f);
         if (r == NULL) {
@@ -674,18 +729,6 @@ node_t *find_biggest(desktop_t *d, node_t *c, client_select_t sel)
     return r;
 }
 
-void move_fence(node_t *n, direction_t dir, fence_move_t mov)
-{
-    if (n == NULL)
-        return;
-
-    if ((mov == MOVE_PUSH && (dir == DIR_RIGHT || dir == DIR_DOWN))
-            || (mov == MOVE_PULL && (dir == DIR_LEFT || dir == DIR_UP)))
-        change_split_ratio(n, CHANGE_INCREASE);
-    else
-        change_split_ratio(n, CHANGE_DECREASE);
-}
-
 void rotate_tree(node_t *n, int deg)
 {
     if (n == NULL || is_leaf(n) || deg == 0)
@@ -749,6 +792,17 @@ void flip_tree(node_t *n, flip_t flp)
     flip_tree(n->second_child, flp);
 }
 
+void equalize_tree(node_t *n)
+{
+    if (n == NULL || n->vacant) {
+        return;
+    } else {
+        n->split_ratio = split_ratio;
+        equalize_tree(n->first_child);
+        equalize_tree(n->second_child);
+    }
+}
+
 int balance_tree(node_t *n)
 {
     if (n == NULL || n->vacant) {
@@ -778,11 +832,8 @@ void unlink_node(monitor_t *m, desktop_t *d, node_t *n)
         d->root = NULL;
         d->focus = NULL;
     } else {
-        if (n == d->focus) {
-            d->focus = history_get_node(d, n);
-            if (d->focus == NULL)
-                d->focus = closest_visible(d, n);
-        }
+        if (n->client->private)
+            update_privacy_level(n, false);
 
         node_t *b;
         node_t *g = p->parent;
@@ -812,6 +863,13 @@ void unlink_node(monitor_t *m, desktop_t *d, node_t *n)
         n->parent = NULL;
         free(p);
         update_vacant_state(b->parent);
+
+        if (n == d->focus) {
+            d->focus = history_get_node(d, n);
+            // fallback to the first extrema (`n` is not reachable)
+            if (d->focus == NULL)
+                d->focus = first_extrema(d->root);
+        }
     }
     if (n->client->sticky)
         m->num_sticky--;
@@ -825,6 +883,7 @@ void remove_node(monitor_t *m, desktop_t *d, node_t *n)
 
     PRINTF("remove node %X\n", n->client->window);
 
+    bool focused = (n == mon->desk->focus);
     unlink_node(m, d, n);
     history_remove(d, n);
     remove_stack_node(n);
@@ -834,7 +893,7 @@ void remove_node(monitor_t *m, desktop_t *d, node_t *n)
     num_clients--;
     ewmh_update_client_list();
 
-    if (mon->desk == d)
+    if (focused)
         update_current();
 }
 
@@ -844,8 +903,10 @@ void destroy_tree(node_t *n)
         return;
     node_t *first_tree = n->first_child;
     node_t *second_tree = n->second_child;
-    if (n->client != NULL)
+    if (n->client != NULL) {
         free(n->client);
+        num_clients--;
+    }
     free(n);
     destroy_tree(first_tree);
     destroy_tree(second_tree);
@@ -856,7 +917,7 @@ bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop
     if (n1 == NULL || n2 == NULL || n1 == n2 || (d1 != d2 && (n1->client->sticky || n2->client->sticky)))
         return false;
 
-    PRINTF("swap nodes %X %X", n1->client->window, n2->client->window);
+    PRINTF("swap nodes %X %X\n", n1->client->window, n2->client->window);
 
     node_t *pn1 = n1->parent;
     node_t *pn2 = n2->parent;
@@ -864,6 +925,8 @@ bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop
     bool n2_first_child = is_first_child(n2);
     int br1 = n1->birth_rotation;
     int br2 = n2->birth_rotation;
+    int pl1 = n1->privacy_level;
+    int pl2 = n2->privacy_level;
 
     if (pn1 != NULL) {
         if (n1_first_child)
@@ -883,12 +946,19 @@ bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop
     n2->parent = pn1;
     n1->birth_rotation = br2;
     n2->birth_rotation = br1;
+    n1->privacy_level = pl2;
+    n2->privacy_level = pl1;
 
     if (n1->vacant != n2->vacant) {
         update_vacant_state(n1->parent);
         update_vacant_state(n2->parent);
     }
 
+    if (n1->client->private != n2->client->private) {
+        n1->client->private = !n1->client->private;
+        n2->client->private = !n2->client->private;
+    }
+
     if (d1 != d2) {
         if (d1->root == n1)
             d1->root = n2;
@@ -900,8 +970,8 @@ bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop
             d2->focus = n1;
 
         if (m1 != m2) {
-            fit_monitor(m1, n2->client);
-            fit_monitor(m2, n1->client);
+            translate_client(m2, m1, n2->client);
+            translate_client(m1, m2, n1->client);
         }
 
         ewmh_set_wm_desktop(n1, d2);
@@ -916,9 +986,6 @@ bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop
             window_show(n2->client->window);
         }
 
-        tag_node(m1, d1, n2, d2, n2->client->tags_field);
-        tag_node(m2, d2, n1, d1, n1->client->tags_field);
-
         update_input_focus();
     }
 
@@ -942,7 +1009,7 @@ bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desk
     insert_node(md, dd, ns, nd);
 
     if (md != ms)
-        fit_monitor(md, ns->client);
+        translate_client(ms, md, ns->client);
 
     if (ds != dd) {
         ewmh_set_wm_desktop(ns, dd);
@@ -952,16 +1019,18 @@ bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desk
             else if (ds != ms->desk && dd == md->desk)
                 window_show(ns->client->window);
         }
+        if (ns->client->fullscreen && dd->focus != ns)
+            set_fullscreen(ns, false);
     }
 
     history_transfer_node(md, dd, ns);
-    stack_under(ns);
+    stack(ns, STACK_BELOW);
 
     if (ds == dd) {
         if (focused)
             focus_node(md, dd, ns);
         else if (active)
-            pseudo_focus(dd, ns);
+            pseudo_focus(md, dd, ns);
     } else {
         if (focused)
             update_current();
@@ -969,7 +1038,6 @@ bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desk
             update_input_focus();
     }
 
-    tag_node(md, dd, ns, ds, ns->client->tags_field);
     arrange(ms, ds);
     if (ds != dd)
         arrange(md, dd);
@@ -977,7 +1045,7 @@ bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desk
     return true;
 }
 
-node_t *closest_node(desktop_t *d, node_t *n, cycle_dir_t dir, client_select_t sel)
+node_t *closest_node(monitor_t *m, desktop_t *d, node_t *n, cycle_dir_t dir, client_select_t sel)
 {
     if (n == NULL)
         return NULL;
@@ -986,8 +1054,10 @@ node_t *closest_node(desktop_t *d, node_t *n, cycle_dir_t dir, client_select_t s
     if (f == NULL)
         f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root));
 
+    coordinates_t ref = {m, d, n};
     while (f != n) {
-        if (node_matches(n, f, sel))
+        coordinates_t loc = {m, d, f};
+        if (node_matches(&loc, &ref, sel))
             return f;
         f = (dir == CYCLE_PREV ? prev_leaf(f, d->root) : next_leaf(f, d->root));
         if (f == NULL)
@@ -1002,18 +1072,11 @@ void circulate_leaves(monitor_t *m, desktop_t *d, circulate_dir_t dir)
         return;
     node_t *p = d->focus->parent;
     bool focus_first_child = is_first_child(d->focus);
-    node_t *head, *tail;
-    for (head = first_extrema(d->root); head != NULL && !is_visible(d, head); head = next_leaf(head, d->root))
-        ;
-    for (tail = second_extrema(d->root); tail != NULL && !is_visible(d, tail); tail = prev_leaf(tail, d->root))
-        ;
-    if (head == tail)
-        return;
     if (dir == CIRCULATE_FORWARD)
-        for (node_t *s = tail, *f = prev_visible_leaf(d, s, d->root); f != NULL; s = prev_visible_leaf(d, f, d->root), f = prev_visible_leaf(d, s, d->root))
+        for (node_t *s = second_extrema(d->root), *f = prev_tiled_leaf(d, s, d->root); f != NULL; s = prev_tiled_leaf(d, f, d->root), f = prev_tiled_leaf(d, s, d->root))
             swap_nodes(m, d, f, m, d, s);
     else
-        for (node_t *f = head, *s = next_visible_leaf(d, f, d->root); s != NULL; f = next_visible_leaf(d, s, d->root), s = next_visible_leaf(d, f, d->root))
+        for (node_t *f = first_extrema(d->root), *s = next_tiled_leaf(d, f, d->root); s != NULL; f = next_tiled_leaf(d, s, d->root), s = next_tiled_leaf(d, f, d->root))
             swap_nodes(m, d, f, m, d, s);
     if (focus_first_child)
         focus_node(m, d, p->first_child);
@@ -1036,3 +1099,10 @@ void update_vacant_state(node_t *n)
         p = p->parent;
     }
 }
+
+void update_privacy_level(node_t *n, bool value)
+{
+    int v = (value ? 1 : -1);
+    for (node_t *p = n; p != NULL; p = p->parent)
+        p->privacy_level += v;
+}