13 bool is_leaf(node_t *n)
15 return (n != NULL && n->first_child == NULL && n->second_child == NULL);
18 bool is_tiled(client_t *c)
22 return (!c->floating && !c->fullscreen);
25 bool is_floating(client_t *c)
29 return (c->floating && !c->fullscreen);
32 bool is_first_child(node_t *n)
34 return (n != NULL && n->parent != NULL && n->parent->first_child == n);
37 bool is_second_child(node_t *n)
39 return (n != NULL && n->parent != NULL && n->parent->second_child == n);
43 * Check if the specified node matches the selection criteria.
46 * node_t *c - the active node
47 * node_t *t - the node to test
48 * client_sel_t sel - the selection criteria
50 * Returns true if the node matches.
52 bool node_matches(node_t *c, node_t *t, client_select_t sel)
54 if (sel.type != CLIENT_TYPE_ALL &&
56 ? sel.type == CLIENT_TYPE_FLOATING
57 : sel.type == CLIENT_TYPE_TILED
60 if (sel.class != CLIENT_CLASS_ALL &&
61 streq(c->client->class_name, t->client->class_name)
62 ? sel.class == CLIENT_CLASS_DIFFER
63 : sel.class == CLIENT_CLASS_EQUAL
66 if (sel.mode != CLIENT_MODE_ALL &&
67 t->split_mode == MODE_MANUAL
68 ? sel.mode == CLIENT_MODE_AUTOMATIC
69 : sel.mode == CLIENT_MODE_MANUAL)
72 if (sel.urgency != CLIENT_URGENCY_ALL &&
74 ? sel.urgency == CLIENT_URGENCY_OFF
75 : sel.urgency == CLIENT_URGENCY_ON
81 bool desktop_matches(desktop_t *t, desktop_select_t sel) {
82 if (sel.status != DESKTOP_STATUS_ALL &&
84 ? sel.status == DESKTOP_STATUS_OCCUPIED
85 : sel.status == DESKTOP_STATUS_FREE
88 if (sel.urgency != DESKTOP_URGENCY_ALL &&
90 ? sel.urgency == DESKTOP_URGENCY_OFF
91 : sel.urgency == DESKTOP_URGENCY_ON
97 bool is_urgent(desktop_t *d)
99 for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
100 if (n->client->urgent)
105 void change_split_ratio(node_t *n, value_change_t chg)
107 n->split_ratio = pow(n->split_ratio,
108 (chg == CHANGE_INCREASE ? (1 / GROWTH_FACTOR) : GROWTH_FACTOR));
111 void change_layout(monitor_t *m, desktop_t *d, layout_t l)
119 void reset_mode(coordinates_t *loc)
121 if (loc->node != NULL) {
122 loc->node->split_mode = MODE_AUTOMATIC;
123 window_draw_border(loc->node, loc->desktop->focus == loc->node, mon == loc->monitor);
124 } else if (loc->desktop != NULL) {
125 for (node_t *a = first_extrema(loc->desktop->root); a != NULL; a = next_leaf(a, loc->desktop->root)) {
126 a->split_mode = MODE_AUTOMATIC;
127 window_draw_border(a, loc->desktop->focus == a, mon == loc->monitor);
132 node_t *brother_tree(node_t *n)
134 if (n == NULL || n->parent == NULL)
136 if (is_first_child(n))
137 return n->parent->second_child;
139 return n->parent->first_child;
142 node_t *first_extrema(node_t *n)
146 else if (n->first_child == NULL)
149 return first_extrema(n->first_child);
152 node_t *second_extrema(node_t *n)
156 else if (n->second_child == NULL)
159 return second_extrema(n->second_child);
162 node_t *next_leaf(node_t *n, node_t *r)
167 while (is_second_child(p) && p != r)
171 return first_extrema(p->parent->second_child);
174 node_t *prev_leaf(node_t *n, node_t *r)
179 while (is_first_child(p) && p != r)
183 return second_extrema(p->parent->first_child);
186 /* bool is_adjacent(node_t *a, node_t *r) */
188 /* node_t *f = r->parent; */
190 /* bool first_child = is_first_child(r); */
191 /* while (p != r) { */
192 /* if (p->parent->split_type == f->split_type && is_first_child(p) == first_child) */
199 /* Returns true if *b* is adjacent to *a* in the direction *dir* */
200 bool is_adjacent(node_t *a, node_t *b, direction_t dir)
204 return (a->rectangle.x + a->rectangle.width) == b->rectangle.x;
207 return (a->rectangle.y + a->rectangle.height) == b->rectangle.y;
210 return (b->rectangle.x + b->rectangle.width) == a->rectangle.x;
213 return (b->rectangle.y + b->rectangle.height) == a->rectangle.y;
219 node_t *find_fence(node_t *n, direction_t dir)
229 if ((dir == DIR_UP && p->split_type == TYPE_HORIZONTAL && p->rectangle.y < n->rectangle.y)
230 || (dir == DIR_LEFT && p->split_type == TYPE_VERTICAL && p->rectangle.x < n->rectangle.x)
231 || (dir == DIR_DOWN && p->split_type == TYPE_HORIZONTAL && (p->rectangle.y + p->rectangle.height) > (n->rectangle.y + n->rectangle.height))
232 || (dir == DIR_RIGHT && p->split_type == TYPE_VERTICAL && (p->rectangle.x + p->rectangle.width) > (n->rectangle.x + n->rectangle.width)))
241 node_t *nearest_neighbor(desktop_t *d, node_t *n, direction_t dir, client_select_t sel)
243 if (n == NULL || n->client->fullscreen
244 || (d->layout == LAYOUT_MONOCLE && is_tiled(n->client)))
247 node_t *nearest = NULL;
248 if (history_aware_focus)
249 nearest = nearest_from_history(d->history, n, dir, sel);
251 nearest = nearest_from_distance(d, n, dir, sel);
255 node_t *nearest_from_history(focus_history_t *f, node_t *n, direction_t dir, client_select_t sel)
257 if (n == NULL || !is_tiled(n->client))
260 node_t *target = find_fence(n, dir);
263 if (dir == DIR_UP || dir == DIR_LEFT)
264 target = target->first_child;
265 else if (dir == DIR_DOWN || dir == DIR_RIGHT)
266 target = target->second_child;
268 node_t *nearest = NULL;
269 int min_rank = INT_MAX;
271 for (node_t *a = first_extrema(target); a != NULL; a = next_leaf(a, target)) {
272 if (a->vacant || !is_adjacent(n, a, dir) || a == n)
274 if (!node_matches(n, a, sel))
277 int rank = history_rank(f, a);
278 if (rank >= 0 && rank < min_rank) {
287 node_t *nearest_from_distance(desktop_t *d, node_t *n, direction_t dir, client_select_t sel)
292 node_t *target = NULL;
294 if (is_tiled(n->client)) {
295 target = find_fence(n, dir);
298 if (dir == DIR_UP || dir == DIR_LEFT)
299 target = target->first_child;
300 else if (dir == DIR_DOWN || dir == DIR_RIGHT)
301 target = target->second_child;
306 node_t *nearest = NULL;
310 get_side_handle(n->client, dir, &pt);
311 get_opposite(dir, &dir2);
314 for (node_t *a = first_extrema(target); a != NULL; a = next_leaf(a, target)) {
315 if (a == n) continue;
316 if (!node_matches(n, a, sel)) continue;
317 if (is_tiled(a->client) != is_tiled(n->client)) continue;
318 if (is_tiled(a->client) && !is_adjacent(n, a, dir)) continue;
320 get_side_handle(a->client, dir2, &pt2);
321 double ds2 = distance(pt, pt2);
331 void get_opposite(direction_t src, direction_t *dst)
349 int tiled_area(node_t *n)
353 xcb_rectangle_t rect = n->client->tiled_rectangle;
354 return rect.width * rect.height;
357 node_t *find_biggest(desktop_t *d, node_t *c, client_select_t sel)
363 int r_area = tiled_area(r);
365 for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
366 if (!is_tiled(f->client) || !node_matches(c, f, sel))
368 int f_area = tiled_area(f);
372 } else if (f_area > r_area) {
381 void move_fence(node_t *n, direction_t dir, fence_move_t mov)
386 if ((mov == MOVE_PUSH && (dir == DIR_RIGHT || dir == DIR_DOWN))
387 || (mov == MOVE_PULL && (dir == DIR_LEFT || dir == DIR_UP)))
388 change_split_ratio(n, CHANGE_INCREASE);
390 change_split_ratio(n, CHANGE_DECREASE);
393 void rotate_tree(node_t *n, int deg)
395 if (n == NULL || is_leaf(n) || deg == 0)
400 if ((deg == 90 && n->split_type == TYPE_HORIZONTAL)
401 || (deg == 270 && n->split_type == TYPE_VERTICAL)
403 tmp = n->first_child;
404 n->first_child = n->second_child;
405 n->second_child = tmp;
406 n->split_ratio = 1.0 - n->split_ratio;
410 if (n->split_type == TYPE_HORIZONTAL)
411 n->split_type = TYPE_VERTICAL;
412 else if (n->split_type == TYPE_VERTICAL)
413 n->split_type = TYPE_HORIZONTAL;
416 rotate_tree(n->first_child, deg);
417 rotate_tree(n->second_child, deg);
420 void rotate_brother(node_t *n)
422 rotate_tree(brother_tree(n), n->birth_rotation);
425 void unrotate_tree(node_t *n, int rot)
429 rotate_tree(n, 360 - rot);
432 void unrotate_brother(node_t *n)
434 unrotate_tree(brother_tree(n), n->birth_rotation);
437 void flip_tree(node_t *n, flip_t flp)
439 if (n == NULL || is_leaf(n))
444 if ((flp == FLIP_HORIZONTAL && n->split_type == TYPE_HORIZONTAL)
445 || (flp == FLIP_VERTICAL && n->split_type == TYPE_VERTICAL)) {
446 tmp = n->first_child;
447 n->first_child = n->second_child;
448 n->second_child = tmp;
449 n->split_ratio = 1.0 - n->split_ratio;
452 flip_tree(n->first_child, flp);
453 flip_tree(n->second_child, flp);
456 int balance_tree(node_t *n)
458 if (n == NULL || n->vacant) {
460 } else if (is_leaf(n)) {
463 int b1 = balance_tree(n->first_child);
464 int b2 = balance_tree(n->second_child);
466 if (b1 > 0 && b2 > 0)
467 n->split_ratio = (double) b1 / b;
472 void arrange(monitor_t *m, desktop_t *d)
477 PRINTF("arrange %s%s%s\n", (num_monitors > 1 ? m->name : ""), (num_monitors > 1 ? " " : ""), d->name);
479 xcb_rectangle_t rect = m->rectangle;
480 int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : window_gap);
481 rect.x += m->left_padding + wg;
482 rect.y += m->top_padding + wg;
483 rect.width -= m->left_padding + m->right_padding + wg;
484 rect.height -= m->top_padding + m->bottom_padding + wg;
485 apply_layout(m, d, d->root, rect, rect);
488 void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, xcb_rectangle_t root_rect)
497 if (is_floating(n->client) && n->client->border_width != border_width) {
498 int ds = 2 * (border_width - n->client->border_width);
499 n->client->floating_rectangle.width += ds;
500 n->client->floating_rectangle.height += ds;
503 if ((borderless_monocle && is_tiled(n->client) && d->layout == LAYOUT_MONOCLE) ||
504 n->client->fullscreen)
505 n->client->border_width = 0;
507 n->client->border_width = border_width;
510 if (!n->client->fullscreen) {
511 if (!n->client->floating) {
513 if (d->layout == LAYOUT_TILED)
515 else if (d->layout == LAYOUT_MONOCLE)
519 int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : window_gap);
520 int bleed = wg + 2 * n->client->border_width;
521 r.width = (bleed < r.width ? r.width - bleed : 1);
522 r.height = (bleed < r.height ? r.height - bleed : 1);
523 n->client->tiled_rectangle = r;
525 /* floating clients */
526 r = n->client->floating_rectangle;
529 /* fullscreen clients */
533 window_move_resize(n->client->window, r.x, r.y, r.width, r.height);
534 window_border_width(n->client->window, n->client->border_width);
535 window_draw_border(n, n == d->focus, m == mon);
538 xcb_rectangle_t first_rect;
539 xcb_rectangle_t second_rect;
541 if (n->first_child->vacant || n->second_child->vacant) {
542 first_rect = second_rect = rect;
545 if (n->split_type == TYPE_VERTICAL) {
546 fence = rect.width * n->split_ratio;
547 first_rect = (xcb_rectangle_t) {rect.x, rect.y, fence, rect.height};
548 second_rect = (xcb_rectangle_t) {rect.x + fence, rect.y, rect.width - fence, rect.height};
549 } else if (n->split_type == TYPE_HORIZONTAL) {
550 fence = rect.height * n->split_ratio;
551 first_rect = (xcb_rectangle_t) {rect.x, rect.y, rect.width, fence};
552 second_rect = (xcb_rectangle_t) {rect.x, rect.y + fence, rect.width, rect.height - fence};
556 apply_layout(m, d, n->first_child, first_rect, root_rect);
557 apply_layout(m, d, n->second_child, second_rect, root_rect);
561 void insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f)
563 if (d == NULL || n == NULL)
566 PRINTF("insert node %X\n", n->client->window);
568 /* n: new leaf node */
569 /* c: new container node */
570 /* f: focus or insertion anchor */
571 /* p: parent of focus */
572 /* g: grand parent of focus */
577 node_t *c = make_node();
578 node_t *p = f->parent;
580 c->birth_rotation = f->birth_rotation;
581 switch (f->split_mode) {
586 if (m->rectangle.width > m->rectangle.height)
587 c->split_type = TYPE_VERTICAL;
589 c->split_type = TYPE_HORIZONTAL;
593 node_t *g = p->parent;
596 if (is_first_child(p))
603 c->split_type = p->split_type;
604 c->split_ratio = p->split_ratio;
607 if (is_first_child(f)) {
616 if (!is_floating(n->client))
618 n->birth_rotation = rot;
623 if (is_first_child(f))
628 c->split_ratio = f->split_ratio;
631 f->birth_rotation = 0;
632 switch (f->split_dir) {
634 c->split_type = TYPE_VERTICAL;
639 c->split_type = TYPE_VERTICAL;
644 c->split_type = TYPE_HORIZONTAL;
649 c->split_type = TYPE_HORIZONTAL;
656 f->split_mode = MODE_AUTOMATIC;
660 update_vacant_state(p);
665 void pseudo_focus(desktop_t *d, node_t *n)
670 history_add(d->history, n);
673 void focus_node(monitor_t *m, desktop_t *d, node_t *n)
675 if (n == NULL && d->root != NULL)
682 for (desktop_t *cd = mon->desk_head; cd != NULL; cd = cd->next)
683 window_draw_border(cd->focus, true, false);
684 for (desktop_t *cd = m->desk_head; cd != NULL; cd = cd->next)
686 window_draw_border(cd->focus, true, true);
688 window_draw_border(n, true, true);
692 window_draw_border(d->focus, false, true);
693 window_draw_border(n, true, true);
696 select_desktop(m, d);
699 ewmh_update_active_window();
703 PRINTF("focus node %X\n", n->client->window);
705 n->client->urgent = false;
712 if (focus_follows_pointer) {
713 xcb_window_t win = XCB_NONE;
714 query_pointer(&win, NULL);
715 if (win != n->client->window)
716 enable_motion_recorder();
718 disable_motion_recorder();
721 ewmh_update_active_window();
724 void update_current(void)
726 focus_node(mon, mon->desk, mon->desk->focus);
729 void unlink_node(desktop_t *d, node_t *n)
731 if (d == NULL || n == NULL)
734 PRINTF("unlink node %X\n", n->client->window);
736 node_t *p = n->parent;
743 node_t *g = p->parent;
744 if (is_first_child(n)) {
747 unrotate_tree(b, n->birth_rotation);
751 unrotate_tree(b, n->birth_rotation);
755 if (is_first_child(p))
763 b->birth_rotation = p->birth_rotation;
768 d->focus = history_get(d->history, 1);
770 update_vacant_state(b->parent);
775 void remove_node(desktop_t *d, node_t *n)
780 PRINTF("remove node %X\n", n->client->window);
783 history_remove(d->history, n);
788 ewmh_update_client_list();
794 void destroy_tree(node_t *n)
798 node_t *first_tree = n->first_child;
799 node_t *second_tree = n->second_child;
800 if (n->client != NULL)
803 destroy_tree(first_tree);
804 destroy_tree(second_tree);
807 void swap_nodes(node_t *n1, node_t *n2)
809 if (n1 == NULL || n2 == NULL || n1 == n2)
814 /* (n1 and n2 are leaves) */
815 node_t *pn1 = n1->parent;
816 node_t *pn2 = n2->parent;
817 bool n1_first_child = is_first_child(n1);
818 bool n2_first_child = is_first_child(n2);
819 int br1 = n1->birth_rotation;
820 int br2 = n2->birth_rotation;
824 pn1->first_child = n2;
826 pn1->second_child = n2;
831 pn2->first_child = n1;
833 pn2->second_child = n1;
838 n1->birth_rotation = br2;
839 n2->birth_rotation = br1;
841 if (n1->vacant != n2->vacant) {
842 update_vacant_state(n1->parent);
843 update_vacant_state(n2->parent);
846 /* If we ever need to generalize: */
847 /* if (d1 != d2) { */
848 /* if (d1->root == n1) */
850 /* if (d1->focus == n1) */
851 /* d1->focus = n2; */
852 /* if (d1->last_focus == n1) */
853 /* d1->last_focus = n2; */
854 /* if (d2->root == n2) */
856 /* if (d2->focus == n2) */
857 /* d2->focus = n1; */
858 /* if (d2->last_focus == n2) */
859 /* d2->last_focus = n1; */
863 void transfer_node(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd, node_t *n)
865 if (n == NULL || dd == ds)
868 PRINTF("transfer node %X\n", n->client->window);
871 history_remove(ds->history, n);
872 insert_node(md, dd, n, dd->focus);
873 ewmh_set_wm_desktop(n, dd);
875 if (ds == ms->desk && dd != md->desk) {
878 window_hide(n->client->window);
881 fit_monitor(md, n->client);
883 if (ds != ms->desk && dd == md->desk)
884 window_show(n->client->window);
894 if (ds == ms->desk || dd == md->desk)
898 void transplant_node(monitor_t *m, desktop_t *d, node_t *n1, node_t *n2)
902 bool was_focused = (d->focus == n1);
904 insert_node(m, d, n1, n2);
909 void select_monitor(monitor_t *m)
914 PRINTF("select monitor %s\n", m->name);
919 if (pointer_follows_monitor)
922 ewmh_update_current_desktop();
926 monitor_t *nearest_monitor(monitor_t *m, direction_t dir, desktop_select_t sel)
929 monitor_t *nearest = NULL;
930 xcb_rectangle_t rect = m->rectangle;
931 for (monitor_t *f = mon_head; f != NULL; f = f->next) {
934 if (!desktop_matches(f->desk, sel))
936 xcb_rectangle_t r = f->rectangle;
937 if ((dir == DIR_LEFT && r.x < rect.x) ||
938 (dir == DIR_RIGHT && r.x >= (rect.x + rect.width)) ||
939 (dir == DIR_UP && r.y < rect.y) ||
940 (dir == DIR_DOWN && r.y >= (rect.y + rect.height))) {
941 int d = abs((r.x + r.width / 2) - (rect.x + rect.width / 2)) +
942 abs((r.y + r.height / 2) - (rect.y + rect.height / 2));
952 void select_desktop(monitor_t *m, desktop_t *d)
959 PRINTF("select desktop %s\n", d->name);
962 desktop_hide(mon->desk);
964 mon->last_desk = mon->desk;
967 ewmh_update_current_desktop();
971 monitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, desktop_select_t sel)
973 monitor_t *f = (dir == CYCLE_PREV ? m->prev : m->next);
975 f = (dir == CYCLE_PREV ? mon_tail : mon_head);
978 if (desktop_matches(f->desk, sel))
980 f = (dir == CYCLE_PREV ? m->prev : m->next);
982 f = (dir == CYCLE_PREV ? mon_tail : mon_head);
988 desktop_t *closest_desktop(monitor_t *m, desktop_t *d, cycle_dir_t dir, desktop_select_t sel)
990 desktop_t *f = (dir == CYCLE_PREV ? d->prev : d->next);
992 f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head);
995 if (desktop_matches(f, sel))
997 f = (dir == CYCLE_PREV ? f->prev : f->next);
999 f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head);
1005 node_t *closest_node(desktop_t *d, node_t *n, cycle_dir_t dir, client_select_t sel)
1010 node_t *f = (dir == CYCLE_PREV ? prev_leaf(n, d->root) : next_leaf(n, d->root));
1012 f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root));
1015 if (node_matches(n, f, sel))
1017 f = (dir == CYCLE_PREV ? prev_leaf(f, d->root) : next_leaf(f, d->root));
1019 f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root));
1024 void circulate_leaves(monitor_t *m, desktop_t *d, circulate_dir_t dir)
1026 if (d == NULL || d->root == NULL || is_leaf(d->root))
1028 node_t *p = d->focus->parent;
1029 bool focus_first_child = is_first_child(d->focus);
1030 if (dir == CIRCULATE_FORWARD)
1031 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))
1034 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))
1036 if (focus_first_child)
1037 focus_node(m, d, p->first_child);
1039 focus_node(m, d, p->second_child);
1042 void update_vacant_state(node_t *n)
1047 PUTS("update vacant state");
1049 /* n is not a leaf */
1053 p->vacant = (p->first_child->vacant && p->second_child->vacant);
1058 void fit_monitor(monitor_t *m, client_t *c)
1060 xcb_rectangle_t crect = c->floating_rectangle;
1061 xcb_rectangle_t mrect = m->rectangle;
1062 while (crect.x < mrect.x)
1063 crect.x += mrect.width;
1064 while (crect.x > (mrect.x + mrect.width - 1))
1065 crect.x -= mrect.width;
1066 while (crect.y < mrect.y)
1067 crect.y += mrect.height;
1068 while (crect.y > (mrect.y + mrect.height - 1))
1069 crect.y -= mrect.height;
1070 c->floating_rectangle = crect;