]> git.lizzy.rs Git - bspwm.git/blobdiff - tree.c
Implement sticky windows
[bspwm.git] / tree.c
diff --git a/tree.c b/tree.c
index f15941fccd367100c64422b11622f951f6483b94..680179d428c30d11193f45f42996cb096c16f20c 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -1,23 +1,23 @@
-#include <string.h>
-#include <math.h>
-#include <limits.h>
 #include <float.h>
-#include "settings.h"
-#include "window.h"
+#include <limits.h>
 #include "bspwm.h"
-#include "ewmh.h"
-#include "tree.h"
 #include "desktop.h"
-#include "monitor.h"
+#include "ewmh.h"
 #include "history.h"
+#include "monitor.h"
 #include "query.h"
+#include "settings.h"
+#include "stack.h"
+#include "window.h"
+#include <math.h>
+#include "tree.h"
 
 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);
+    PRINTF("arrange %s %s\n", m->name, d->name);
 
     xcb_rectangle_t rect = m->rectangle;
     int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : d->window_gap);
@@ -37,8 +37,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 != border_width) {
-            int ds = 2 * (border_width - n->client->border_width);
+        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;
         }
@@ -47,7 +47,7 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, x
                 n->client->fullscreen)
             n->client->border_width = 0;
         else
-            n->client->border_width = border_width;
+            n->client->border_width = d->border_width;
 
         xcb_rectangle_t r;
         if (!n->client->fullscreen) {
@@ -116,6 +116,7 @@ void insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f)
 
     if (f == NULL) {
         d->root = n;
+        d->focus = n;
     } else {
         node_t *c = make_node();
         node_t *p = f->parent;
@@ -202,16 +203,17 @@ void insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f)
         if (f->vacant)
             update_vacant_state(p);
     }
+    if (n->client->sticky)
+        d->num_sticky++;
     put_status();
 }
 
 void pseudo_focus(desktop_t *d, node_t *n)
 {
-    if (n == NULL || d->focus == n)
+    if (n == NULL)
         return;
     d->focus = n;
-    history_add(d->history, n);
-    stack(d, n);
+    stack(n);
 }
 
 void focus_node(monitor_t *m, desktop_t *d, node_t *n)
@@ -219,9 +221,28 @@ void focus_node(monitor_t *m, desktop_t *d, node_t *n)
     if (n == NULL && d->root != NULL)
         return;
 
-    if (mon->desk != d)
+    if (mon->desk != d || n == NULL)
         clear_input_focus();
 
+    if (mon->desk->num_sticky > 0 && d != mon->desk) {
+        node_t *a = first_extrema(mon->desk->root);
+        sticky_still = false;
+        while (a != NULL) {
+            node_t *b = next_leaf(a, mon->desk->root);
+            if (a->client->sticky)
+                transfer_node(mon, mon->desk, a, m, d, d->focus);
+            a = b;
+        }
+        sticky_still = true;
+        if (n == NULL)
+            n = d->focus;
+    }
+
+    if (n != NULL && d->focus != NULL && n != d->focus && d->focus->client->fullscreen) {
+        set_fullscreen(d->focus, false);
+        arrange(m, d);
+    }
+
     if (mon != m) {
         for (desktop_t *cd = mon->desk_head; cd != NULL; cd = cd->next)
             window_draw_border(cd->focus, true, false);
@@ -240,6 +261,7 @@ void focus_node(monitor_t *m, desktop_t *d, node_t *n)
     select_desktop(m, d);
 
     if (n == NULL) {
+        history_add(m, d, NULL);
         ewmh_update_active_window();
         return;
     }
@@ -249,6 +271,7 @@ void focus_node(monitor_t *m, desktop_t *d, node_t *n)
     n->client->urgent = false;
 
     pseudo_focus(d, n);
+    history_add(m, d, n);
     set_input_focus(n);
 
     if (focus_follows_pointer) {
@@ -284,10 +307,10 @@ node_t *make_node(void)
 client_t *make_client(xcb_window_t win)
 {
     client_t *c = malloc(sizeof(client_t));
-    strncpy(c->class_name, MISSING_VALUE, sizeof(c->class_name));
-    c->border_width = border_width;
+    snprintf(c->class_name, sizeof(c->class_name), "%s", MISSING_VALUE);
+    c->border_width = BORDER_WIDTH;
     c->window = win;
-    c->floating = c->transient = c->fullscreen = c->locked = c->urgent = false;
+    c->floating = c->transient = c->fullscreen = c->locked = c->sticky = c->urgent = false;
     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) {
@@ -330,7 +353,7 @@ bool is_second_child(node_t *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));
+            (chg == CHANGE_INCREASE ? (1 / growth_factor) : growth_factor));
 }
 
 void reset_mode(coordinates_t *loc)
@@ -463,13 +486,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->history, n, dir, sel);
+        nearest = nearest_from_history(d, n, dir, sel);
     if (nearest == NULL)
         nearest = nearest_from_distance(d, n, dir, sel);
     return nearest;
 }
 
-node_t *nearest_from_history(focus_history_t *f, node_t *n, direction_t dir, client_select_t sel)
+node_t *nearest_from_history(desktop_t *d, node_t *n, direction_t dir, client_select_t sel)
 {
     if (n == NULL || !is_tiled(n->client))
         return NULL;
@@ -491,7 +514,7 @@ node_t *nearest_from_history(focus_history_t *f, node_t *n, direction_t dir, cli
         if (!node_matches(n, a, sel))
             continue;
 
-        int rank = history_rank(f, a);
+        int rank = history_rank(d, a);
         if (rank >= 0 && rank < min_rank) {
             nearest = a;
             min_rank = rank;
@@ -724,11 +747,16 @@ void unlink_node(desktop_t *d, node_t *n)
         n->parent = NULL;
         free(p);
 
-        if (n == d->focus)
-            d->focus = history_get(d->history, 1);
+        if (n == d->focus) {
+            d->focus = history_get_node(d, n);
+            if (d->focus == NULL && d->root != NULL)
+                d->focus = first_extrema(d->root);
+        }
 
         update_vacant_state(b->parent);
     }
+    if (n->client->sticky)
+        d->num_sticky--;
     put_status();
 }
 
@@ -740,7 +768,8 @@ void remove_node(desktop_t *d, node_t *n)
     PRINTF("remove node %X\n", n->client->window);
 
     unlink_node(d, n);
-    history_remove(d->history, n);
+    history_remove(d, n);
+    remove_stack_node(n);
     free(n->client);
     free(n);
 
@@ -764,14 +793,13 @@ void destroy_tree(node_t *n)
     destroy_tree(second_tree);
 }
 
-void swap_nodes(node_t *n1, node_t *n2)
+void swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2)
 {
-    if (n1 == NULL || n2 == NULL || n1 == n2)
+    if (n1 == NULL || n2 == NULL || n1 == n2 || (d1 != d2 && (n1->client->sticky || n2->client->sticky)))
         return;
 
-    PUTS("swap nodes");
+    PRINTF("swap nodes %X %X", n1->client->window, n2->client->window);
 
-    /* (n1 and n2 are leaves) */
     node_t *pn1 = n1->parent;
     node_t *pn2 = n2->parent;
     bool n1_first_child = is_first_child(n1);
@@ -803,64 +831,81 @@ void swap_nodes(node_t *n1, node_t *n2)
         update_vacant_state(n2->parent);
     }
 
-    /* If we ever need to generalize: */
-    /* if (d1 != d2) { */
-    /*     if (d1->root == n1) */
-    /*         d1->root = n2; */
-    /*     if (d1->focus == n1) */
-    /*         d1->focus = n2; */
-    /*     if (d1->last_focus == n1) */
-    /*         d1->last_focus = n2; */
-    /*     if (d2->root == n2) */
-    /*         d2->root = n1; */
-    /*     if (d2->focus == n2) */
-    /*         d2->focus = n1; */
-    /*     if (d2->last_focus == n2) */
-    /*         d2->last_focus = n1; */
-    /* } */
-}
-
-void transfer_node(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd, node_t *n)
-{
-    if (n == NULL || dd == ds)
-        return;
+    if (d1 != d2) {
+        if (d1->root == n1)
+            d1->root = n2;
+        if (d1->focus == n1)
+            d1->focus = n2;
+        if (d2->root == n2)
+            d2->root = n1;
+        if (d2->focus == n2)
+            d2->focus = n1;
+
+        if (m1 != m2) {
+            fit_monitor(m1, n2->client);
+            fit_monitor(m2, n1->client);
+        }
 
-    PRINTF("transfer node %X\n", n->client->window);
+        ewmh_set_wm_desktop(n1, d2);
+        ewmh_set_wm_desktop(n2, d1);
+        history_swap_nodes(m1, d1, n1, m2, d2, n2);
 
-    unlink_node(ds, n);
-    history_remove(ds->history, n);
-    insert_node(md, dd, n, dd->focus);
-    ewmh_set_wm_desktop(n, dd);
+        if (m1->desk != d1 && m2->desk == d2) {
+            window_show(n1->client->window);
+            window_hide(n2->client->window);
+        } else if (m1->desk == d1 && m2->desk != d2) {
+            window_hide(n1->client->window);
+            window_show(n2->client->window);
+        }
 
-    if (ds == ms->desk && dd != md->desk) {
-        if (n == ds->focus)
-            clear_input_focus();
-        window_hide(n->client->window);
+        update_input_focus();
     }
+}
 
-    fit_monitor(md, n->client);
+void transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desktop_t *dd, node_t *nd)
+{
+    if (ns == NULL || ns == nd || (sticky_still && ns->client->sticky))
+        return;
 
-    if (ds != ms->desk && dd == md->desk)
-        window_show(n->client->window);
+    PRINTF("transfer node %X\n", ns->client->window);
 
-    pseudo_focus(dd, n);
+    bool focused = (ns == mon->desk->focus);
+    bool active = (ns == ds->focus);
 
-    arrange(ms, ds);
-    arrange(md, dd);
+    if (focused)
+        clear_input_focus();
 
-    if (ds == ms->desk || dd == md->desk)
-        update_current();
-}
+    unlink_node(ds, ns);
+    insert_node(md, dd, ns, nd);
 
-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);
+    if (md != ms)
+        fit_monitor(md, ns->client);
+
+    if (ds != dd) {
+        ewmh_set_wm_desktop(ns, dd);
+        if (!ns->client->sticky) {
+            if (ds == ms->desk && dd != md->desk)
+                window_hide(ns->client->window);
+            else if (ds != ms->desk && dd == md->desk)
+                window_show(ns->client->window);
+        }
+    }
+
+    history_transfer_node(md, dd, ns);
+    stack_under(ns);
+
+    if (ds == dd) {
+        if (focused)
+            focus_node(md, dd, ns);
+        else if (active)
+            pseudo_focus(dd, ns);
+    } else {
+        if (focused)
+            update_current();
+    }
+
+    arrange(ms, ds);
+    arrange(md, dd);
 }
 
 node_t *closest_node(desktop_t *d, node_t *n, cycle_dir_t dir, client_select_t sel)
@@ -890,10 +935,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);
+            swap_nodes(m, d, f, m, d, 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);
+            swap_nodes(m, d, f, m, d, s);
     if (focus_first_child)
         focus_node(m, d, p->first_child);
     else