]> git.lizzy.rs Git - bspwm.git/blob - tree.c
c1e1595725f192779ad89711326b0ce90f43c0e2
[bspwm.git] / tree.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <math.h>
5 #include <xcb/xcb.h>
6 #include <xcb/xcb_event.h>
7 #include "settings.h"
8 #include "helpers.h"
9 #include "misc.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 void magnetise_tree(node_t *n, corner_t corner)
171 {
172     if (n == NULL || is_leaf(n)) 
173         return;
174
175     PUTS("magnetise tree");
176
177     switch (n->split_type) {
178         case TYPE_HORIZONTAL:
179             if (corner == TOP_LEFT || corner == TOP_RIGHT)
180                 change_split_ratio(n, CHANGE_DECREASE);
181             else
182                 change_split_ratio(n, CHANGE_INCREASE);
183             break;
184         case TYPE_VERTICAL:
185             if (corner == TOP_LEFT || corner == BOTTOM_LEFT)
186                 change_split_ratio(n, CHANGE_DECREASE);
187             else
188                 change_split_ratio(n, CHANGE_INCREASE);
189             break;
190         default:
191             break;
192     }
193
194     magnetise_tree(n->first_child, corner);
195     magnetise_tree(n->second_child, corner);
196 }
197
198 void dump_tree(desktop_t *d, node_t *n, char *rsp, int depth)
199 {
200     if (n == NULL)
201         return;
202
203     char line[MAXLEN];
204
205     for (int i = 0; i < depth; i++)
206         strcat(rsp, "  ");
207
208     if (is_leaf(n))
209         sprintf(line, "%s %X %s%s%s%s%s", n->client->class_name, n->client->window, (n->client->floating ? "f" : "-"), (n->client->transient ? "t" : "-"), (n->client->fullscreen ? "F" : "-"), (n->client->urgent ? "u" : "-"), (n->client->locked ? "l" : "-")); 
210     else
211         sprintf(line, "%s %.2f", (n->split_type == TYPE_HORIZONTAL ? "H" : "V"), n->split_ratio);
212
213     strcat(rsp, line);
214
215     if (n == d->focus)
216         strcat(rsp, " *\n");
217     else
218         strcat(rsp, "\n");
219
220     dump_tree(d, n->first_child, rsp, depth + 1);
221     dump_tree(d, n->second_child, rsp, depth + 1);
222 }
223
224 void refresh_current(void) {
225     if (desk->focus == NULL)
226         ewmh_update_active_window();
227     else
228         focus_node(desk, desk->focus, true);
229 }
230
231 void list_desktops(char *rsp)
232 {
233     desktop_t *d = desk_head;
234
235     while (d != NULL) {
236         strcat(rsp, d->name);
237         if (desk == d)
238             strcat(rsp, " @\n");
239         else
240             strcat(rsp, "\n");
241         dump_tree(d, d->root, rsp, 1);
242         d = d->next;
243     }
244 }
245
246 void update_root_dimensions(void)
247 {
248     root_rect.x = left_padding + window_gap;
249     root_rect.y = top_padding + window_gap;
250     root_rect.width = screen_width - (left_padding + right_padding + window_gap);
251     root_rect.height = screen_height - (top_padding + bottom_padding + window_gap);
252 }
253
254 void apply_layout(desktop_t *d, node_t *n, xcb_rectangle_t rect)
255 {
256     if (d == NULL || n == NULL)
257         return;
258
259     n->rectangle = rect;
260
261     if (is_leaf(n)) {
262         if (n->client->fullscreen)
263             return;
264
265         if (borderless_monocle && is_tiled(n->client) && d->layout == LAYOUT_MONOCLE)
266             n->client->border_width = 0;
267         else
268             n->client->border_width = border_width;
269
270         xcb_rectangle_t r;
271         if (is_tiled(n->client)) {
272             if (d->layout == LAYOUT_TILED)
273                 r = rect;
274             else if (d->layout == LAYOUT_MONOCLE)
275                 r = root_rect;
276             int bleed = window_gap + 2 * n->client->border_width;
277             r.width = (bleed < r.width ? r.width - bleed : 1);
278             r.height = (bleed < r.height ? r.height - bleed : 1);
279             n->client->tiled_rectangle = r;
280         } else {
281             r = n->client->floating_rectangle;
282         }
283
284         window_move_resize(n->client->window, r.x, r.y, r.width, r.height);
285         window_border_width(n->client->window, n->client->border_width);
286         window_draw_border(n, n == d->focus);
287
288         if (d->layout == LAYOUT_MONOCLE && n == d->focus)
289             window_raise(n->client->window);
290
291     } else {
292         xcb_rectangle_t first_rect;
293         xcb_rectangle_t second_rect;
294
295         if (n->first_child->vacant || n->second_child->vacant) {
296             first_rect = second_rect = rect;
297         } else {
298             unsigned int fence;
299             if (n->split_type == TYPE_VERTICAL) {
300                 fence = rect.width * n->split_ratio;
301                 first_rect = (xcb_rectangle_t) {rect.x, rect.y, fence, rect.height};
302                 second_rect = (xcb_rectangle_t) {rect.x + fence, rect.y, rect.width - fence, rect.height};
303
304             } else if (n->split_type == TYPE_HORIZONTAL) {
305                 fence = rect.height * n->split_ratio;
306                 first_rect = (xcb_rectangle_t) {rect.x, rect.y, rect.width, fence};
307                 second_rect = (xcb_rectangle_t) {rect.x, rect.y + fence, rect.width, rect.height - fence};
308             }
309         }
310
311         apply_layout(d, n->first_child, first_rect);
312         apply_layout(d, n->second_child, second_rect);
313     }
314 }
315
316 void insert_node(desktop_t *d, node_t *n)
317 {
318     if (d == NULL || n == NULL)
319         return;
320
321     PRINTF("insert node %X\n", n->client->window);
322
323     node_t *focus = d->focus;
324
325     if (focus == NULL) {
326         d->root = n;
327     } else {
328         node_t *dad = make_node();
329         node_t *fopar = focus->parent;
330         n->parent = dad;
331         n->born_as = split_mode;
332         switch (split_mode) {
333             case MODE_AUTOMATIC:
334                 if (fopar == NULL) {
335                     dad->first_child = n;
336                     dad->second_child = focus;
337                     if (focus->rectangle.width > focus->rectangle.height)
338                         dad->split_type = TYPE_VERTICAL;
339                     else
340                         dad->split_type = TYPE_HORIZONTAL;
341                     focus->parent = dad;
342                     d->root = dad;
343                 } else {
344                     node_t *grandpa = fopar->parent;
345                     dad->parent = grandpa;
346                     if (grandpa != NULL) {
347                         if (is_first_child(fopar))
348                             grandpa->first_child = dad;
349                         else
350                             grandpa->second_child = dad;
351                     } else {
352                         d->root = dad;
353                     }
354                     dad->split_type = fopar->split_type;
355                     dad->split_ratio = fopar->split_ratio;
356                     fopar->parent = dad;
357                     if (is_first_child(focus)) {
358                         dad->first_child = n;
359                         dad->second_child = fopar;
360                         rotate_tree(fopar, ROTATE_CLOCKWISE);
361                     } else {
362                         dad->first_child = fopar;
363                         dad->second_child = n;
364                         rotate_tree(fopar, ROTATE_COUNTER_CLOCKWISE);
365                     }
366                 }
367                 break;
368             case MODE_MANUAL:
369                 if (fopar != NULL) {
370                     if (is_first_child(focus))
371                         fopar->first_child = dad;
372                     else
373                         fopar->second_child = dad;
374                 }
375                 dad->split_ratio = focus->split_ratio;
376                 dad->parent = fopar;
377                 focus->parent = dad;
378                 switch (split_dir) {
379                     case DIR_LEFT:
380                         dad->split_type = TYPE_VERTICAL;
381                         dad->first_child = n;
382                         dad->second_child = focus;
383                         break;
384                     case DIR_RIGHT:
385                         dad->split_type = TYPE_VERTICAL;
386                         dad->first_child = focus;
387                         dad->second_child = n;
388                         break;
389                     case DIR_UP:
390                         dad->split_type = TYPE_HORIZONTAL;
391                         dad->first_child = n;
392                         dad->second_child = focus;
393                         break;
394                     case DIR_DOWN:
395                         dad->split_type = TYPE_HORIZONTAL;
396                         dad->first_child = focus;
397                         dad->second_child = n;
398                         break;
399                 }
400                 if (d->root == focus)
401                     d->root = dad;
402                 split_mode = MODE_AUTOMATIC;
403                 break;
404         }
405         if (focus->vacant)
406             update_vacant_state(fopar);
407     }
408 }
409
410 void focus_node(desktop_t *d, node_t *n, bool is_mapped)
411 {
412     if (n == NULL)
413         return;
414
415     PRINTF("focus node %X\n", n->client->window);
416
417     split_mode = MODE_AUTOMATIC;
418     n->client->urgent = false;
419
420     if (is_mapped) {
421         if (d->focus != n) {
422             window_draw_border(d->focus, false);
423             window_draw_border(n, true);
424         }
425         xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, n->client->window, XCB_CURRENT_TIME);
426     }
427
428     if (!is_tiled(n->client) || d->layout == LAYOUT_MONOCLE)
429         window_raise(n->client->window);
430     else if (is_tiled(n->client))
431         window_lower(n->client->window);
432
433     if (d->focus != n) {
434         d->last_focus = d->focus;
435         d->focus = n;
436     }
437
438     ewmh_update_active_window();
439 }
440
441 void update_current(void)
442 {
443     if (desk->focus == NULL)
444         ewmh_update_active_window();
445     else
446         focus_node(desk, desk->focus, true);
447 }
448
449 void unlink_node(desktop_t *d, node_t *n)
450 {
451     if (d == NULL || n == NULL)
452         return;
453
454     PRINTF("unlink node %X\n", n->client->window);
455
456     node_t *p = n->parent;
457
458     if (p == NULL) {
459         d->root = NULL;
460         d->focus = NULL;
461         d->last_focus = NULL;
462     } else {
463         node_t *b;
464         node_t *g = p->parent;
465         bool n_first_child = is_first_child(n);
466         if (n_first_child) {
467             b = p->second_child;
468             if (n->born_as == MODE_AUTOMATIC)
469                 rotate_tree(b, ROTATE_COUNTER_CLOCKWISE);
470         } else {
471             b = p->first_child;
472             if (n->born_as == MODE_AUTOMATIC)
473                 rotate_tree(b, ROTATE_CLOCKWISE);
474         }
475         b->parent = g;
476         if (g != NULL) {
477             if (is_first_child(p))
478                 g->first_child = b;
479             else
480                 g->second_child = b;
481         } else {
482             d->root = b;
483         }
484
485         n->parent = NULL;
486         free(p);
487
488         if (n == d->last_focus) {
489             d->last_focus = NULL;
490         } else if (n == d->focus) {
491             if (d->last_focus != NULL)
492                 d->focus = d->last_focus;
493             else
494                 d->focus = (n_first_child ? first_extrema(b) : second_extrema(b));
495             d->last_focus = NULL;
496         }
497
498         update_vacant_state(b->parent);
499     }
500 }
501
502 void remove_node(desktop_t *d, node_t *n)
503 {
504     if (d == NULL || n == NULL)
505         return;
506
507     PRINTF("remove node %X\n", n->client->window);
508
509     unlink_node(d, n);
510     free(n->client);
511     free(n);
512
513     num_clients--;
514     ewmh_update_client_list();
515
516     if (desk == d)
517         update_current();
518 }
519
520 void swap_nodes(node_t *n1, node_t *n2)
521 {
522     if (n1 == NULL || n2 == NULL || n1 == n2)
523         return;
524
525     PUTS("swap nodes");
526
527     /* (n1 and n2 are leaves) */
528     node_t *pn1 = n1->parent;
529     node_t *pn2 = n2->parent;
530     bool n1_first_child = is_first_child(n1);
531     bool n2_first_child = is_first_child(n2);
532
533     if (pn1 != NULL) {
534         if (n1_first_child)
535             pn1->first_child = n2;
536         else
537             pn1->second_child = n2;
538     }
539
540     if (pn2 != NULL) {
541         if (n2_first_child)
542             pn2->first_child = n1;
543         else
544             pn2->second_child = n1;
545     }
546
547     n1->parent = pn2;
548     n2->parent = pn1;
549
550     if (n1->vacant != n2->vacant) {
551         update_vacant_state(n1->parent);
552         update_vacant_state(n2->parent);
553     }
554 }
555
556 void transfer_node(desktop_t *ds, desktop_t *dd, node_t *n)
557 {
558     if (n == NULL || ds == NULL || dd == NULL || dd == ds)
559         return;
560
561     PRINTF("transfer node %X\n", n->client->window);
562
563     unlink_node(ds, n);
564
565     if (ds == desk) {
566         window_hide(n->client->window);
567     }
568
569     insert_node(dd, n);
570
571     if (dd == desk) {
572         window_show(n->client->window);
573         focus_node(dd, n, true);
574     } else {
575         focus_node(dd, n, false);
576     }
577
578     if (ds == desk || dd == desk)
579         update_current();
580 }
581
582 void select_desktop(desktop_t *d)
583 {
584     if (d == NULL || d == desk)
585         return;
586
587     PRINTF("select desktop %s\n", d->name);
588
589     node_t *n = first_extrema(d->root);
590
591     while (n != NULL) {
592         window_show(n->client->window);
593         n = next_leaf(n);
594     }
595
596     n = first_extrema(desk->root);
597
598     while (n != NULL) {
599         window_hide(n->client->window);
600         n = next_leaf(n);
601     }
602
603     last_desk = desk;
604     desk = d;
605
606     update_current();
607     ewmh_update_current_desktop();
608 }
609
610 void cycle_desktop(cycle_dir_t dir)
611 {
612     if (dir == CYCLE_NEXT)
613         select_desktop((desk->next == NULL ? desk_head : desk->next));
614     else if (dir == CYCLE_PREV)
615         select_desktop((desk->prev == NULL ? desk_tail : desk->prev));
616 }
617
618 void cycle_leaf(desktop_t *d, node_t *n, cycle_dir_t dir, skip_client_t skip)
619 {
620     if (n == NULL)
621         return;
622
623     PUTS("cycle leaf");
624
625     node_t *f = (dir == CYCLE_PREV ? prev_leaf(n) : next_leaf(n));
626     if (f == NULL)
627         f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root));
628
629     while (f != n) {
630         bool tiled = is_tiled(f->client);
631         if (skip == SKIP_NONE || (skip == SKIP_TILED && !tiled) || (skip == SKIP_FLOATING && tiled)
632                 || (skip == SKIP_CLASS_DIFFER && strcmp(f->client->class_name, n->client->class_name) == 0)
633                 || (skip == SKIP_CLASS_EQUAL && strcmp(f->client->class_name, n->client->class_name) != 0)) {
634             focus_node(d, f, true);
635             return;
636         }
637         f = (dir == CYCLE_PREV ? prev_leaf(f) : next_leaf(f));
638         if (f == NULL)
639             f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root));
640     }
641 }
642
643 void update_vacant_state(node_t *n)
644 {
645     if (n == NULL)
646         return;
647
648     PUTS("update vacant state");
649
650     /* n is not a leaf */
651     node_t *p = n;
652
653     while (p != NULL) {
654         p->vacant = (p->first_child->vacant && p->second_child->vacant);
655         p = p->parent;
656     }
657 }
658
659 desktop_t *find_desktop(char *name)
660 {
661     desktop_t *d = desk_head;
662     while (d != NULL) {
663         if (strcmp(d->name, name) == 0)
664             return d;
665         d = d->next;
666     }
667     return NULL;
668 }
669
670 void add_desktop(char *name)
671 {
672     desktop_t *d = make_desktop(name);
673     desk_tail->next = d;
674     d->prev = desk_tail;
675     desk_tail = d;
676     num_desktops++;
677     ewmh_update_number_of_desktops();
678     ewmh_update_desktop_names();
679 }