]> git.lizzy.rs Git - bspwm.git/blob - window.c
Fail verbosely
[bspwm.git] / window.c
1 /* Copyright (c) 2012, Bastien Dejean
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright notice, this
8  *    list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright notice,
10  *    this list of conditions and the following disclaimer in the documentation
11  *    and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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
20  * ON 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 <stdio.h>
26 #include <stdlib.h>
27 #include <stdbool.h>
28 #include <string.h>
29 #include "bspwm.h"
30 #include "ewmh.h"
31 #include "monitor.h"
32 #include "query.h"
33 #include "rule.h"
34 #include "settings.h"
35 #include "geometry.h"
36 #include "stack.h"
37 #include "tree.h"
38 #include "parse.h"
39 #include "window.h"
40
41 void schedule_window(xcb_window_t win)
42 {
43         coordinates_t loc;
44         uint8_t override_redirect = 0;
45         xcb_get_window_attributes_reply_t *wa = xcb_get_window_attributes_reply(dpy, xcb_get_window_attributes(dpy, win), NULL);
46
47         if (wa != NULL) {
48                 override_redirect = wa->override_redirect;
49                 free(wa);
50         }
51
52         if (override_redirect || locate_window(win, &loc)) {
53                 return;
54         }
55
56         /* ignore pending windows */
57         for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
58                 if (pr->win == win) {
59                         return;
60                 }
61         }
62
63         rule_consequence_t *csq = make_rule_conquence();
64         apply_rules(win, csq);
65         if (!schedule_rules(win, csq)) {
66                 manage_window(win, csq, -1);
67                 free(csq);
68         }
69 }
70
71 void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd)
72 {
73         monitor_t *m = mon;
74         desktop_t *d = mon->desk;
75         node_t *f = mon->desk->focus;
76
77         parse_rule_consequence(fd, csq);
78
79         if (!csq->manage) {
80                 free(csq->layer);
81                 free(csq->state);
82                 window_show(win);
83                 return;
84         }
85
86         if (csq->node_desc[0] != '\0') {
87                 coordinates_t ref = {m, d, f};
88                 coordinates_t trg = {NULL, NULL, NULL};
89                 if (node_from_desc(csq->node_desc, &ref, &trg) == SELECTOR_OK) {
90                         m = trg.monitor;
91                         d = trg.desktop;
92                         f = trg.node;
93                 }
94         } else if (csq->desktop_desc[0] != '\0') {
95                 coordinates_t ref = {m, d, NULL};
96                 coordinates_t trg = {NULL, NULL, NULL};
97                 if (desktop_from_desc(csq->desktop_desc, &ref, &trg) == SELECTOR_OK) {
98                         m = trg.monitor;
99                         d = trg.desktop;
100                         f = trg.desktop->focus;
101                 }
102         } else if (csq->monitor_desc[0] != '\0') {
103                 coordinates_t ref = {m, NULL, NULL};
104                 coordinates_t trg = {NULL, NULL, NULL};
105                 if (monitor_from_desc(csq->monitor_desc, &ref, &trg) == SELECTOR_OK) {
106                         m = trg.monitor;
107                         d = trg.monitor->desk;
108                         f = trg.monitor->desk->focus;
109                 }
110         }
111
112         if (csq->sticky) {
113                 m = mon;
114                 d = mon->desk;
115                 f = mon->desk->focus;
116         }
117
118         if (csq->split_dir[0] != '\0' && f != NULL) {
119                 direction_t dir;
120                 if (parse_direction(csq->split_dir, &dir)) {
121                         presel_dir(m, d, f, dir);
122                 }
123         }
124
125         if (csq->split_ratio != 0 && f != NULL) {
126                 presel_ratio(m, d, f, csq->split_ratio);
127         }
128
129         node_t *n = make_node(win);
130         client_t *c = make_client();
131         c->border_width = csq->border ? d->border_width : 0;
132         n->client = c;
133         initialize_client(n);
134         update_floating_rectangle(n);
135
136         if (c->floating_rectangle.x == 0 && c->floating_rectangle.y == 0) {
137                 csq->center = true;
138         }
139
140         c->min_width = csq->min_width;
141         c->max_width = csq->max_width;
142         c->min_height = csq->min_height;
143         c->max_height = csq->max_height;
144
145         monitor_t *mm = monitor_from_client(c);
146         embrace_client(mm, c);
147         adapt_geometry(&mm->rectangle, &m->rectangle, n);
148
149         if (csq->center) {
150                 window_center(m, c);
151         }
152
153         snprintf(c->class_name, sizeof(c->class_name), "%s", csq->class_name);
154         snprintf(c->instance_name, sizeof(c->instance_name), "%s", csq->instance_name);
155
156         f = insert_node(m, d, n, f);
157         clients_count++;
158
159         put_status(SBSC_MASK_NODE_MANAGE, "node_manage 0x%X 0x%X 0x%X 0x%X\n", m->id, d->id, win, f!=NULL?f->id:0);
160
161         if (f != NULL && f->client != NULL && csq->state != NULL && *(csq->state) == STATE_FLOATING) {
162                 c->last_layer = c->layer = f->client->layer;
163         }
164
165         if (csq->layer != NULL) {
166                 c->last_layer = c->layer = *(csq->layer);
167         }
168
169         if (csq->state != NULL) {
170                 set_state(m, d, n, *(csq->state));
171                 c->last_state = c->state;
172         }
173
174         set_locked(m, d, n, csq->locked);
175         set_sticky(m, d, n, csq->sticky);
176         set_private(m, d, n, csq->private);
177
178         arrange(m, d);
179
180         bool give_focus = (csq->focus && (d == mon->desk || csq->follow));
181
182         if (give_focus) {
183                 focus_node(m, d, n);
184         } else if (csq->focus) {
185                 activate_node(m, d, n);
186         } else {
187                 stack(d, n, false);
188         }
189
190         uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
191         xcb_change_window_attributes(dpy, win, XCB_CW_EVENT_MASK, values);
192
193         if (d == m->desk) {
194                 show_node(n);
195         } else {
196                 hide_node(n);
197         }
198
199         /* the same function is already called in `focus_node` but has no effects on unmapped windows */
200         if (give_focus) {
201                 xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, win, XCB_CURRENT_TIME);
202         }
203
204         ewmh_set_wm_desktop(n, d);
205         ewmh_update_client_list(false);
206         free(csq->layer);
207         free(csq->state);
208 }
209
210 void unmanage_window(xcb_window_t win)
211 {
212         coordinates_t loc;
213         if (locate_window(win, &loc)) {
214                 put_status(SBSC_MASK_NODE_UNMANAGE, "node_unmanage 0x%X 0x%X 0x%X\n", loc.monitor->id, loc.desktop->id, win);
215                 remove_node(loc.monitor, loc.desktop, loc.node);
216                 if (frozen_pointer->window == win) {
217                         frozen_pointer->action = ACTION_NONE;
218                 }
219                 arrange(loc.monitor, loc.desktop);
220         } else {
221                 for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
222                         if (pr->win == win) {
223                                 remove_pending_rule(pr);
224                                 return;
225                         }
226                 }
227         }
228 }
229
230 bool is_presel_window(xcb_window_t win)
231 {
232         xcb_icccm_get_wm_class_reply_t reply;
233         bool ret = false;
234         if (xcb_icccm_get_wm_class_reply(dpy, xcb_icccm_get_wm_class(dpy, win), &reply, NULL) == 1) {
235                 if (streq(BSPWM_CLASS_NAME, reply.class_name) && streq(PRESEL_FEEDBACK_I, reply.instance_name)) {
236                         ret = true;
237                 }
238                 xcb_icccm_get_wm_class_reply_wipe(&reply);
239         }
240         return ret;
241 }
242
243 void initialize_presel_feedback(node_t *n)
244 {
245         if (n == NULL || n->presel == NULL || n->presel->feedback != XCB_NONE) {
246                 return;
247         }
248
249         xcb_window_t win = xcb_generate_id(dpy);
250         uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_SAVE_UNDER;
251         uint32_t values[] = {get_color_pixel(presel_feedback_color), 1};
252         xcb_create_window(dpy, XCB_COPY_FROM_PARENT, win, root, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
253                                   XCB_COPY_FROM_PARENT, mask, values);
254
255         xcb_icccm_set_wm_class(dpy, win, sizeof(PRESEL_FEEDBACK_IC), PRESEL_FEEDBACK_IC);
256         stacking_list_t *s = stack_tail;
257         while (s != NULL && !IS_TILED(s->node->client)) {
258                 s = s->prev;
259         }
260         if (s != NULL) {
261                 window_above(win, s->node->id);
262         }
263         n->presel->feedback = win;
264 }
265
266 void draw_presel_feedback(monitor_t *m, desktop_t *d, node_t *n)
267 {
268         if (n == NULL || n->presel == NULL) {
269                 return;
270         }
271
272         bool exists = (n->presel->feedback != XCB_NONE);
273         if (!exists) {
274                 initialize_presel_feedback(n);
275         }
276
277         int gap = gapless_monocle && (d->layout == LAYOUT_MONOCLE || (single_monocle && tiled_count(d->root) == 1))
278                 ? 0
279                 : d->window_gap;
280
281         presel_t *p = n->presel;
282         xcb_rectangle_t rect = n->rectangle;
283         rect.x = rect.y = 0;
284         rect.width -= gap;
285         rect.height -= gap;
286         xcb_rectangle_t presel_rect = rect;
287
288         switch (p->split_dir) {
289                 case DIR_NORTH:
290                         presel_rect.height = p->split_ratio * rect.height;
291                         break;
292                 case DIR_EAST:
293                         presel_rect.width = (1 - p->split_ratio) * rect.width;
294                         presel_rect.x = rect.width - presel_rect.width;
295                         break;
296                 case DIR_SOUTH:
297                         presel_rect.height = (1 - p->split_ratio) * rect.height;
298                         presel_rect.y = rect.height - presel_rect.height;
299                         break;
300                 case DIR_WEST:
301                         presel_rect.width = p->split_ratio * rect.width;
302                         break;
303         }
304
305         window_move_resize(p->feedback, n->rectangle.x + presel_rect.x, n->rectangle.y + presel_rect.y,
306                            presel_rect.width, presel_rect.height);
307
308         if (!exists && m->desk == d) {
309                 window_show(p->feedback);
310         }
311 }
312
313 void refresh_presel_feebacks_in(node_t *n, desktop_t *d, monitor_t *m)
314 {
315         if (n == NULL) {
316                 return;
317         } else {
318                 if (n->presel != NULL) {
319                         draw_presel_feedback(m, d, n);
320                 }
321                 refresh_presel_feebacks_in(n->first_child, d, m);
322                 refresh_presel_feebacks_in(n->second_child, d, m);
323         }
324 }
325
326 void update_colors(void)
327 {
328         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
329                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
330                         update_colors_in(d->root, d, m);
331                 }
332         }
333 }
334
335 void update_colors_in(node_t *n, desktop_t *d, monitor_t *m)
336 {
337         if (n == NULL) {
338                 return;
339         } else {
340                 if (n->presel != NULL) {
341                         uint32_t pxl = get_color_pixel(presel_feedback_color);
342                         xcb_change_window_attributes(dpy, n->presel->feedback, XCB_CW_BACK_PIXEL, &pxl);
343                         if (d == m->desk) {
344                                 /* hack to induce back pixel refresh */
345                                 window_hide(n->presel->feedback);
346                                 window_show(n->presel->feedback);
347                         }
348                 }
349                 if (n == d->focus) {
350                         draw_border(n, true, (m == mon));
351                 } else if (n->client != NULL) {
352                         draw_border(n, false, (m == mon));
353                 } else {
354                         update_colors_in(n->first_child, d, m);
355                         update_colors_in(n->second_child, d, m);
356                 }
357         }
358 }
359
360 void draw_border(node_t *n, bool focused_node, bool focused_monitor)
361 {
362         if (n == NULL) {
363                 return;
364         }
365
366         uint32_t border_color_pxl = get_border_color(focused_node, focused_monitor);
367         for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
368                 if (f->client->border_width > 0) {
369                         window_draw_border(f->id, border_color_pxl);
370                 }
371         }
372 }
373
374 void window_draw_border(xcb_window_t win, uint32_t border_color_pxl)
375 {
376         xcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXEL, &border_color_pxl);
377 }
378
379 pointer_state_t *make_pointer_state(void)
380 {
381         pointer_state_t *p = malloc(sizeof(pointer_state_t));
382         p->monitor = NULL;
383         p->desktop = NULL;
384         p->node = p->vertical_fence = p->horizontal_fence = NULL;
385         p->client = NULL;
386         p->window = XCB_NONE;
387         p->action = ACTION_NONE;
388         return p;
389 }
390
391 void adopt_orphans(void)
392 {
393         xcb_query_tree_reply_t *qtr = xcb_query_tree_reply(dpy, xcb_query_tree(dpy, root), NULL);
394         if (qtr == NULL) {
395                 return;
396         }
397
398         int len = xcb_query_tree_children_length(qtr);
399         xcb_window_t *wins = xcb_query_tree_children(qtr);
400
401         for (int i = 0; i < len; i++) {
402                 uint32_t idx;
403                 xcb_window_t win = wins[i];
404                 if (xcb_ewmh_get_wm_desktop_reply(ewmh, xcb_ewmh_get_wm_desktop(ewmh, win), &idx, NULL) == 1) {
405                         schedule_window(win);
406                 }
407         }
408
409         free(qtr);
410 }
411
412 void window_close(node_t *n)
413 {
414         if (n == NULL) {
415                 return;
416         } else if (n->client != NULL) {
417                 send_client_message(n->id, ewmh->WM_PROTOCOLS, WM_DELETE_WINDOW);
418         } else {
419                 window_close(n->first_child);
420                 window_close(n->second_child);
421         }
422 }
423
424 void window_kill(monitor_t *m, desktop_t *d, node_t *n)
425 {
426         if (n == NULL) {
427                 return;
428         }
429
430         for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
431                 xcb_kill_client(dpy, f->id);
432         }
433
434         remove_node(m, d, n);
435 }
436
437 uint32_t get_border_color(bool focused_node, bool focused_monitor)
438 {
439         if (focused_monitor && focused_node) {
440                 return get_color_pixel(focused_border_color);
441         } else if (focused_node) {
442                 return get_color_pixel(active_border_color);
443         } else {
444                 return get_color_pixel(normal_border_color);
445         }
446 }
447
448 void update_floating_rectangle(node_t *n)
449 {
450         client_t *c = n->client;
451
452         xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, n->id), NULL);
453
454         if (geo != NULL) {
455                 c->floating_rectangle = (xcb_rectangle_t) {geo->x, geo->y, geo->width, geo->height};
456         }
457
458         free(geo);
459 }
460
461 void restrain_floating_width(client_t *c, int *width)
462 {
463         if (*width < 1) {
464                 *width = 1;
465         }
466         if (c->min_width > 0 && *width < c->min_width) {
467                 *width = c->min_width;
468         } else if (c->max_width > 0 && *width > c->max_width) {
469                 *width = c->max_width;
470         }
471 }
472
473 void restrain_floating_height(client_t *c, int *height)
474 {
475         if (*height < 1) {
476                 *height = 1;
477         }
478         if (c->min_height > 0 && *height < c->min_height) {
479                 *height = c->min_height;
480         } else if (c->max_height > 0 && *height > c->max_height) {
481                 *height = c->max_height;
482         }
483 }
484
485 void restrain_floating_size(client_t *c, int *width, int *height)
486 {
487         restrain_floating_width(c, width);
488         restrain_floating_height(c, height);
489 }
490
491 void query_pointer(xcb_window_t *win, xcb_point_t *pt)
492 {
493         window_lower(motion_recorder);
494
495         xcb_query_pointer_reply_t *qpr = xcb_query_pointer_reply(dpy, xcb_query_pointer(dpy, root), NULL);
496
497         if (qpr != NULL) {
498                 if (win != NULL) {
499                         *win = qpr->child;
500                         xcb_point_t pt = {qpr->root_x, qpr->root_y};
501                         for (stacking_list_t *s = stack_tail; s != NULL; s = s->prev) {
502                                 if (!s->node->client->visible) {
503                                         continue;
504                                 }
505                                 xcb_rectangle_t rect = get_rectangle(NULL, s->node);
506                                 if (is_inside(pt, rect)) {
507                                         if (s->node->id == qpr->child || is_presel_window(qpr->child)) {
508                                                 *win = s->node->id;
509                                         }
510                                         break;
511                                 }
512                         }
513                 }
514                 if (pt != NULL) {
515                         *pt = (xcb_point_t) {qpr->root_x, qpr->root_y};
516                 }
517         }
518
519         free(qpr);
520
521         window_raise(motion_recorder);
522 }
523
524 void window_border_width(xcb_window_t win, uint32_t bw)
525 {
526         uint32_t values[] = {bw};
527         xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_BORDER_WIDTH, values);
528 }
529
530 void window_move(xcb_window_t win, int16_t x, int16_t y)
531 {
532         uint32_t values[] = {x, y};
533         xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_X_Y, values);
534 }
535
536 void window_resize(xcb_window_t win, uint16_t w, uint16_t h)
537 {
538         uint32_t values[] = {w, h};
539         xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_WIDTH_HEIGHT, values);
540 }
541
542 void window_move_resize(xcb_window_t win, int16_t x, int16_t y, uint16_t w, uint16_t h)
543 {
544         uint32_t values[] = {x, y, w, h};
545         xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT, values);
546 }
547
548 void window_raise(xcb_window_t win)
549 {
550         uint32_t values[] = {XCB_STACK_MODE_ABOVE};
551         xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_STACK_MODE, values);
552 }
553
554 void window_center(monitor_t *m, client_t *c)
555 {
556         xcb_rectangle_t *r = &c->floating_rectangle;
557         xcb_rectangle_t a = m->rectangle;
558         if (r->width >= a.width) {
559                 r->x = a.x;
560         } else {
561                 r->x = a.x + (a.width - r->width) / 2;
562         }
563         if (r->height >= a.height) {
564                 r->y = a.y;
565         } else {
566                 r->y = a.y + (a.height - r->height) / 2;
567         }
568         r->x -= c->border_width;
569         r->y -= c->border_width;
570 }
571
572 void window_stack(xcb_window_t w1, xcb_window_t w2, uint32_t mode)
573 {
574         if (w2 == XCB_NONE) {
575                 return;
576         }
577         uint16_t mask = XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE;
578         uint32_t values[] = {w2, mode};
579         xcb_configure_window(dpy, w1, mask, values);
580 }
581
582 void window_above(xcb_window_t w1, xcb_window_t w2)
583 {
584         window_stack(w1, w2, XCB_STACK_MODE_ABOVE);
585 }
586
587 void window_below(xcb_window_t w1, xcb_window_t w2)
588 {
589         window_stack(w1, w2, XCB_STACK_MODE_BELOW);
590 }
591
592 void window_lower(xcb_window_t win)
593 {
594         uint32_t values[] = {XCB_STACK_MODE_BELOW};
595         xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_STACK_MODE, values);
596 }
597
598 void window_set_visibility(xcb_window_t win, bool visible)
599 {
600         uint32_t values_off[] = {ROOT_EVENT_MASK & ~XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY};
601         uint32_t values_on[] = {ROOT_EVENT_MASK};
602         xcb_change_window_attributes(dpy, root, XCB_CW_EVENT_MASK, values_off);
603         if (visible) {
604                 xcb_map_window(dpy, win);
605         } else {
606                 xcb_unmap_window(dpy, win);
607         }
608         xcb_change_window_attributes(dpy, root, XCB_CW_EVENT_MASK, values_on);
609 }
610
611 void window_hide(xcb_window_t win)
612 {
613         window_set_visibility(win, false);
614 }
615
616 void window_show(xcb_window_t win)
617 {
618         window_set_visibility(win, true);
619 }
620
621 void enable_motion_recorder(void)
622 {
623         window_raise(motion_recorder);
624         window_show(motion_recorder);
625 }
626
627 void disable_motion_recorder(void)
628 {
629         window_hide(motion_recorder);
630 }
631
632 void update_motion_recorder(void)
633 {
634         xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, root), NULL);
635
636         if (geo != NULL) {
637                 window_resize(motion_recorder, geo->width, geo->height);
638         }
639
640         free(geo);
641 }
642
643 void update_input_focus(void)
644 {
645         set_input_focus(mon->desk->focus);
646 }
647
648 void set_input_focus(node_t *n)
649 {
650         if (n == NULL || n->client == NULL) {
651                 clear_input_focus();
652         } else {
653                 if (n->client->icccm_input) {
654                         xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_PARENT, n->id, XCB_CURRENT_TIME);
655                 } else if (n->client->icccm_focus) {
656                         send_client_message(n->id, ewmh->WM_PROTOCOLS, WM_TAKE_FOCUS);
657                 }
658         }
659 }
660
661 void clear_input_focus(void)
662 {
663         xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
664 }
665
666 void center_pointer(xcb_rectangle_t r)
667 {
668         int16_t cx = r.x + r.width / 2;
669         int16_t cy = r.y + r.height / 2;
670         window_lower(motion_recorder);
671         xcb_warp_pointer(dpy, XCB_NONE, root, 0, 0, 0, 0, cx, cy);
672         window_raise(motion_recorder);
673 }
674
675 void get_atom(char *name, xcb_atom_t *atom)
676 {
677         xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(dpy, xcb_intern_atom(dpy, 0, strlen(name), name), NULL);
678         if (reply != NULL) {
679                 *atom = reply->atom;
680         } else {
681                 *atom = XCB_NONE;
682         }
683         free(reply);
684 }
685
686 void set_atom(xcb_window_t win, xcb_atom_t atom, uint32_t value)
687 {
688         xcb_change_property(dpy, XCB_PROP_MODE_REPLACE, win, atom, XCB_ATOM_CARDINAL, 32, 1, &value);
689 }
690
691 bool has_proto(xcb_atom_t atom, xcb_icccm_get_wm_protocols_reply_t *protocols)
692 {
693         for (uint32_t i = 0; i < protocols->atoms_len; i++) {
694                 if (protocols->atoms[i] == atom) {
695                         return true;
696                 }
697         }
698         return false;
699 }
700
701 void send_client_message(xcb_window_t win, xcb_atom_t property, xcb_atom_t value)
702 {
703         xcb_client_message_event_t e;
704
705         e.response_type = XCB_CLIENT_MESSAGE;
706         e.window = win;
707         e.format = 32;
708         e.sequence = 0;
709         e.type = property;
710         e.data.data32[0] = value;
711         e.data.data32[1] = XCB_CURRENT_TIME;
712
713         xcb_send_event(dpy, false, win, XCB_EVENT_MASK_NO_EVENT, (char *) &e);
714 }