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