]> git.lizzy.rs Git - bspwm.git/blob - tree.c
New setting: `split_ratio`
[bspwm.git] / tree.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <math.h>
6 #include <xcb/xcb.h>
7 #include <xcb/xcb_event.h>
8 #include "settings.h"
9 #include "helpers.h"
10 #include "window.h"
11 #include "types.h"
12 #include "bspwm.h"
13 #include "ewmh.h"
14 #include "tree.h"
15
16 bool is_leaf(node_t *n)
17 {
18     return (n != NULL && n->first_child == NULL && n->second_child == NULL);
19 }
20
21 bool is_tiled(client_t *c)
22 {
23     if (c == NULL)
24         return false;
25     return (!c->floating && !c->transient && !c->fullscreen);
26 }
27
28 bool is_floating(client_t *c)
29 {
30     if (c == NULL)
31         return false;
32     return (c->floating && !c->fullscreen);
33 }
34
35 bool is_first_child(node_t *n)
36 {
37     return (n != NULL && n->parent != NULL && n->parent->first_child == n);
38 }
39
40 bool is_second_child(node_t *n)
41 {
42     return (n != NULL && n->parent != NULL && n->parent->second_child == n);
43 }
44
45 void change_split_ratio(node_t *n, value_change_t chg) {
46     n->split_ratio = pow(n->split_ratio, (chg == CHANGE_INCREASE ? INC_EXP : DEC_EXP));
47 }
48
49 node_t *first_extrema(node_t *n)
50 {
51     if (n == NULL)
52         return NULL;
53     else if (n->first_child == NULL)
54         return n;
55     else
56         return first_extrema(n->first_child);
57 }
58
59 node_t *second_extrema(node_t *n)
60 {
61     if (n == NULL)
62         return NULL;
63     else if (n->second_child == NULL)
64         return n;
65     else
66         return second_extrema(n->second_child);
67 }
68
69 node_t *next_leaf(node_t *n)
70 {
71     if (n == NULL)
72         return NULL;
73     node_t *p = n;
74     while (is_second_child(p))
75         p = p->parent;
76     if (p->parent == NULL)
77         return NULL;
78     return first_extrema(p->parent->second_child);
79 }
80
81 node_t *prev_leaf(node_t *n)
82 {
83     if (n == NULL)
84         return NULL;
85     node_t *p = n;
86     while (is_first_child(p))
87         p = p->parent;
88     if (p->parent == NULL)
89         return NULL;
90     return second_extrema(p->parent->first_child);
91 }
92
93 node_t *find_fence(node_t *n, direction_t dir)
94 {
95     node_t *p;
96
97     if (n == NULL)
98         return NULL;
99
100     p = n->parent;
101
102     while (p != NULL) {
103         if ((dir == DIR_UP && p->split_type == TYPE_HORIZONTAL && p->rectangle.y < n->rectangle.y)
104                 || (dir == DIR_LEFT && p->split_type == TYPE_VERTICAL && p->rectangle.x < n->rectangle.x)
105                 || (dir == DIR_DOWN && p->split_type == TYPE_HORIZONTAL && (p->rectangle.y + p->rectangle.height) > (n->rectangle.y + n->rectangle.height))
106                 || (dir == DIR_RIGHT && p->split_type == TYPE_VERTICAL && (p->rectangle.x + p->rectangle.width) > (n->rectangle.x + n->rectangle.width)))
107             return p;
108         p = p->parent;
109     }
110
111     return NULL;
112 }
113
114 node_t *find_neighbor(node_t *n, direction_t dir)
115 {
116     node_t *fence = find_fence(n, dir);
117
118     if (fence == NULL)
119         return NULL;
120
121     if (dir == DIR_UP || dir == DIR_LEFT)
122         return second_extrema(fence->first_child);
123     else if (dir == DIR_DOWN || dir == DIR_RIGHT)
124         return first_extrema(fence->second_child);
125
126     return NULL;
127 }
128
129 int tiled_area(node_t *n)
130 {
131     if (n == NULL)
132         return -1;
133     xcb_rectangle_t rect = n->client->tiled_rectangle;
134     return rect.width * rect.height;
135 }
136
137 node_t *find_by_area(desktop_t *d, swap_arg_t a)
138 {
139     if (d == NULL)
140         return NULL;
141
142     node_t *r = NULL;
143     int r_area = tiled_area(r);
144
145     for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f)) {
146         int f_area = tiled_area(f);
147         if (r == NULL) {
148             r = f;
149             r_area = f_area;
150         } else if ((a == SWAP_BIGGEST && f_area > r_area)
151                 || (a == SWAP_SMALLEST && f_area < r_area)) {
152             r = f;
153             r_area = f_area;
154         }
155     }
156
157     return r;
158 }
159
160 void move_fence(node_t *n, direction_t dir, fence_move_t mov)
161 {
162     node_t *fence = find_fence(n, dir);
163
164     if (fence == NULL)
165         return;
166
167     if ((mov == MOVE_PUSH && (dir == DIR_RIGHT || dir == DIR_DOWN))
168             || (mov == MOVE_PULL && (dir == DIR_LEFT || dir == DIR_UP)))
169         change_split_ratio(fence, CHANGE_INCREASE);
170     else
171         change_split_ratio(fence, CHANGE_DECREASE);
172 }
173
174 void rotate_tree(node_t *n, rotate_t rot)
175 {
176     if (n == NULL || is_leaf(n))
177         return;
178
179     node_t *tmp;
180
181     if ((rot == ROTATE_CLOCKWISE && n->split_type == TYPE_HORIZONTAL)
182             || (rot == ROTATE_COUNTER_CLOCKWISE && n->split_type == TYPE_VERTICAL)
183             || rot == ROTATE_FULL_CYCLE) {
184         tmp = n->first_child;
185         n->first_child = n->second_child;
186         n->second_child = tmp;
187         n->split_ratio = 1.0 - n->split_ratio;
188     }
189
190     if (rot != ROTATE_FULL_CYCLE) {
191         if (n->split_type == TYPE_HORIZONTAL)
192             n->split_type = TYPE_VERTICAL;
193         else if (n->split_type == TYPE_VERTICAL)
194             n->split_type = TYPE_HORIZONTAL;
195     }
196
197     rotate_tree(n->first_child, rot);
198     rotate_tree(n->second_child, rot);
199 }
200
201 void flip_tree(node_t *n, flip_t flp)
202 {
203     if (n == NULL || is_leaf(n))
204         return;
205
206     node_t *tmp;
207
208     if ((flp == FLIP_HORIZONTAL && n->split_type == TYPE_HORIZONTAL)
209             || (flp == FLIP_VERTICAL && n->split_type == TYPE_VERTICAL)) {
210         tmp = n->first_child;
211         n->first_child = n->second_child;
212         n->second_child = tmp;
213         n->split_ratio = 1.0 - n->split_ratio;
214     }
215
216     flip_tree(n->first_child, flp);
217     flip_tree(n->second_child, flp);
218 }
219
220 int balance_tree(node_t *n)
221 {
222     if (n == NULL || n->vacant) {
223         return 0;
224     } else if (is_leaf(n)) {
225         return 1;
226     } else {
227         int b1 = balance_tree(n->first_child);
228         int b2 = balance_tree(n->second_child);
229         int b = b1 + b2;
230         if (b1 > 0 && b2 > 0)
231             n->split_ratio = (double) b1 / b;
232         return b;
233     }
234 }
235
236 void arrange(monitor_t *m, desktop_t *d)
237 {
238     PRINTF("arrange %s%s%s\n", (num_monitors > 1 ? m->name : ""), (num_monitors > 1 ? " " : ""), d->name);
239
240     xcb_rectangle_t rect = m->rectangle;
241     int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : window_gap);
242     rect.x += m->left_padding + wg;
243     rect.y += m->top_padding + wg;
244     rect.width -= m->left_padding + m->right_padding + wg;
245     rect.height -= m->top_padding + m->bottom_padding + wg;
246     apply_layout(m, d, d->root, rect, rect);
247 }
248
249 void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, xcb_rectangle_t root_rect)
250 {
251     if (n == NULL)
252         return;
253
254     n->rectangle = rect;
255
256     if (is_leaf(n)) {
257         if (n->client->fullscreen)
258             return;
259
260         if (is_floating(n->client) && n->client->border_width != border_width) {
261             int ds = 2 * (border_width - n->client->border_width);
262             n->client->floating_rectangle.width += ds;
263             n->client->floating_rectangle.height += ds;
264         }
265
266         if (borderless_monocle && is_tiled(n->client) && d->layout == LAYOUT_MONOCLE)
267             n->client->border_width = 0;
268         else
269             n->client->border_width = border_width;
270
271         xcb_rectangle_t r;
272         if (is_tiled(n->client)) {
273             if (d->layout == LAYOUT_TILED)
274                 r = rect;
275             else if (d->layout == LAYOUT_MONOCLE)
276                 r = root_rect;
277             int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : window_gap);
278             int bleed = wg + 2 * n->client->border_width;
279             r.width = (bleed < r.width ? r.width - bleed : 1);
280             r.height = (bleed < r.height ? r.height - bleed : 1);
281             n->client->tiled_rectangle = r;
282         } else {
283             r = n->client->floating_rectangle;
284         }
285
286         window_move_resize(n->client->window, r.x, r.y, r.width, r.height);
287         window_border_width(n->client->window, n->client->border_width);
288         window_draw_border(n, n == d->focus, m == mon);
289
290     } else {
291         xcb_rectangle_t first_rect;
292         xcb_rectangle_t second_rect;
293
294         if (n->first_child->vacant || n->second_child->vacant) {
295             first_rect = second_rect = rect;
296         } else {
297             unsigned int fence;
298             if (n->split_type == TYPE_VERTICAL) {
299                 fence = rect.width * n->split_ratio;
300                 first_rect = (xcb_rectangle_t) {rect.x, rect.y, fence, rect.height};
301                 second_rect = (xcb_rectangle_t) {rect.x + fence, rect.y, rect.width - fence, rect.height};
302
303             } else if (n->split_type == TYPE_HORIZONTAL) {
304                 fence = rect.height * n->split_ratio;
305                 first_rect = (xcb_rectangle_t) {rect.x, rect.y, rect.width, fence};
306                 second_rect = (xcb_rectangle_t) {rect.x, rect.y + fence, rect.width, rect.height - fence};
307             }
308         }
309
310         apply_layout(m, d, n->first_child, first_rect, root_rect);
311         apply_layout(m, d, n->second_child, second_rect, root_rect);
312     }
313 }
314
315 void insert_node(monitor_t *m, desktop_t *d, node_t *n)
316 {
317     if (d == NULL || n == NULL)
318         return;
319
320     PRINTF("insert node %X\n", n->client->window);
321
322     node_t *focus = d->focus;
323
324     if (focus == NULL) {
325         d->root = n;
326     } else {
327         node_t *dad = make_node();
328         node_t *fopar = focus->parent;
329         n->parent = dad;
330         n->client->born_as = split_mode;
331         switch (split_mode) {
332             case MODE_AUTOMATIC:
333                 if (fopar == NULL) {
334                     dad->first_child = n;
335                     dad->second_child = focus;
336                     if (m->rectangle.width > m->rectangle.height)
337                         dad->split_type = TYPE_VERTICAL;
338                     else
339                         dad->split_type = TYPE_HORIZONTAL;
340                     focus->parent = dad;
341                     d->root = dad;
342                 } else {
343                     node_t *grandpa = fopar->parent;
344                     dad->parent = grandpa;
345                     if (grandpa != NULL) {
346                         if (is_first_child(fopar))
347                             grandpa->first_child = dad;
348                         else
349                             grandpa->second_child = dad;
350                     } else {
351                         d->root = dad;
352                     }
353                     dad->split_type = fopar->split_type;
354                     dad->split_ratio = fopar->split_ratio;
355                     fopar->parent = dad;
356                     if (is_first_child(focus)) {
357                         dad->first_child = n;
358                         dad->second_child = fopar;
359                         rotate_tree(fopar, ROTATE_CLOCKWISE);
360                     } else {
361                         dad->first_child = fopar;
362                         dad->second_child = n;
363                         rotate_tree(fopar, ROTATE_COUNTER_CLOCKWISE);
364                     }
365                 }
366                 break;
367             case MODE_MANUAL:
368                 if (fopar != NULL) {
369                     if (is_first_child(focus))
370                         fopar->first_child = dad;
371                     else
372                         fopar->second_child = dad;
373                 }
374                 dad->split_ratio = focus->split_ratio;
375                 dad->parent = fopar;
376                 focus->parent = dad;
377                 switch (split_dir) {
378                     case DIR_LEFT:
379                         dad->split_type = TYPE_VERTICAL;
380                         dad->first_child = n;
381                         dad->second_child = focus;
382                         break;
383                     case DIR_RIGHT:
384                         dad->split_type = TYPE_VERTICAL;
385                         dad->first_child = focus;
386                         dad->second_child = n;
387                         break;
388                     case DIR_UP:
389                         dad->split_type = TYPE_HORIZONTAL;
390                         dad->first_child = n;
391                         dad->second_child = focus;
392                         break;
393                     case DIR_DOWN:
394                         dad->split_type = TYPE_HORIZONTAL;
395                         dad->first_child = focus;
396                         dad->second_child = n;
397                         break;
398                 }
399                 if (d->root == focus)
400                     d->root = dad;
401                 split_mode = MODE_AUTOMATIC;
402                 break;
403         }
404         if (focus->vacant)
405             update_vacant_state(fopar);
406     }
407 }
408
409 void focus_node(monitor_t *m, desktop_t *d, node_t *n, bool is_mapped)
410 {
411     if (n == NULL)
412         return;
413
414     PRINTF("focus node %X\n", n->client->window);
415
416     split_mode = MODE_AUTOMATIC;
417     n->client->urgent = false;
418
419     if (is_mapped) {
420         if (mon != m) {
421             for (desktop_t *cd = mon->desk_head; cd != NULL; cd = cd->next)
422                 window_draw_border(cd->focus, true, false);
423             for (desktop_t *cd = m->desk_head; cd != NULL; cd = cd->next)
424                 if (cd != d)
425                     window_draw_border(cd->focus, true, true);
426             if (d->focus == n)
427                 window_draw_border(n, true, true);
428         }
429         if (d->focus != n) {
430             window_draw_border(d->focus, false, true);
431             window_draw_border(n, true, true);
432         }
433         xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, n->client->window, XCB_CURRENT_TIME);
434     }
435
436     if (!is_tiled(n->client)) {
437         if (!adaptative_raise || !might_cover(d, n))
438             window_raise(n->client->window);
439     } else {
440         window_pseudo_raise(d, n->client->window);
441     }
442
443     if (focus_follows_pointer) {
444         xcb_window_t win = XCB_NONE;
445         query_pointer(&win, NULL);
446         if (win != n->client->window)
447             enable_motion_recorder();
448         else
449             disable_motion_recorder();
450     }
451
452     if (d->focus != n) {
453         d->last_focus = d->focus;
454         d->focus = n;
455     }
456
457     ewmh_update_active_window();
458     put_status();
459 }
460
461 void update_current(void)
462 {
463     if (mon->desk->focus == NULL)
464         ewmh_update_active_window();
465     else
466         focus_node(mon, mon->desk, mon->desk->focus, true);
467     put_status();
468 }
469
470 void unlink_node(desktop_t *d, node_t *n)
471 {
472     if (d == NULL || n == NULL)
473         return;
474
475     PRINTF("unlink node %X\n", n->client->window);
476
477     node_t *p = n->parent;
478
479     if (p == NULL) {
480         d->root = NULL;
481         d->focus = NULL;
482         d->last_focus = NULL;
483     } else {
484         node_t *b;
485         node_t *g = p->parent;
486         bool n_first_child = is_first_child(n);
487         if (n_first_child) {
488             b = p->second_child;
489             if (n->client->born_as == MODE_AUTOMATIC)
490                 rotate_tree(b, ROTATE_COUNTER_CLOCKWISE);
491         } else {
492             b = p->first_child;
493             if (n->client->born_as == MODE_AUTOMATIC)
494                 rotate_tree(b, ROTATE_CLOCKWISE);
495         }
496         b->parent = g;
497         if (g != NULL) {
498             if (is_first_child(p))
499                 g->first_child = b;
500             else
501                 g->second_child = b;
502         } else {
503             d->root = b;
504         }
505
506         n->parent = NULL;
507         free(p);
508
509         if (n == d->last_focus) {
510             d->last_focus = NULL;
511         } else if (n == d->focus) {
512             if (d->last_focus != NULL)
513                 d->focus = d->last_focus;
514             else
515                 d->focus = (n_first_child ? first_extrema(b) : second_extrema(b));
516             d->last_focus = NULL;
517         }
518
519         update_vacant_state(b->parent);
520     }
521 }
522
523 void remove_node(desktop_t *d, node_t *n)
524 {
525     if (d == NULL || n == NULL)
526         return;
527
528     PRINTF("remove node %X\n", n->client->window);
529
530     unlink_node(d, n);
531     free(n->client);
532     free(n);
533
534     num_clients--;
535     ewmh_update_client_list();
536
537     if (mon->desk == d)
538         update_current();
539 }
540
541 void destroy_tree(node_t *n)
542 {
543     if (n == NULL)
544         return;
545     node_t *first_tree = n->first_child;
546     node_t *second_tree = n->second_child;
547     if (n->client != NULL)
548         free(n->client);
549     free(n);
550     destroy_tree(first_tree);
551     destroy_tree(second_tree);
552 }
553
554 void swap_nodes(desktop_t *d1, node_t *n1, desktop_t *d2, node_t *n2)
555 {
556     if (n1 == NULL || n2 == NULL || n1 == n2)
557         return;
558
559     PUTS("swap nodes");
560
561     /* (n1 and n2 are leaves) */
562     node_t *pn1 = n1->parent;
563     node_t *pn2 = n2->parent;
564     bool n1_first_child = is_first_child(n1);
565     bool n2_first_child = is_first_child(n2);
566
567     if (pn1 != NULL) {
568         if (n1_first_child)
569             pn1->first_child = n2;
570         else
571             pn1->second_child = n2;
572     }
573
574     if (pn2 != NULL) {
575         if (n2_first_child)
576             pn2->first_child = n1;
577         else
578             pn2->second_child = n1;
579     }
580
581     n1->parent = pn2;
582     n2->parent = pn1;
583
584     if (n1->vacant != n2->vacant) {
585         update_vacant_state(n1->parent);
586         update_vacant_state(n2->parent);
587     }
588
589     if (d1 != d2) {
590         if (d1->root == n1)
591             d1->root = n2;
592         if (d1->focus == n1)
593             d1->focus = n2;
594         if (d1->last_focus == n1)
595             d1->last_focus = n2;
596         if (d2->root == n2)
597             d2->root = n1;
598         if (d2->focus == n2)
599             d2->focus = n1;
600         if (d2->last_focus == n2)
601             d2->last_focus = n1;
602     }
603 }
604
605 void transfer_node(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd, node_t *n)
606 {
607     if (n == NULL || ds == NULL || dd == NULL || ms == NULL || md == NULL || (ms == md && dd == ds))
608         return;
609
610     PRINTF("transfer node %X\n", n->client->window);
611
612     unlink_node(ds, n);
613     insert_node(md, dd, n);
614     ewmh_set_wm_desktop(n, dd);
615
616     if (ds == ms->desk && dd != md->desk) {
617         if (n == ds->focus)
618             clear_input_focus();
619         window_hide(n->client->window);
620     }
621
622     fit_monitor(md, n->client);
623
624     if (n->client->fullscreen)
625         window_move_resize(n->client->window, md->rectangle.x, md->rectangle.y, md->rectangle.width, md->rectangle.height);
626
627     if (ds != ms->desk && dd == md->desk) {
628         window_show(n->client->window);
629         focus_node(md, dd, n, true);
630     } else {
631         focus_node(md, dd, n, false);
632     }
633
634     if (ds == ms->desk || dd == md->desk)
635         update_current();
636 }
637
638 void select_monitor(monitor_t *m)
639 {
640     if (m == NULL || mon == m)
641         return;
642
643     PRINTF("select monitor %s\n", m->name);
644
645     focus_node(m, m->desk, m->desk->focus, true);
646
647     last_mon = mon;
648     mon = m;
649
650     ewmh_update_current_desktop();
651     put_status();
652 }
653
654 void select_desktop(desktop_t *d)
655 {
656     if (d == NULL || d == mon->desk)
657         return;
658
659     PRINTF("select desktop %s\n", d->name);
660
661     clear_input_focus();
662
663     if (visible) {
664         node_t *n = first_extrema(d->root);
665
666         while (n != NULL) {
667             window_show(n->client->window);
668             n = next_leaf(n);
669         }
670
671         n = first_extrema(mon->desk->root);
672
673         while (n != NULL) {
674             window_hide(n->client->window);
675             n = next_leaf(n);
676         }
677     }
678
679     mon->last_desk = mon->desk;
680     mon->desk = d;
681
682     update_current();
683     ewmh_update_current_desktop();
684     put_status();
685 }
686
687 void cycle_monitor(cycle_dir_t dir)
688 {
689     if (dir == CYCLE_NEXT)
690         select_monitor((mon->next == NULL ? mon_head : mon->next));
691     else if (dir == CYCLE_PREV)
692         select_monitor((mon->prev == NULL ? mon_tail : mon->prev));
693 }
694
695 void cycle_desktop(monitor_t *m, desktop_t *d, cycle_dir_t dir, skip_desktop_t skip)
696 {
697     desktop_t *f = (dir == CYCLE_PREV ? d->prev : d->next);
698     if (f == NULL)
699         f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head);
700
701     while (f != d) {
702         if (skip == DESKTOP_SKIP_NONE
703                 || (skip == DESKTOP_SKIP_FREE && f->root != NULL)
704                 || (skip == DESKTOP_SKIP_OCCUPIED && f->root == NULL)) {
705             select_desktop(f);
706             return;
707         }
708         f = (dir == CYCLE_PREV ? f->prev : f->next);
709         if (f == NULL)
710             f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head);
711     }
712 }
713
714 void cycle_leaf(monitor_t *m, desktop_t *d, node_t *n, cycle_dir_t dir, skip_client_t skip)
715 {
716     if (n == NULL)
717         return;
718
719     PUTS("cycle leaf");
720
721     node_t *f = (dir == CYCLE_PREV ? prev_leaf(n) : next_leaf(n));
722     if (f == NULL)
723         f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root));
724
725     while (f != n) {
726         bool tiled = is_tiled(f->client);
727         if (skip == CLIENT_SKIP_NONE || (skip == CLIENT_SKIP_TILED && !tiled) || (skip == CLIENT_SKIP_FLOATING && tiled)
728                 || (skip == CLIENT_SKIP_CLASS_DIFFER && strcmp(f->client->class_name, n->client->class_name) == 0)
729                 || (skip == CLIENT_SKIP_CLASS_EQUAL && strcmp(f->client->class_name, n->client->class_name) != 0)) {
730             focus_node(m, d, f, true);
731             return;
732         }
733         f = (dir == CYCLE_PREV ? prev_leaf(f) : next_leaf(f));
734         if (f == NULL)
735             f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root));
736     }
737 }
738
739 void nearest_leaf(monitor_t *m, desktop_t *d, node_t *n, nearest_arg_t dir, skip_client_t skip)
740 {
741     if (n == NULL)
742         return;
743
744     PUTS("nearest leaf");
745
746     node_t *x = NULL;
747
748     for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f))
749         if (skip == CLIENT_SKIP_NONE || (skip == CLIENT_SKIP_TILED && !is_tiled(f->client)) || (skip == CLIENT_SKIP_FLOATING && is_tiled(f->client))
750                 || (skip == CLIENT_SKIP_CLASS_DIFFER && strcmp(f->client->class_name, n->client->class_name) == 0)
751                 || (skip == CLIENT_SKIP_CLASS_EQUAL && strcmp(f->client->class_name, n->client->class_name) != 0))
752             if ((dir == NEAREST_OLDER
753                         && (f->client->uid < n->client->uid)
754                         && (x == NULL || f->client->uid > x->client->uid))
755                     || (dir == NEAREST_NEWER
756                         && (f->client->uid > n->client->uid)
757                         && (x == NULL || f->client->uid < x->client->uid)))
758                 x = f;
759
760     focus_node(m, d, x, true);
761 }
762
763 void circulate_leaves(monitor_t *m, desktop_t *d, circulate_dir_t dir) {
764     if (d == NULL || d->root == NULL || is_leaf(d->root))
765         return;
766     node_t *par = d->focus->parent;
767     bool focus_first_child = is_first_child(d->focus);
768     if (dir == CIRCULATE_FORWARD)
769         for (node_t *s = second_extrema(d->root), *f = prev_leaf(s); f != NULL; s = prev_leaf(f), f = prev_leaf(s))
770             swap_nodes(d, f, d, s);
771     else
772         for (node_t *f = first_extrema(d->root), *s = next_leaf(f); s != NULL; f = next_leaf(s), s = next_leaf(f))
773             swap_nodes(d, f, d, s);
774     if (focus_first_child)
775         focus_node(m, d, par->first_child, true);
776     else
777         focus_node(m, d, par->second_child, true);
778 }
779
780 void update_vacant_state(node_t *n)
781 {
782     if (n == NULL)
783         return;
784
785     PUTS("update vacant state");
786
787     /* n is not a leaf */
788     node_t *p = n;
789
790     while (p != NULL) {
791         p->vacant = (p->first_child->vacant && p->second_child->vacant);
792         p = p->parent;
793     }
794 }
795
796 void fit_monitor(monitor_t *m, client_t *c)
797 {
798     xcb_rectangle_t crect = c->floating_rectangle;
799     xcb_rectangle_t mrect = m->rectangle;
800     while (crect.x < mrect.x)
801         crect.x += mrect.width;
802     while (crect.x > (mrect.x + mrect.width - 1))
803         crect.x -= mrect.width;
804     while (crect.y < mrect.y)
805         crect.y += mrect.height;
806     while (crect.y > (mrect.y + mrect.height - 1))
807         crect.y -= mrect.height;
808     c->floating_rectangle = crect;
809 }
810
811 void put_status(void)
812 {
813     if (status_fifo == NULL)
814         return;
815     if (status_prefix != NULL)
816         fprintf(status_fifo, "%s", status_prefix);
817     bool urgent = false;
818     for (monitor_t *m = mon_head; m != NULL; m = m->next) {
819         fprintf(status_fifo, "%c%s:", (mon == m ? 'M' : 'm'), m->name);
820         for (desktop_t *d = m->desk_head; d != NULL; d = d->next, urgent = false) {
821             for (node_t *n = first_extrema(d->root); n != NULL && !urgent; n = next_leaf(n))
822                 urgent |= n->client->urgent;
823             fprintf(status_fifo, "%c%c%s:", (m->desk == d ? 'D' : (d->root != NULL ? 'd' : '_')), (urgent ? '!' : '_'), d->name);
824         }
825     }
826     fprintf(status_fifo, "L%s:W%X\n", (mon->desk->layout == LAYOUT_TILED ? "tiled" : "monocle"), (mon->desk->focus == NULL ? 0 : mon->desk->focus->client->window));
827     fflush(status_fifo);
828 }
829
830 void list_monitors(list_option_t opt, char *rsp)
831 {
832     char line[MAXLEN];
833     for (monitor_t *m = mon_head; m != NULL; m = m->next) {
834         snprintf(line, sizeof(line), "%s %ux%u%+i%+i", m->name, m->rectangle.width, m->rectangle.height, m->rectangle.x, m->rectangle.y);
835         strncat(rsp, line, REMLEN(rsp));
836         if (m == mon)
837             strncat(rsp, " #\n", REMLEN(rsp));
838         else if (m == last_mon)
839             strncat(rsp, " ~\n", REMLEN(rsp));
840         else
841             strncat(rsp, "\n", REMLEN(rsp));
842         if (opt == LIST_OPTION_VERBOSE)
843             list_desktops(m, opt, 1, rsp);
844     }
845 }
846
847 void list_desktops(monitor_t *m, list_option_t opt, unsigned int depth, char *rsp)
848 {
849     char line[MAXLEN];
850     for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
851         for (unsigned int i = 0; i < depth; i++)
852             strncat(rsp, "  ", REMLEN(rsp));
853         snprintf(line, sizeof(line), "%s %c", d->name, (d->layout == LAYOUT_TILED ? 'T' : 'M'));
854         strncat(rsp, line, REMLEN(rsp));
855         if (d == m->desk)
856             strncat(rsp, " @\n", REMLEN(rsp));
857         else if (d == m->last_desk)
858             strncat(rsp, " ~\n", REMLEN(rsp));
859         else
860             strncat(rsp, "\n", REMLEN(rsp));
861         if (opt == LIST_OPTION_VERBOSE)
862             list(d, d->root, rsp, depth + 1);
863     }
864 }
865
866 void list(desktop_t *d, node_t *n, char *rsp, unsigned int depth)
867 {
868     if (n == NULL)
869         return;
870
871     char line[MAXLEN];
872
873     for (unsigned int i = 0; i < depth; i++)
874         strncat(rsp, "  ", REMLEN(rsp));
875
876     if (is_leaf(n)) {
877         client_t *c = n->client;
878         snprintf(line, sizeof(line), "%c %s %X %u %u %ux%u%+i%+i %c%c%c%c%c", (c->born_as == MODE_AUTOMATIC ? 'a' : 'm'), c->class_name, c->window, c->uid, c->border_width, c->floating_rectangle.width, c->floating_rectangle.height, c->floating_rectangle.x, c->floating_rectangle.y, (c->floating ? 'f' : '-'), (c->transient ? 't' : '-'), (c->fullscreen ? 'F' : '-'), (c->urgent ? 'u' : '-'), (c->locked ? 'l' : '-'));
879     } else {
880         snprintf(line, sizeof(line), "%c %.2f", (n->split_type == TYPE_HORIZONTAL ? 'H' : 'V'), n->split_ratio);
881     }
882
883     strncat(rsp, line, REMLEN(rsp));
884
885     if (n == d->focus)
886         strncat(rsp, " *\n", REMLEN(rsp));
887     else if (n == d->last_focus)
888         strncat(rsp, " ~\n", REMLEN(rsp));
889     else
890         strncat(rsp, "\n", REMLEN(rsp));
891
892     list(d, n->first_child, rsp, depth + 1);
893     list(d, n->second_child, rsp, depth + 1);
894 }
895
896 void restore(char *file_path)
897 {
898     if (file_path == NULL)
899         return;
900
901     FILE *snapshot = fopen(file_path, "r");
902     if (snapshot == NULL) {
903         warn("restore: can't open file\n");
904         return;
905     }
906
907     char line[MAXLEN];
908     monitor_t *m = NULL;
909     desktop_t *d = NULL;
910     node_t *n = NULL;
911     num_clients = 0;
912     unsigned int level, last_level = 0, max_uid = 0;
913     bool aborted = false;
914
915     while (!aborted && fgets(line, sizeof(line), snapshot) != NULL) {
916         unsigned int len = strlen(line);
917         level = 0;
918         while (level < strlen(line) && isspace(line[level]))
919             level++;
920         if (level == 0) {
921             if (m == NULL)
922                 m = mon_head;
923             else
924                 m = m->next;
925             if (len >= 2)
926                 switch (line[len - 2]) {
927                     case '#':
928                         mon = m;
929                         break;
930                     case '~':
931                         last_mon = m;
932                         break;
933                 }
934         } else if (level == 2) {
935             if (d == NULL)
936                 d = m->desk_head;
937             else
938                 d = d->next;
939             int i = len - 1;
940             while (i > 0 && !isupper(line[i]))
941                 i--;
942             if (line[i] == 'M')
943                 d->layout = LAYOUT_MONOCLE;
944             else if (line[i] == 'T')
945                 d->layout = LAYOUT_TILED;
946             if (len >= 2)
947                 switch (line[len - 2]) {
948                     case '@':
949                         m->desk = d;
950                         break;
951                     case '~':
952                         m->last_desk = d;
953                         break;
954                 }
955         } else {
956             node_t *birth = make_node();
957             if (level == 4) {
958                 empty_desktop(d);
959                 d->root = birth;
960             } else {
961                 if (level > last_level) {
962                     n->first_child = birth;
963                 } else {
964                     do {
965                         n = n->parent;
966                     } while (n != NULL && n->second_child != NULL);
967                     if (n == NULL) {
968                         warn("restore: file is malformed\n");
969                         aborted = true;
970                     }
971                     n->second_child = birth;
972                 }
973                 birth->parent = n;
974             }
975             n = birth;
976
977             if (isupper(line[level])) {
978                 char st;
979                 sscanf(line + level, "%c %lf", &st, &n->split_ratio);
980                 if (st == 'H')
981                     n->split_type = TYPE_HORIZONTAL;
982                 else if (st == 'V')
983                     n->split_type = TYPE_VERTICAL;
984             } else {
985                 client_t *c = make_client(XCB_NONE);
986                 num_clients++;
987                 char ba, floating, transient, fullscreen, urgent, locked;
988                 sscanf(line + level, "%c %s %X %u %u %hux%hu%hi%hi %c%c%c%c%c", &ba, c->class_name, &c->window, &c->uid, &c->border_width, &c->floating_rectangle.width, &c->floating_rectangle.height, &c->floating_rectangle.x, &c->floating_rectangle.y, &floating, &transient, &fullscreen, &urgent, &locked);
989                 if (ba == 'a')
990                     c->born_as = MODE_AUTOMATIC;
991                 else if (ba == 'm')
992                     c->born_as = MODE_MANUAL;
993                 c->floating = (floating == '-' ? false : true);
994                 c->transient = (transient == '-' ? false : true);
995                 c->fullscreen = (fullscreen == '-' ? false : true);
996                 c->urgent = (urgent == '-' ? false : true);
997                 c->locked = (locked == '-' ? false : true);
998                 if (c->uid > max_uid)
999                     max_uid = c->uid;
1000                 n->client = c;
1001                 if (len >= 2)
1002                     switch (line[len - 2]) {
1003                         case '*':
1004                             d->focus = n;
1005                             break;
1006                         case '~':
1007                             d->last_focus = n;
1008                             break;
1009                     }
1010             }
1011         }
1012         last_level = level;
1013     }
1014
1015     fclose(snapshot);
1016
1017     if (!aborted) {
1018         client_uid = max_uid + 1;
1019         for (monitor_t *m = mon_head; m != NULL; m = m->next)
1020             for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
1021                 for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n)) {
1022                     uint32_t values[] = {(focus_follows_pointer ? CLIENT_EVENT_MASK_FFP : CLIENT_EVENT_MASK)};
1023                     xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values);
1024                     if (n->client->floating) {
1025                         n->vacant = true;
1026                         update_vacant_state(n->parent);
1027                     }
1028                 }
1029         ewmh_update_current_desktop();
1030     }
1031 }