]> git.lizzy.rs Git - bspwm.git/blobdiff - tree.c
New setting: `history_aware_focus`
[bspwm.git] / tree.c
diff --git a/tree.c b/tree.c
index 8fb6ec5790f44921f64d984b0d7e2a91f294ad5a..a6041a92546b11bad8603093d7ad3c33353c64e2 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -2,6 +2,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
+#include <limits.h>
 #include <math.h>
 #include <float.h>
 #include <xcb/xcb.h>
@@ -23,7 +24,7 @@ bool is_tiled(client_t *c)
 {
     if (c == NULL)
         return false;
-    return (!c->floating && !c->transient && !c->fullscreen);
+    return (!c->floating && !c->fullscreen);
 }
 
 bool is_floating(client_t *c)
@@ -48,6 +49,14 @@ void change_split_ratio(node_t *n, value_change_t chg)
     n->split_ratio = pow(n->split_ratio, (chg == CHANGE_INCREASE ? INC_EXP : DEC_EXP));
 }
 
+void change_layout(monitor_t *m, desktop_t *d, layout_t l)
+{
+    d->layout = l;
+    arrange(m, d);
+    if (d == mon->desk)
+        put_status();
+}
+
 node_t *first_extrema(node_t *n)
 {
     if (n == NULL)
@@ -92,6 +101,19 @@ node_t *prev_leaf(node_t *n, node_t *r)
     return second_extrema(p->parent->first_child);
 }
 
+bool is_adjacent(node_t *a, node_t *r)
+{
+    node_t *f = r->parent;
+    node_t *p = a;
+    bool first_child = is_first_child(r);
+    while (p != r) {
+        if (p->parent->split_type == f->split_type && is_first_child(p) == first_child)
+            return false;
+        p = p->parent;
+    }
+    return true;
+}
+
 node_t *find_fence(node_t *n, direction_t dir)
 {
     node_t *p;
@@ -115,35 +137,51 @@ node_t *find_fence(node_t *n, direction_t dir)
 
 node_t *find_neighbor(node_t *n, direction_t dir)
 {
+    if (n == NULL)
+        return NULL;
+
     node_t *fence = find_fence(n, dir);
 
     if (fence == NULL)
         return NULL;
 
+    node_t *nearest = NULL;
+
     if (dir == DIR_UP || dir == DIR_LEFT)
-        return second_extrema(fence->first_child);
+        nearest = second_extrema(fence->first_child);
     else if (dir == DIR_DOWN || dir == DIR_RIGHT)
-        return first_extrema(fence->second_child);
+        nearest = first_extrema(fence->second_child);
 
-    return NULL;
+    return nearest;
 }
 
-void get_opposite(direction_t src, direction_t* dst)
+node_t *nearest_from_history(focus_history_t *f, node_t *n, direction_t dir)
 {
-    switch (src) {
-        case DIR_RIGHT:
-            *dst = DIR_LEFT;
-            break;
-        case DIR_DOWN:
-            *dst = DIR_UP;
-            break;
-        case DIR_LEFT:
-            *dst = DIR_RIGHT;
-            break;
-        case DIR_UP:
-            *dst = DIR_DOWN;
-            break;
+    if (n == NULL || !is_tiled(n->client))
+        return NULL;
+
+    node_t *target = find_fence(n, dir);
+    if (target == NULL)
+        return NULL;
+    if (dir == DIR_UP || dir == DIR_LEFT)
+        target = target->first_child;
+    else if (dir == DIR_DOWN || dir == DIR_RIGHT)
+        target = target->second_child;
+
+    node_t *nearest = NULL;
+    int min_rank = INT_MAX;
+
+    for (node_t *a = first_extrema(target); a != NULL; a = next_leaf(a, target)) {
+        if (!is_tiled(a->client) || !is_adjacent(a, target) || a == n)
+            continue;
+        int rank = history_rank(f, a);
+        if (rank >= 0 && rank < min_rank) {
+            nearest = a;
+            min_rank = rank;
+        }
     }
+
+    return nearest;
 }
 
 node_t *nearest_neighbor(desktop_t *d, node_t *n, direction_t dir)
@@ -152,6 +190,7 @@ node_t *nearest_neighbor(desktop_t *d, node_t *n, direction_t dir)
         return NULL;
 
     node_t *target = NULL;
+
     if (is_tiled(n->client)) {
         target = find_fence(n, dir);
         if (target == NULL)
@@ -163,6 +202,7 @@ node_t *nearest_neighbor(desktop_t *d, node_t *n, direction_t dir)
     } else {
         target = d->root;
     }
+
     node_t *nearest = NULL;
     direction_t dir2;
     xcb_point_t pt;
@@ -170,20 +210,41 @@ node_t *nearest_neighbor(desktop_t *d, node_t *n, direction_t dir)
     get_side_handle(n->client, dir, &pt);
     get_opposite(dir, &dir2);
     double ds = DBL_MAX;
+
     for (node_t *a = first_extrema(target); a != NULL; a = next_leaf(a, target)) {
-        if (is_tiled(a->client) != is_tiled(n->client) || a == n)
+        if (is_tiled(a->client) != is_tiled(n->client)
+                || (is_tiled(a->client) && !is_adjacent(a, target))
+                || a == n)
             continue;
         get_side_handle(a->client, dir2, &pt2);
         double ds2 = distance(pt, pt2);
-        PRINTF("distance %X %g\n", a->client->window, ds2);
         if (ds2 < ds) {
             ds = ds2;
             nearest = a;
         }
     }
+
     return nearest;
 }
 
+void get_opposite(direction_t src, direction_t *dst)
+{
+    switch (src) {
+        case DIR_RIGHT:
+            *dst = DIR_LEFT;
+            break;
+        case DIR_DOWN:
+            *dst = DIR_UP;
+            break;
+        case DIR_LEFT:
+            *dst = DIR_RIGHT;
+            break;
+        case DIR_UP:
+            *dst = DIR_DOWN;
+            break;
+    }
+}
+
 int tiled_area(node_t *n)
 {
     if (n == NULL)
@@ -230,7 +291,7 @@ void move_fence(node_t *n, direction_t dir, fence_move_t mov)
 
 void rotate_tree(node_t *n, rotate_t rot)
 {
-    if (n == NULL || is_leaf(n))
+    if (n == NULL || is_leaf(n) || rot == ROTATE_IDENTITY)
         return;
 
     node_t *tmp;
@@ -363,8 +424,6 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, x
     n->rectangle = rect;
 
     if (is_leaf(n)) {
-        if (n->client->fullscreen)
-            return;
 
         if (is_floating(n->client) && n->client->border_width != border_width) {
             int ds = 2 * (border_width - n->client->border_width);
@@ -372,24 +431,32 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, x
             n->client->floating_rectangle.height += ds;
         }
 
-        if (borderless_monocle && is_tiled(n->client) && d->layout == LAYOUT_MONOCLE)
+        if ((borderless_monocle && is_tiled(n->client) && d->layout == LAYOUT_MONOCLE) ||
+                n->client->fullscreen)
             n->client->border_width = 0;
         else
             n->client->border_width = border_width;
 
         xcb_rectangle_t r;
-        if (is_tiled(n->client)) {
-            if (d->layout == LAYOUT_TILED)
-                r = rect;
-            else if (d->layout == LAYOUT_MONOCLE)
-                r = root_rect;
-            int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : 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;
+        if (!n->client->fullscreen) {
+            if (!n->client->floating) {
+                /* tiled clients */
+                if (d->layout == LAYOUT_TILED)
+                    r = rect;
+                else if (d->layout == LAYOUT_MONOCLE)
+                    r = root_rect;
+                int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : 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 */
+                r = n->client->floating_rectangle;
+            }
         } else {
-            r = n->client->floating_rectangle;
+            /* fullscreen clients */
+            r = m->rectangle;
         }
 
         window_move_resize(n->client->window, r.x, r.y, r.width, r.height);
@@ -472,7 +539,8 @@ void insert_node(monitor_t *m, desktop_t *d, node_t *n)
                         dad->second_child = n;
                         rot = ROTATE_COUNTER_CLOCKWISE;
                     }
-                    rotate_tree(fopar, rot);
+                    if (!is_floating(n->client))
+                        rotate_tree(fopar, rot);
                     n->birth_rotation = rot;
                 }
                 break;
@@ -533,6 +601,8 @@ void focus_node(monitor_t *m, desktop_t *d, node_t *n)
     if (n == NULL && d->root != NULL)
         return;
 
+    split_mode = MODE_AUTOMATIC;
+
     if (mon->desk != d)
         clear_input_focus();
 
@@ -560,18 +630,12 @@ void focus_node(monitor_t *m, desktop_t *d, node_t *n)
 
     PRINTF("focus node %X\n", n->client->window);
 
-    split_mode = MODE_AUTOMATIC;
     n->client->urgent = false;
 
     pseudo_focus(d, n);
-    xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, n->client->window, XCB_CURRENT_TIME);
+    stack(d, n);
 
-    if (!is_tiled(n->client)) {
-        if (!adaptative_raise || !might_cover(d, n))
-            window_raise(n->client->window);
-    } else {
-        stack_tiled(d);
-    }
+    xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, n->client->window, XCB_CURRENT_TIME);
 
     if (focus_follows_pointer) {
         xcb_window_t win = XCB_NONE;
@@ -639,7 +703,7 @@ void unlink_node(desktop_t *d, node_t *n)
 
 void remove_node(desktop_t *d, node_t *n)
 {
-    if (d == NULL || n == NULL)
+    if (n == NULL)
         return;
 
     PRINTF("remove node %X\n", n->client->window);
@@ -726,7 +790,7 @@ void swap_nodes(node_t *n1, node_t *n2)
 
 void transfer_node(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd, node_t *n)
 {
-    if (n == NULL || (ms == md && dd == ds))
+    if (n == NULL || dd == ds)
         return;
 
     PRINTF("transfer node %X\n", n->client->window);
@@ -743,14 +807,17 @@ void transfer_node(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd, n
 
     fit_monitor(md, n->client);
 
-    if (n->client->fullscreen)
-        window_move_resize(n->client->window, md->rectangle.x, md->rectangle.y, md->rectangle.width, md->rectangle.height);
-
     if (ds != ms->desk && dd == md->desk)
         window_show(n->client->window);
 
     pseudo_focus(dd, n);
 
+    if (md->desk == dd)
+        stack(dd, n);
+
+    arrange(ms, ds);
+    arrange(md, dd);
+
     if (ds == ms->desk || dd == md->desk)
         update_current();
 }
@@ -765,6 +832,9 @@ void select_monitor(monitor_t *m)
     last_mon = mon;
     mon = m;
 
+    if (pointer_follows_monitor)
+        center_pointer(m);
+
     ewmh_update_current_desktop();
     put_status();
 }
@@ -778,10 +848,8 @@ void select_desktop(monitor_t *m, desktop_t *d)
 
     PRINTF("select desktop %s\n", d->name);
 
-    if (visible) {
-        desktop_show(d);
-        desktop_hide(mon->desk);
-    }
+    desktop_show(d);
+    desktop_hide(mon->desk);
 
     mon->last_desk = mon->desk;
     mon->desk = d;
@@ -933,7 +1001,8 @@ void put_status(void)
         }
     }
     if (mon != NULL && mon->desk != NULL)
-        fprintf(status_fifo, "L%s\n", (mon->desk->layout == LAYOUT_TILED ? "tiled" : "monocle"));
+        fprintf(status_fifo, "L%s", (mon->desk->layout == LAYOUT_TILED ? "tiled" : "monocle"));
+    fprintf(status_fifo, "\n");
     fflush(status_fifo);
 }