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