]> git.lizzy.rs Git - bspwm.git/blob - window.c
Asynchronously parse rule command output
[bspwm.git] / window.c
1 /* * Copyright (c) 2012-2013 Bastien Dejean
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification,
5  * are permitted provided that the following conditions are met:
6  *
7  *  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation and/or
11  * other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
17  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include "bspwm.h"
28 #include "ewmh.h"
29 #include "monitor.h"
30 #include "query.h"
31 #include "rule.h"
32 #include "settings.h"
33 #include "stack.h"
34 #include "tree.h"
35 #include "window.h"
36
37 void schedule_window(xcb_window_t win)
38 {
39     coordinates_t loc;
40     uint8_t override_redirect = 0;
41     xcb_get_window_attributes_reply_t *wa = xcb_get_window_attributes_reply(dpy, xcb_get_window_attributes(dpy, win), NULL);
42
43     if (wa != NULL) {
44         override_redirect = wa->override_redirect;
45         free(wa);
46     }
47
48     if (override_redirect || locate_window(win, &loc))
49         return;
50
51     rule_consequence_t *csq = make_rule_conquence();
52     apply_rules(win, csq);
53     if (!schedule_rules(win, csq)) {
54         manage_window(win, csq, -1);
55         free(csq);
56     }
57 }
58
59 void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd)
60 {
61     monitor_t *m = mon;
62     desktop_t *d = mon->desk;
63
64     parse_rule_consequence(fd, csq, &m, &d);
65
66     if (csq->lower)
67         window_lower(win);
68
69     if (!csq->manage) {
70         disable_floating_atom(win);
71         window_show(win);
72         return;
73     }
74
75     PRINTF("manage %X\n", win);
76
77     client_t *c = make_client(win);
78     update_floating_rectangle(c);
79     translate_client(monitor_from_client(c), m, c);
80     if (csq->center)
81         window_center(m, c);
82     c->frame = csq->frame;
83
84     xcb_icccm_get_wm_class_reply_t reply;
85     if (xcb_icccm_get_wm_class_reply(dpy, xcb_icccm_get_wm_class(dpy, win), &reply, NULL) == 1) {
86         snprintf(c->class_name, sizeof(c->class_name), "%s", reply.class_name);
87         xcb_icccm_get_wm_class_reply_wipe(&reply);
88     }
89
90     csq->floating = csq->floating || d->floating;
91
92     node_t *n = make_node();
93     n->client = c;
94
95     insert_node(m, d, n, d->focus);
96
97     disable_floating_atom(c->window);
98     set_floating(n, csq->floating);
99     set_locked(m, d, n, csq->locked);
100     set_sticky(m, d, n, csq->sticky);
101     set_private(m, d, n, csq->private);
102
103     if (d->focus != NULL && d->focus->client->fullscreen)
104         set_fullscreen(d->focus, false);
105
106     set_fullscreen(n, csq->fullscreen);
107     c->transient = csq->transient;
108
109     arrange(m, d);
110
111     bool give_focus = (csq->focus && (d == mon->desk || csq->follow));
112
113     if (give_focus)
114         focus_node(m, d, n);
115     else if (csq->focus)
116         pseudo_focus(d, n);
117     else
118         stack(n, STACK_ABOVE);
119
120     uint32_t values[] = {get_event_mask(n->client)};
121     xcb_change_window_attributes(dpy, c->window, XCB_CW_EVENT_MASK, values);
122
123     if (visible) {
124         if (d == m->desk)
125             window_show(n->client->window);
126         else
127             window_hide(n->client->window);
128     }
129
130     /* the same function is already called in `focus_node` but has no effects on unmapped windows */
131     if (give_focus)
132         xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, win, XCB_CURRENT_TIME);
133
134     num_clients++;
135     ewmh_set_wm_desktop(n, d);
136     ewmh_update_client_list();
137 }
138
139 void unmanage_window(xcb_window_t win)
140 {
141     coordinates_t loc;
142     if (locate_window(win, &loc)) {
143         PRINTF("unmanage %X\n", win);
144         remove_node(loc.monitor, loc.desktop, loc.node);
145         arrange(loc.monitor, loc.desktop);
146     } else {
147         for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
148             if (pr->win == win) {
149                 remove_pending_rule(pr);
150                 return;
151             }
152         }
153     }
154 }
155
156 void window_draw_border(node_t *n, bool focused_window, bool focused_monitor)
157 {
158     if (n == NULL || (n->client->border_width < 1 && !n->client->frame)) {
159         return;
160     }
161
162     if (n->client->frame) {
163         draw_frame_background(n, focused_window, focused_monitor);
164         return;
165     }
166
167     xcb_window_t win = n->client->window;
168     uint32_t border_color_pxl = get_border_color(n->client, focused_window, focused_monitor);
169
170     if (n->split_mode == MODE_AUTOMATIC) {
171         xcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXEL, &border_color_pxl);
172     } else {
173         unsigned int border_width = n->client->border_width;
174         uint32_t presel_border_color_pxl;
175         get_color(presel_border_color, win, &presel_border_color_pxl);
176
177         xcb_rectangle_t actual_rectangle = get_rectangle(n->client);
178
179         uint16_t width = actual_rectangle.width;
180         uint16_t height = actual_rectangle.height;
181
182         uint16_t full_width = width + 2 * border_width;
183         uint16_t full_height = height + 2 * border_width;
184
185         xcb_rectangle_t border_rectangles[] =
186         {
187             { width, 0, 2 * border_width, height + 2 * border_width },
188             { 0, height, width + 2 * border_width, 2 * border_width }
189         };
190
191         xcb_rectangle_t *presel_rectangles;
192
193         uint8_t win_depth = root_depth;
194         xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, win), NULL);
195         if (geo != NULL)
196             win_depth = geo->depth;
197         free(geo);
198
199         xcb_pixmap_t pixmap = xcb_generate_id(dpy);
200         xcb_create_pixmap(dpy, win_depth, pixmap, win, full_width, full_height);
201
202         xcb_gcontext_t gc = xcb_generate_id(dpy);
203         xcb_create_gc(dpy, gc, pixmap, 0, NULL);
204
205         xcb_change_gc(dpy, gc, XCB_GC_FOREGROUND, &border_color_pxl);
206         xcb_poly_fill_rectangle(dpy, pixmap, gc, LENGTH(border_rectangles), border_rectangles);
207
208         uint16_t fence = (int16_t) (n->split_ratio * ((n->split_dir == DIR_UP || n->split_dir == DIR_DOWN) ? height : width));
209         presel_rectangles = malloc(2 * sizeof(xcb_rectangle_t));
210         switch (n->split_dir) {
211             case DIR_UP:
212                 presel_rectangles[0] = (xcb_rectangle_t) {width, 0, 2 * border_width, fence};
213                 presel_rectangles[1] = (xcb_rectangle_t) {0, height + border_width, full_width, border_width};
214                 break;
215             case DIR_DOWN:
216                 presel_rectangles[0] = (xcb_rectangle_t) {width, fence + 1, 2 * border_width, height + border_width - (fence + 1)};
217                 presel_rectangles[1] = (xcb_rectangle_t) {0, height, full_width, border_width};
218                 break;
219             case DIR_LEFT:
220                 presel_rectangles[0] = (xcb_rectangle_t) {0, height, fence, 2 * border_width};
221                 presel_rectangles[1] = (xcb_rectangle_t) {width + border_width, 0, border_width, full_height};
222                 break;
223             case DIR_RIGHT:
224                 presel_rectangles[0] = (xcb_rectangle_t) {fence + 1, height, width + border_width - (fence + 1), 2 * border_width};
225                 presel_rectangles[1] = (xcb_rectangle_t) {width, 0, border_width, full_height};
226                 break;
227         }
228         xcb_change_gc(dpy, gc, XCB_GC_FOREGROUND, &presel_border_color_pxl);
229         xcb_poly_fill_rectangle(dpy, pixmap, gc, 2, presel_rectangles);
230         xcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXMAP, &pixmap);
231         free(presel_rectangles);
232         xcb_free_gc(dpy, gc);
233         xcb_free_pixmap(dpy, pixmap);
234     }
235 }
236
237 void draw_frame_background(node_t *n, bool focused_window, bool focused_monitor)
238 {
239     if (n == NULL)
240         return;
241
242     xcb_window_t win = n->client->window;
243     uint32_t border_color_pxl = get_border_color(n->client, focused_window, focused_monitor);
244     uint32_t opacity = (focused_window ? (focused_monitor ? focused_frame_opacity : active_frame_opacity) : normal_frame_opacity) * 0xffffffff;
245     xcb_change_property(dpy, XCB_PROP_MODE_REPLACE, win, _NET_WM_WINDOW_OPACITY, XCB_ATOM_CARDINAL, 32, 1, &opacity);
246     uint8_t win_depth = root_depth;
247     xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, win), NULL);
248     if (geo != NULL)
249         win_depth = geo->depth;
250     free(geo);
251     xcb_rectangle_t rectangle = get_rectangle(n->client);
252     rectangle.x = rectangle.y = 0;
253     uint16_t width = rectangle.width;
254     uint16_t height = rectangle.height;
255     xcb_pixmap_t pixmap = xcb_generate_id(dpy);
256     xcb_create_pixmap(dpy, win_depth, pixmap, win, width, height);
257     xcb_gcontext_t gc = xcb_generate_id(dpy);
258     xcb_create_gc(dpy, gc, pixmap, 0, NULL);
259     xcb_change_gc(dpy, gc, XCB_GC_FOREGROUND, &border_color_pxl);
260     xcb_poly_fill_rectangle(dpy, pixmap, gc, 1, &rectangle);
261     if (n->split_mode == MODE_MANUAL) {
262         xcb_rectangle_t presel_rectangle = rectangle;
263         uint32_t presel_border_color_pxl;
264         get_color(presel_border_color, win, &presel_border_color_pxl);
265         xcb_change_gc(dpy, gc, XCB_GC_FOREGROUND, &presel_border_color_pxl);
266         switch (n->split_dir) {
267             case DIR_UP:
268                 presel_rectangle.height = n->split_ratio * rectangle.height;
269                 break;
270             case DIR_RIGHT:
271                 presel_rectangle.width = (1 - n->split_ratio) * rectangle.width;
272                 presel_rectangle.x = rectangle.width - presel_rectangle.width;
273                 break;
274             case DIR_DOWN:
275                 presel_rectangle.height = (1 - n->split_ratio) * rectangle.height;
276                 presel_rectangle.y = rectangle.height - presel_rectangle.height;
277                 break;
278             case DIR_LEFT:
279                 presel_rectangle.width = n->split_ratio * rectangle.width;
280                 break;
281         }
282         xcb_poly_fill_rectangle(dpy, pixmap, gc, 1, &presel_rectangle);
283     }
284     xcb_copy_area(dpy, pixmap, win, gc, 0, 0, 0, 0, width, height);
285     xcb_free_gc(dpy, gc);
286     xcb_free_pixmap(dpy, pixmap);
287 }
288
289 pointer_state_t *make_pointer_state(void)
290 {
291     pointer_state_t *p = malloc(sizeof(pointer_state_t));
292     p->monitor = NULL;
293     p->desktop = NULL;
294     p->node = p->vertical_fence = p->horizontal_fence = NULL;
295     p->client = NULL;
296     p->window = XCB_NONE;
297     p->action = ACTION_NONE;
298     return p;
299 }
300
301 bool contains(xcb_rectangle_t a, xcb_rectangle_t b)
302 {
303     return (a.x <= b.x && (a.x + a.width) >= (b.x + b.width)
304             && a.y <= b.y && (a.y + a.height) >= (b.y + b.height));
305 }
306
307 xcb_rectangle_t get_rectangle(client_t *c)
308 {
309     if (is_tiled(c))
310         return c->tiled_rectangle;
311     else
312         return c->floating_rectangle;
313 }
314
315 void get_side_handle(client_t *c, direction_t dir, xcb_point_t *pt)
316 {
317     xcb_rectangle_t rect = get_rectangle(c);
318     switch (dir) {
319         case DIR_RIGHT:
320             pt->x = rect.x + rect.width;
321             pt->y = rect.y + (rect.height / 2);
322             break;
323         case DIR_DOWN:
324             pt->x = rect.x + (rect.width / 2);
325             pt->y = rect.y + rect.height;
326             break;
327         case DIR_LEFT:
328             pt->x = rect.x;
329             pt->y = rect.y + (rect.height / 2);
330             break;
331         case DIR_UP:
332             pt->x = rect.x + (rect.width / 2);
333             pt->y = rect.y;
334             break;
335     }
336 }
337
338 void adopt_orphans(void)
339 {
340     xcb_query_tree_reply_t *qtr = xcb_query_tree_reply(dpy, xcb_query_tree(dpy, root), NULL);
341     if (qtr == NULL)
342         return;
343
344     PUTS("adopt orphans");
345
346     int len = xcb_query_tree_children_length(qtr);
347     xcb_window_t *wins = xcb_query_tree_children(qtr);
348     for (int i = 0; i < len; i++) {
349         uint32_t idx;
350         xcb_window_t win = wins[i];
351         if (xcb_ewmh_get_wm_desktop_reply(ewmh, xcb_ewmh_get_wm_desktop(ewmh, win), &idx, NULL) == 1)
352             schedule_window(win);
353     }
354
355     free(qtr);
356 }
357
358 void window_close(node_t *n)
359 {
360     if (n == NULL || n->client->locked)
361         return;
362
363     PRINTF("close window %X\n", n->client->window);
364
365     send_client_message(n->client->window, ewmh->WM_PROTOCOLS, WM_DELETE_WINDOW);
366 }
367
368 void window_kill(monitor_t *m, desktop_t *d, node_t *n)
369 {
370     if (n == NULL)
371         return;
372
373     xcb_window_t win = n->client->window;
374     PRINTF("kill window %X\n", win);
375
376     xcb_kill_client(dpy, win);
377     remove_node(m, d, n);
378 }
379
380 void set_fullscreen(node_t *n, bool value)
381 {
382     if (n == NULL || n->client->frame || n->client->fullscreen == value)
383         return;
384
385     client_t *c = n->client;
386
387     PRINTF("fullscreen %X: %s\n", c->window, BOOLSTR(value));
388
389     c->fullscreen = value;
390     if (value)
391         ewmh_wm_state_add(c, ewmh->_NET_WM_STATE_FULLSCREEN);
392     else
393         ewmh_wm_state_remove(c, ewmh->_NET_WM_STATE_FULLSCREEN);
394     stack(n, STACK_ABOVE);
395 }
396
397 void set_floating(node_t *n, bool value)
398 {
399     if (n == NULL || n->client->transient || n->client->fullscreen || n->client->frame || n->client->floating == value)
400         return;
401
402     PRINTF("floating %X: %s\n", n->client->window, BOOLSTR(value));
403
404     n->split_mode = MODE_AUTOMATIC;
405     client_t *c = n->client;
406     c->floating = n->vacant = value;
407     update_vacant_state(n->parent);
408
409     if (value) {
410         enable_floating_atom(c->window);
411         unrotate_brother(n);
412     } else {
413         disable_floating_atom(c->window);
414         rotate_brother(n);
415     }
416
417     stack(n, STACK_ABOVE);
418 }
419
420 void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value)
421 {
422     if (n == NULL || n->client->locked == value)
423         return;
424
425     client_t *c = n->client;
426
427     PRINTF("set locked %X: %s\n", c->window, BOOLSTR(value));
428
429     c->locked = value;
430     window_draw_border(n, d->focus == n, m == mon);
431 }
432
433 void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value)
434 {
435     if (n == NULL || n->client->sticky == value)
436         return;
437
438     client_t *c = n->client;
439
440     PRINTF("set sticky %X: %s\n", c->window, BOOLSTR(value));
441
442     if (d != m->desk)
443         transfer_node(m, d, n, m, m->desk, m->desk->focus);
444
445     c->sticky = value;
446     if (value) {
447         ewmh_wm_state_add(c, ewmh->_NET_WM_STATE_STICKY);
448         m->num_sticky++;
449     } else {
450         ewmh_wm_state_remove(c, ewmh->_NET_WM_STATE_STICKY);
451         m->num_sticky--;
452     }
453
454     window_draw_border(n, d->focus == n, m == mon);
455 }
456
457 void set_private(monitor_t *m, desktop_t *d, node_t *n, bool value)
458 {
459     if (n == NULL || n->client->private == value)
460         return;
461
462     client_t *c = n->client;
463
464     PRINTF("set private %X: %s\n", c->window, BOOLSTR(value));
465
466     c->private = value;
467     update_privacy_level(n, value);
468     window_draw_border(n, d->focus == n, m == mon);
469 }
470
471 void set_urgency(monitor_t *m, desktop_t *d, node_t *n, bool value)
472 {
473     if (value && mon->desk->focus == n)
474         return;
475     n->client->urgent = value;
476     window_draw_border(n, d->focus == n, m == mon);
477     put_status();
478 }
479
480 void set_floating_atom(xcb_window_t win, uint32_t value)
481 {
482     if (!apply_floating_atom)
483         return;
484     set_atom(win, _BSPWM_FLOATING_WINDOW, value);
485 }
486
487 void enable_floating_atom(xcb_window_t win)
488 {
489     set_floating_atom(win, 1);
490 }
491
492 void disable_floating_atom(xcb_window_t win)
493 {
494     set_floating_atom(win, 0);
495 }
496
497 uint32_t get_border_color(client_t *c, bool focused_window, bool focused_monitor)
498 {
499     if (c == NULL)
500         return 0;
501
502     uint32_t pxl = 0;
503
504     if (focused_monitor && focused_window) {
505         if (c->locked)
506             get_color(focused_locked_border_color, c->window, &pxl);
507         else if (c->sticky)
508             get_color(focused_sticky_border_color, c->window, &pxl);
509         else if (c->private)
510             get_color(focused_private_border_color, c->window, &pxl);
511         else
512             get_color(focused_border_color, c->window, &pxl);
513     } else if (focused_window) {
514         if (c->urgent)
515             get_color(urgent_border_color, c->window, &pxl);
516         else if (c->locked)
517             get_color(active_locked_border_color, c->window, &pxl);
518         else if (c->sticky)
519             get_color(active_sticky_border_color, c->window, &pxl);
520         else if (c->private)
521             get_color(active_private_border_color, c->window, &pxl);
522         else
523             get_color(active_border_color, c->window, &pxl);
524     } else {
525         if (c->urgent)
526             get_color(urgent_border_color, c->window, &pxl);
527         else if (c->locked)
528             get_color(normal_locked_border_color, c->window, &pxl);
529         else if (c->sticky)
530             get_color(normal_sticky_border_color, c->window, &pxl);
531         else if (c->private)
532             get_color(normal_private_border_color, c->window, &pxl);
533         else
534             get_color(normal_border_color, c->window, &pxl);
535     }
536
537     return pxl;
538 }
539
540 void update_floating_rectangle(client_t *c)
541 {
542     xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, c->window), NULL);
543
544     if (geo != NULL)
545         c->floating_rectangle = (xcb_rectangle_t) {geo->x, geo->y, geo->width, geo->height};
546     else
547         c->floating_rectangle = (xcb_rectangle_t) {0, 0, 32, 24};
548
549     free(geo);
550 }
551
552
553 void query_pointer(xcb_window_t *win, xcb_point_t *pt)
554 {
555     window_lower(motion_recorder);
556
557     xcb_query_pointer_reply_t *qpr = xcb_query_pointer_reply(dpy, xcb_query_pointer(dpy, root), NULL);
558
559     if (qpr != NULL) {
560         if (win != NULL)
561             *win = qpr->child;
562         if (pt != NULL)
563             *pt = (xcb_point_t) {qpr->root_x, qpr->root_y};
564         free(qpr);
565     }
566
567     window_raise(motion_recorder);
568 }
569
570 bool window_focus(xcb_window_t win)
571 {
572     coordinates_t loc;
573     if (locate_window(win, &loc)) {
574         if (loc.node != mon->desk->focus)
575             focus_node(loc.monitor, loc.desktop, loc.node);
576         return true;
577     }
578     return false;
579 }
580
581 void window_border_width(xcb_window_t win, uint32_t bw)
582 {
583     uint32_t values[] = {bw};
584     xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_BORDER_WIDTH, values);
585 }
586
587 void window_move(xcb_window_t win, int16_t x, int16_t y)
588 {
589     uint32_t values[] = {x, y};
590     xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_X_Y, values);
591 }
592
593 void window_resize(xcb_window_t win, uint16_t w, uint16_t h)
594 {
595     uint32_t values[] = {w, h};
596     xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_WIDTH_HEIGHT, values);
597 }
598
599 void window_move_resize(xcb_window_t win, int16_t x, int16_t y, uint16_t w, uint16_t h)
600 {
601     uint32_t values[] = {x, y, w, h};
602     xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT, values);
603 }
604
605 void window_raise(xcb_window_t win)
606 {
607     uint32_t values[] = {XCB_STACK_MODE_ABOVE};
608     xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_STACK_MODE, values);
609 }
610
611 void window_center(monitor_t *m, client_t *c)
612 {
613     xcb_rectangle_t *r = &c->floating_rectangle;
614     xcb_rectangle_t a = m->rectangle;
615     if (r->width >= a.width)
616         r->x = a.x;
617     else
618         r->x = a.x + (a.width - r->width) / 2;
619     if (r->height >= a.height)
620         r->y = a.y;
621     else
622         r->y = a.y + (a.height - r->height) / 2;
623 }
624
625 void window_stack(xcb_window_t w1, xcb_window_t w2, uint32_t mode)
626 {
627     if (w2 == XCB_NONE)
628         return;
629     uint16_t mask = XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE;
630     uint32_t values[] = {w2, mode};
631     xcb_configure_window(dpy, w1, mask, values);
632 }
633
634 void window_above(xcb_window_t w1, xcb_window_t w2)
635 {
636     window_stack(w1, w2, XCB_STACK_MODE_ABOVE);
637 }
638
639 void window_below(xcb_window_t w1, xcb_window_t w2)
640 {
641     window_stack(w1, w2, XCB_STACK_MODE_BELOW);
642 }
643
644 void window_lower(xcb_window_t win)
645 {
646     uint32_t values[] = {XCB_STACK_MODE_BELOW};
647     xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_STACK_MODE, values);
648 }
649
650 void window_set_visibility(xcb_window_t win, bool visible)
651 {
652     uint32_t values_off[] = {ROOT_EVENT_MASK & ~XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY};
653     uint32_t values_on[] = {ROOT_EVENT_MASK};
654     xcb_change_window_attributes(dpy, root, XCB_CW_EVENT_MASK, values_off);
655     if (visible)
656         xcb_map_window(dpy, win);
657     else
658         xcb_unmap_window(dpy, win);
659     xcb_change_window_attributes(dpy, root, XCB_CW_EVENT_MASK, values_on);
660 }
661
662 void window_hide(xcb_window_t win)
663 {
664     PRINTF("window hide %X\n", win);
665     window_set_visibility(win, false);
666 }
667
668 void window_show(xcb_window_t win)
669 {
670     PRINTF("window show %X\n", win);
671     window_set_visibility(win, true);
672 }
673
674 void toggle_visibility(void)
675 {
676     visible = !visible;
677     if (!visible)
678         clear_input_focus();
679     for (monitor_t *m = mon_head; m != NULL; m = m->next)
680         for (node_t *n = first_extrema(m->desk->root); n != NULL; n = next_leaf(n, m->desk->root))
681             window_set_visibility(n->client->window, visible);
682     if (visible)
683         update_input_focus();
684 }
685
686 void enable_motion_recorder(void)
687 {
688     PUTS("enable motion recorder");
689     window_raise(motion_recorder);
690     window_show(motion_recorder);
691 }
692
693 void disable_motion_recorder(void)
694 {
695     PUTS("disable motion recorder");
696     window_hide(motion_recorder);
697 }
698
699 void update_motion_recorder(void)
700 {
701     xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, root), NULL);
702
703     if (geo != NULL) {
704         window_resize(motion_recorder, geo->width, geo->height);
705         PRINTF("update motion recorder size: %ux%u\n", geo->width, geo->height);
706     }
707
708     free(geo);
709 }
710
711 void update_input_focus(void)
712 {
713     set_input_focus(mon->desk->focus);
714 }
715
716 void set_input_focus(node_t *n)
717 {
718     if (n == NULL) {
719         clear_input_focus();
720     } else {
721         if (n->client->icccm_focus)
722             send_client_message(n->client->window, ewmh->WM_PROTOCOLS, WM_TAKE_FOCUS);
723         xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, n->client->window, XCB_CURRENT_TIME);
724     }
725 }
726
727 void clear_input_focus(void)
728 {
729     xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
730 }
731
732 void center_pointer(monitor_t *m)
733 {
734     int16_t cx = m->rectangle.x + m->rectangle.width / 2;
735     int16_t cy = m->rectangle.y + m->rectangle.height / 2;
736     window_lower(motion_recorder);
737     xcb_warp_pointer(dpy, XCB_NONE, root, 0, 0, 0, 0, cx, cy);
738     window_raise(motion_recorder);
739 }
740
741 void get_atom(char *name, xcb_atom_t *atom)
742 {
743     xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(dpy, xcb_intern_atom(dpy, 0, strlen(name), name), NULL);
744     if (reply != NULL)
745         *atom = reply->atom;
746     else
747         *atom = XCB_NONE;
748     free(reply);
749 }
750
751 void set_atom(xcb_window_t win, xcb_atom_t atom, uint32_t value)
752 {
753     xcb_change_property(dpy, XCB_PROP_MODE_REPLACE, win, atom, XCB_ATOM_CARDINAL, 32, 1, &value);
754 }
755
756 bool has_proto(xcb_atom_t atom, xcb_icccm_get_wm_protocols_reply_t *protocols)
757 {
758     for (uint32_t i = 0; i < protocols->atoms_len; i++)
759         if (protocols->atoms[i] == atom)
760             return true;
761     return false;
762 }
763
764 void send_client_message(xcb_window_t win, xcb_atom_t property, xcb_atom_t value)
765 {
766     xcb_client_message_event_t e;
767
768     e.response_type = XCB_CLIENT_MESSAGE;
769     e.window = win;
770     e.format = 32;
771     e.sequence = 0;
772     e.type = property;
773     e.data.data32[0] = value;
774     e.data.data32[1] = XCB_CURRENT_TIME;
775
776     xcb_send_event(dpy, false, win, XCB_EVENT_MASK_NO_EVENT, (char *) &e);
777 }
778
779 uint32_t get_event_mask(client_t *c)
780 {
781     return CLIENT_EVENT_MASK | (c->frame ? XCB_EVENT_MASK_EXPOSURE : 0) | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0);
782 }