]> 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 a453d59285824819270f20cfc1126fc4502a3ea8..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 dir, direction_t* val)
+node_t *nearest_from_history(focus_history_t *f, node_t *n, direction_t dir)
 {
-    switch (dir) {
-        case DIR_RIGHT:
-            *val = DIR_LEFT;
-            break;
-        case DIR_DOWN:
-            *val = DIR_UP;
-            break;
-        case DIR_LEFT:
-            *val = DIR_RIGHT;
-            break;
-        case DIR_UP:
-            *val = 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)
@@ -192,7 +253,7 @@ int tiled_area(node_t *n)
     return rect.width * rect.height;
 }
 
-node_t *find_by_area(desktop_t *d, swap_arg_t a)
+node_t *find_biggest(desktop_t *d)
 {
     if (d == NULL)
         return NULL;
@@ -205,8 +266,7 @@ node_t *find_by_area(desktop_t *d, swap_arg_t a)
         if (r == NULL) {
             r = f;
             r_area = f_area;
-        } else if ((a == SWAP_BIGGEST && f_area > r_area)
-                || (a == SWAP_SMALLEST && f_area < r_area)) {
+        } else if (f_area > r_area) {
             r = f;
             r_area = f_area;
         }
@@ -231,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;
@@ -342,6 +402,9 @@ int balance_tree(node_t *n)
 
 void arrange(monitor_t *m, desktop_t *d)
 {
+    if (d->root == NULL)
+        return;
+
     PRINTF("arrange %s%s%s\n", (num_monitors > 1 ? m->name : ""), (num_monitors > 1 ? " " : ""), d->name);
 
     xcb_rectangle_t rect = m->rectangle;
@@ -361,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);
@@ -370,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);
@@ -470,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;
@@ -518,8 +588,24 @@ void insert_node(monitor_t *m, desktop_t *d, node_t *n)
     put_status();
 }
 
+void pseudo_focus(desktop_t *d, node_t *n)
+{
+    if (d->focus == n)
+        return;
+    d->focus = n;
+    history_add(d->history, n);
+}
+
 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();
+
     if (mon != m) {
         for (desktop_t *cd = mon->desk_head; cd != NULL; cd = cd->next)
             window_draw_border(cd->focus, true, false);
@@ -544,22 +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;
 
-    xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, n->client->window, XCB_CURRENT_TIME);
-
-    if (d->focus != n) {
-        d->focus = n;
-        history_add(d->history, n);
-    }
+    pseudo_focus(d, n);
+    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;
@@ -627,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);
@@ -714,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 || ds == NULL || dd == NULL || ms == NULL || md == NULL || (ms == md && dd == ds))
+    if (n == NULL || dd == ds)
         return;
 
     PRINTF("transfer node %X\n", n->client->window);
@@ -731,14 +807,16 @@ 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);
 
-    dd->focus = n;
-    history_add(dd->history, n);
+    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();
@@ -754,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();
 }
@@ -767,23 +848,8 @@ void select_desktop(monitor_t *m, desktop_t *d)
 
     PRINTF("select desktop %s\n", d->name);
 
-    clear_input_focus();
-
-    if (visible) {
-        node_t *n = first_extrema(d->root);
-
-        while (n != NULL) {
-            window_show(n->client->window);
-            n = next_leaf(n, d->root);
-        }
-
-        n = first_extrema(mon->desk->root);
-
-        while (n != NULL) {
-            window_hide(n->client->window);
-            n = next_leaf(n, mon->desk->root);
-        }
-    }
+    desktop_show(d);
+    desktop_hide(mon->desk);
 
     mon->last_desk = mon->desk;
     mon->desk = d;
@@ -934,7 +1000,9 @@ void put_status(void)
             fprintf(status_fifo, "%c%s:", m->desk == d ? (urgent ? 'U' : 'D') : (d->root == NULL ? 'E' : (urgent ? 'u' : 'd')), d->name);
         }
     }
-    fprintf(status_fifo, "L%s\n", (mon->desk->layout == LAYOUT_TILED ? "tiled" : "monocle"));
+    if (mon != NULL && mon->desk != NULL)
+        fprintf(status_fifo, "L%s", (mon->desk->layout == LAYOUT_TILED ? "tiled" : "monocle"));
+    fprintf(status_fifo, "\n");
     fflush(status_fifo);
 }
 
@@ -1013,6 +1081,8 @@ void restore_layout(char *file_path)
         return;
     }
 
+    PUTS("restore layout");
+
     char line[MAXLEN];
     monitor_t *m = NULL;
     desktop_t *d = NULL;
@@ -1146,6 +1216,8 @@ void restore_history(char *file_path)
         return;
     }
 
+    PUTS("restore history");
+
     char line[MAXLEN];
     desktop_t *d = NULL;
     unsigned int level;
@@ -1157,7 +1229,6 @@ void restore_history(char *file_path)
         level = 0;
         while (level < strlen(line) && isspace(line[level]))
             level++;
-        PRINTF("level %u: %s\n", level, line + level);
         if (level == 0) {
             desktop_location_t loc;
             if (locate_desktop(line + level, &loc))