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