]> git.lizzy.rs Git - bspwm.git/blobdiff - tree.c
Implement ICCCM's WM_TAKE_FOCUS behavior
[bspwm.git] / tree.c
diff --git a/tree.c b/tree.c
index 8038aafc42bf371a5b35063276c72f8a05031039..dbfe18dce71ca5546768dffe29e0811f99f4bb6e 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -39,6 +39,69 @@ bool is_second_child(node_t *n)
     return (n != NULL && n->parent != NULL && n->parent->second_child == n);
 }
 
+/**
+ * Check if the specified node matches the selection criteria.
+ *
+ * Arguments:
+ *  node_t *c           - the active node
+ *  node_t *t           - the node to test
+ *  client_sel_t sel    - the selection criteria
+ *
+ * Returns true if the node matches.
+ **/
+bool node_matches(node_t *c, node_t *t, client_select_t sel)
+{
+    if (sel.type != CLIENT_TYPE_ALL &&
+            is_tiled(t->client)
+            ? sel.type == CLIENT_TYPE_FLOATING
+            : sel.type == CLIENT_TYPE_TILED
+       ) return false;
+
+    if (sel.class != CLIENT_CLASS_ALL &&
+            streq(c->client->class_name, t->client->class_name)
+            ? sel.class == CLIENT_CLASS_DIFFER
+            : sel.class == CLIENT_CLASS_EQUAL
+       ) return false;
+
+    if (sel.mode != CLIENT_MODE_ALL &&
+            t->split_mode == MODE_MANUAL
+            ? sel.mode == CLIENT_MODE_AUTOMATIC
+            : sel.mode == CLIENT_MODE_MANUAL)
+        return false;
+
+    if (sel.urgency != CLIENT_URGENCY_ALL &&
+            t->client->urgent
+            ? sel.urgency == CLIENT_URGENCY_OFF
+            : sel.urgency == CLIENT_URGENCY_ON
+       ) return false;
+
+    return true;
+}
+
+bool desktop_matches(desktop_t *t, desktop_select_t sel) {
+    if (sel.status != DESKTOP_STATUS_ALL &&
+            t->root == NULL
+            ? sel.status == DESKTOP_STATUS_OCCUPIED
+            : sel.status == DESKTOP_STATUS_FREE
+       ) return false;
+
+    if (sel.urgency != DESKTOP_URGENCY_ALL &&
+            is_urgent(t)
+            ? sel.urgency == DESKTOP_URGENCY_OFF
+            : sel.urgency == DESKTOP_URGENCY_ON
+       ) return false;
+
+    return true;
+}
+
+bool is_urgent(desktop_t *d)
+{
+    for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
+        if (n->client->urgent)
+            return true;
+    return false;
+}
+
 void change_split_ratio(node_t *n, value_change_t chg)
 {
     n->split_ratio = pow(n->split_ratio,
@@ -66,6 +129,16 @@ void reset_mode(coordinates_t *loc)
     }
 }
 
+node_t *brother_tree(node_t *n)
+{
+    if (n == NULL || n->parent == NULL)
+        return NULL;
+    if (is_first_child(n))
+        return n->parent->second_child;
+    else
+        return n->parent->first_child;
+}
+
 node_t *first_extrema(node_t *n)
 {
     if (n == NULL)
@@ -165,7 +238,7 @@ node_t *find_fence(node_t *n, direction_t dir)
 }
 
 
-node_t *nearest_neighbor(desktop_t *d, node_t *n, direction_t dir)
+node_t *nearest_neighbor(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)))
@@ -173,38 +246,13 @@ node_t *nearest_neighbor(desktop_t *d, node_t *n, direction_t dir)
 
     node_t *nearest = NULL;
     if (history_aware_focus)
-        nearest = nearest_from_history(d->history, n, dir);
-    if (nearest == NULL) {
-        if (focus_by_distance) {
-            nearest = nearest_from_distance(d, n, dir);
-        } else {
-            nearest = nearest_from_tree(n, dir);
-        }
-    }
+        nearest = nearest_from_history(d->history, n, dir, sel);
+    if (nearest == NULL)
+        nearest = nearest_from_distance(d, n, dir, sel);
     return nearest;
 }
 
-node_t *nearest_from_tree(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)
-        nearest = second_extrema(fence->first_child);
-    else if (dir == DIR_DOWN || dir == DIR_RIGHT)
-        nearest = first_extrema(fence->second_child);
-
-    return nearest;
-}
-
-node_t *nearest_from_history(focus_history_t *f, node_t *n, direction_t dir)
+node_t *nearest_from_history(focus_history_t *f, node_t *n, direction_t dir, client_select_t sel)
 {
     if (n == NULL || !is_tiled(n->client))
         return NULL;
@@ -223,6 +271,9 @@ node_t *nearest_from_history(focus_history_t *f, node_t *n, direction_t dir)
     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))
+            continue;
+
         int rank = history_rank(f, a);
         if (rank >= 0 && rank < min_rank) {
             nearest = a;
@@ -233,7 +284,7 @@ node_t *nearest_from_history(focus_history_t *f, node_t *n, direction_t dir)
     return nearest;
 }
 
-node_t *nearest_from_distance(desktop_t *d, node_t *n, direction_t dir)
+node_t *nearest_from_distance(desktop_t *d, node_t *n, direction_t dir, client_select_t sel)
 {
     if (n == NULL)
         return NULL;
@@ -261,10 +312,11 @@ node_t *nearest_from_distance(desktop_t *d, node_t *n, direction_t dir)
     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)
-                || (is_tiled(a->client) && !is_adjacent(n, a, dir))
-                || a == n)
-            continue;
+        if (a == n) continue;
+        if (!node_matches(n, a, sel)) continue;
+        if (is_tiled(a->client) != is_tiled(n->client)) continue;
+        if (is_tiled(a->client) && !is_adjacent(n, a, dir)) continue;
+
         get_side_handle(a->client, dir2, &pt2);
         double ds2 = distance(pt, pt2);
         if (ds2 < ds) {
@@ -302,7 +354,7 @@ int tiled_area(node_t *n)
     return rect.width * rect.height;
 }
 
-node_t *find_biggest(desktop_t *d)
+node_t *find_biggest(desktop_t *d, node_t *c, client_select_t sel)
 {
     if (d == NULL)
         return NULL;
@@ -311,7 +363,7 @@ node_t *find_biggest(desktop_t *d)
     int r_area = tiled_area(r);
 
     for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
-        if (!is_tiled(f->client))
+        if (!is_tiled(f->client) || !node_matches(c, f, sel))
             continue;
         int f_area = tiled_area(f);
         if (r == NULL) {
@@ -328,53 +380,46 @@ node_t *find_biggest(desktop_t *d)
 
 void move_fence(node_t *n, direction_t dir, fence_move_t mov)
 {
-    node_t *fence = find_fence(n, dir);
-
-    if (fence == NULL)
+    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(fence, CHANGE_INCREASE);
+        change_split_ratio(n, CHANGE_INCREASE);
     else
-        change_split_ratio(fence, CHANGE_DECREASE);
+        change_split_ratio(n, CHANGE_DECREASE);
 }
 
-void rotate_tree(node_t *n, int rot)
+void rotate_tree(node_t *n, int deg)
 {
-    if (n == NULL || is_leaf(n) || rot == 0)
+    if (n == NULL || is_leaf(n) || deg == 0)
         return;
 
     node_t *tmp;
 
-    if ((rot == 90 && n->split_type == TYPE_HORIZONTAL)
-            || (rot == 270 && n->split_type == TYPE_VERTICAL)
-            || rot == 180) {
+    if ((deg == 90 && n->split_type == TYPE_HORIZONTAL)
+            || (deg == 270 && n->split_type == TYPE_VERTICAL)
+            || deg == 180) {
         tmp = n->first_child;
         n->first_child = n->second_child;
         n->second_child = tmp;
         n->split_ratio = 1.0 - n->split_ratio;
     }
 
-    if (rot != 180) {
+    if (deg != 180) {
         if (n->split_type == TYPE_HORIZONTAL)
             n->split_type = TYPE_VERTICAL;
         else if (n->split_type == TYPE_VERTICAL)
             n->split_type = TYPE_HORIZONTAL;
     }
 
-    rotate_tree(n->first_child, rot);
-    rotate_tree(n->second_child, rot);
+    rotate_tree(n->first_child, deg);
+    rotate_tree(n->second_child, deg);
 }
 
 void rotate_brother(node_t *n)
 {
-    if (n == NULL || n->parent == NULL)
-        return;
-    if (is_first_child(n))
-        rotate_tree(n->parent->second_child, n->birth_rotation);
-    else
-        rotate_tree(n->parent->first_child, n->birth_rotation);
+    rotate_tree(brother_tree(n), n->birth_rotation);
 }
 
 void unrotate_tree(node_t *n, int rot)
@@ -386,12 +431,7 @@ void unrotate_tree(node_t *n, int rot)
 
 void unrotate_brother(node_t *n)
 {
-    if (n == NULL || n->parent == NULL)
-        return;
-    if (is_first_child(n))
-        unrotate_tree(n->parent->second_child, n->birth_rotation);
-    else
-        unrotate_tree(n->parent->first_child, n->birth_rotation);
+    unrotate_tree(brother_tree(n), n->birth_rotation);
 }
 
 void flip_tree(node_t *n, flip_t flp)
@@ -474,6 +514,8 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, x
                     r = rect;
                 else if (d->layout == LAYOUT_MONOCLE)
                     r = root_rect;
+                else
+                    return;
                 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);
@@ -762,18 +804,13 @@ void destroy_tree(node_t *n)
     destroy_tree(second_tree);
 }
 
-void swap_nodes(node_t *n1, node_t *n2, bool interpret)
+void swap_nodes(node_t *n1, node_t *n2)
 {
     if (n1 == NULL || n2 == NULL || n1 == n2)
         return;
 
     PUTS("swap nodes");
 
-    if (interpret && n2->split_mode == MODE_MANUAL) {
-        transplant_node(mon, mon->desk, n1, n2);
-        return;
-    }
-
     /* (n1 and n2 are leaves) */
     node_t *pn1 = n1->parent;
     node_t *pn2 = n2->parent;
@@ -860,8 +897,13 @@ void transfer_node(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd, n
 
 void transplant_node(monitor_t *m, desktop_t *d, node_t *n1, node_t *n2)
 {
+    if (n1 == n2)
+        return;
+    bool was_focused = (d->focus == n1);
     unlink_node(d, n1);
     insert_node(m, d, n1, n2);
+    if (was_focused)
+        pseudo_focus(d, n1);
 }
 
 void select_monitor(monitor_t *m)
@@ -881,7 +923,7 @@ void select_monitor(monitor_t *m)
     put_status();
 }
 
-monitor_t *nearest_monitor(monitor_t *m, direction_t dir)
+monitor_t *nearest_monitor(monitor_t *m, direction_t dir, desktop_select_t sel)
 {
     int dmin = INT_MAX;
     monitor_t *nearest = NULL;
@@ -889,6 +931,8 @@ monitor_t *nearest_monitor(monitor_t *m, direction_t dir)
     for (monitor_t *f = mon_head; f != NULL; f = f->next) {
         if (f == m)
             continue;
+        if (!desktop_matches(f->desk, sel))
+            continue;
         xcb_rectangle_t r = f->rectangle;
         if ((dir == DIR_LEFT && r.x < rect.x) ||
                 (dir == DIR_RIGHT && r.x >= (rect.x + rect.width)) ||
@@ -931,11 +975,8 @@ monitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, desktop_select_t sel)
         f = (dir == CYCLE_PREV ? mon_tail : mon_head);
 
     while (f != m) {
-        if (sel == DESKTOP_ALL
-                || (sel == DESKTOP_FREE && f->desk->root == NULL)
-                || (sel == DESKTOP_OCCUPIED && f->desk->root != NULL)) {
+        if (desktop_matches(f->desk, sel))
             return f;
-        }
         f = (dir == CYCLE_PREV ? m->prev : m->next);
         if (f == NULL)
             f = (dir == CYCLE_PREV ? mon_tail : mon_head);
@@ -951,11 +992,8 @@ desktop_t *closest_desktop(monitor_t *m, desktop_t *d, cycle_dir_t dir, desktop_
         f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head);
 
     while (f != d) {
-        if (sel == DESKTOP_ALL
-                || (sel == DESKTOP_FREE && f->root == NULL)
-                || (sel == DESKTOP_OCCUPIED && f->root != NULL)) {
+        if (desktop_matches(f, sel))
             return f;
-        }
         f = (dir == CYCLE_PREV ? f->prev : f->next);
         if (f == NULL)
             f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head);
@@ -974,17 +1012,8 @@ node_t *closest_node(desktop_t *d, node_t *n, cycle_dir_t dir, client_select_t s
         f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root));
 
     while (f != n) {
-        bool tiled = is_tiled(f->client);
-        if ((sel.type == CLIENT_TYPE_ALL
-                    || (tiled && sel.type == CLIENT_TYPE_TILED)
-                    || (!tiled && sel.type == CLIENT_TYPE_FLOATING)) &&
-                (sel.class == CLIENT_CLASS_ALL
-                 || (sel.class == CLIENT_CLASS_EQUAL
-                     && streq(f->client->class_name, n->client->class_name))
-                 || (sel.class == CLIENT_CLASS_DIFFER
-                     && !streq(f->client->class_name, n->client->class_name)))) {
+        if (node_matches(n, f, sel))
             return f;
-        }
         f = (dir == CYCLE_PREV ? prev_leaf(f, d->root) : next_leaf(f, d->root));
         if (f == NULL)
             f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root));
@@ -1000,10 +1029,10 @@ void circulate_leaves(monitor_t *m, desktop_t *d, circulate_dir_t dir)
     bool focus_first_child = is_first_child(d->focus);
     if (dir == CIRCULATE_FORWARD)
         for (node_t *s = second_extrema(d->root), *f = prev_leaf(s, d->root); f != NULL; s = prev_leaf(f, d->root), f = prev_leaf(s, d->root))
-            swap_nodes(f, s, false);
+            swap_nodes(f, s);
     else
         for (node_t *f = first_extrema(d->root), *s = next_leaf(f, d->root); s != NULL; f = next_leaf(s, d->root), s = next_leaf(f, d->root))
-            swap_nodes(f, s, false);
+            swap_nodes(f, s);
     if (focus_first_child)
         focus_node(m, d, p->first_child);
     else