]> Pileus Git - wmpus/blob - sys-xcb.c
Implement transient for
[wmpus] / sys-xcb.c
1 /*
2  * Copyright (c) 2015 Andy Spencer <andy753421@gmail.com>
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  */
15
16 #define _GNU_SOURCE
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <search.h>
20 #include <string.h>
21
22 #include <xcb/xcb.h>
23 #include <xcb/xcb_event.h>
24 #include <xcb/xcb_keysyms.h>
25 #include <xcb/xcb_icccm.h>
26 #include <xcb/xcb_ewmh.h>
27 #include <xcb/xinerama.h>
28
29 #include "util.h"
30 #include "conf.h"
31 #include "types.h"
32 #include "sys.h"
33 #include "wm.h"
34
35 /* Configuration */
36 static int border     = 2;
37 static int stack      = 25;
38 static int no_capture = 0;
39
40 /* Internal structures */
41 typedef struct {
42         int left;
43         int right;
44         int top;
45         int bottom;
46 } strut_t;
47
48 struct win_sys {
49         xcb_window_t     xcb;    // xcb window id
50         xcb_event_mask_t events; // currently watch events
51         strut_t          strut;  // toolbar struts
52         state_t          state;  // window state if not mapped
53         xcb_window_t     parent; // transient for window
54         int mapped;              // window is managed by wm
55         int managed;             // window is managed by wm
56 };
57
58 /* Global data */
59 static xcb_connection_t      *conn;
60 static xcb_ewmh_connection_t  ewmh;
61 static xcb_key_symbols_t     *keysyms;
62 static xcb_colormap_t         colormap;
63 static xcb_window_t           root;
64 static xcb_event_mask_t       events;
65 static list_t                *screens;
66 static list_t                *struts;
67 static void                  *cache;
68 static unsigned int           grabbed;
69 static int                    running;
70 static xcb_window_t           control;
71
72 static xcb_pixmap_t           clr_focus;
73 static xcb_pixmap_t           clr_unfocus;
74 static xcb_pixmap_t           clr_urgent;
75
76 static xcb_atom_t             wm_protos;
77 static xcb_atom_t             wm_delete;
78 static xcb_atom_t             wm_nhints;
79
80 /************************
81  * Conversion functions *
82  ************************/
83
84 /* State names */
85 static char *state_map[] = {
86         [ST_HIDE ] "hide ",
87         [ST_SHOW ] "show ",
88         [ST_FULL ] "full ",
89         [ST_MAX  ] "max  ",
90         [ST_SHADE] "shade",
91         [ST_ICON ] "icon ",
92         [ST_CLOSE] "close",
93 };
94
95 /* Key presses */
96 static struct {
97         event_t      ev;
98         xcb_keysym_t sym;
99 } keysym_map[] = {
100         { EV_LEFT,     0xFF51 },
101         { EV_RIGHT,    0xFF53 },
102         { EV_UP,       0xFF52 },
103         { EV_DOWN,     0xFF54 },
104         { EV_HOME,     0xFF50 },
105         { EV_END,      0xFF57 },
106         { EV_PAGEUP,   0xFF55 },
107         { EV_PAGEDOWN, 0xFF56 },
108         { EV_F1,       0xFFBE },
109         { EV_F2,       0xFFBF },
110         { EV_F3,       0xFFC0 },
111         { EV_F4,       0xFFC1 },
112         { EV_F5,       0xFFC2 },
113         { EV_F6,       0xFFC3 },
114         { EV_F7,       0xFFC4 },
115         { EV_F8,       0xFFC5 },
116         { EV_F9,       0xFFC6 },
117         { EV_F10,      0xFFC7 },
118         { EV_F11,      0xFFC8 },
119         { EV_F12,      0xFFC9 },
120 };
121
122 /************************
123  * Conversion functions *
124  ************************/
125
126 static xcb_keycode_t *event_to_keycodes(event_t ev)
127 {
128         xcb_keycode_t *codes = NULL;
129
130         /* Get keysym */
131         xcb_keysym_t keysym = map_get(keysym_map, ev, ev, sym, ev);
132
133         /* Get keycodes */
134         if (!(codes = xcb_key_symbols_get_keycode(keysyms, keysym)))
135                 warn("no keycode found for %d->%d", ev, keysym);
136
137         return codes;
138 }
139
140 static event_t keycode_to_event(xcb_keycode_t code)
141 {
142         /* Get keysym */
143         xcb_keysym_t keysym = xcb_key_symbols_get_keysym(keysyms, code, 0);
144
145         /* Get event */
146         return map_get(keysym_map, sym,keysym, ev,keysym);
147 }
148
149 /* Button presses */
150 static event_t button_to_event(xcb_button_t btn)
151 {
152         return EV_MOUSE0 + btn;
153 }
154
155 static xcb_button_t event_to_button(event_t ev)
156 {
157         return ev - EV_MOUSE0;
158 }
159
160 /* Modifier masks */
161 static xcb_mod_mask_t mod_to_mask(mod_t mod)
162 {
163         xcb_mod_mask_t mask = 0;
164         if (mod.alt)   mask |= XCB_MOD_MASK_1;
165         if (mod.ctrl)  mask |= XCB_MOD_MASK_CONTROL;
166         if (mod.shift) mask |= XCB_MOD_MASK_SHIFT;
167         if (mod.win)   mask |= XCB_MOD_MASK_4;
168         return mask;
169 }
170
171 static mod_t mask_to_mod(xcb_mod_mask_t mask, int up)
172 {
173         mod_t mod = { .up = up };
174         if (mask & XCB_MOD_MASK_1)       mod.alt   = 1;
175         if (mask & XCB_MOD_MASK_CONTROL) mod.ctrl  = 1;
176         if (mask & XCB_MOD_MASK_SHIFT)   mod.shift = 1;
177         if (mask & XCB_MOD_MASK_4)       mod.win   = 1;
178         return mod;
179 }
180
181 /* Mouse pointers */
182 static ptr_t list_to_ptr(int16_t *list)
183 {
184         ptr_t ptr = {};
185         if (list) {
186                 ptr.rx = list[0]; // root_x
187                 ptr.ry = list[1]; // root_y
188                 ptr.x  = list[2]; // event_x
189                 ptr.y  = list[3]; // event_y
190         }
191         return ptr;
192 }
193
194 /********************
195  * Window functions *
196  ********************/
197
198 static int win_cmp(const void *_a, const void *_b)
199 {
200         const win_t *a = _a;
201         const win_t *b = _b;
202         if (a->sys->xcb < b->sys->xcb) return -1;
203         if (a->sys->xcb > b->sys->xcb) return  1;
204         return 0;
205 }
206
207 static win_t *win_get(xcb_window_t xcb)
208 {
209         win_sys_t sys = { .xcb =  xcb };
210         win_t     key = { .sys = &sys };
211
212         win_t   **win = tfind(&key, &cache, win_cmp);
213
214         if (!win) {
215                 warn("no window for %u", xcb);
216                 return NULL;
217         }
218
219         return *win;
220 }
221
222 static win_t *win_new(xcb_window_t xcb)
223 {
224         win_t *win = new0(win_t);
225         win->sys = new0(win_sys_t);
226         win->sys->xcb = xcb;
227
228         win_t **old = tfind(win, &cache, win_cmp);
229         if (old) {
230                 warn("duplicate window for %u\n", xcb);
231                 free(win->sys);
232                 free(win);
233                 return *old;
234         }
235
236         tsearch(win, &cache, win_cmp);
237         printf("win_new: xcb=%-8u -> win=%p\n",
238                         win->sys->xcb, win);
239         return win;
240 }
241
242 static void win_free(win_t *win)
243 {
244         printf("win_free: xcb=%-8u -> win=%p\n",
245                         win->sys->xcb, win);
246         free(win->sys);
247         free(win);
248 }
249
250 static void win_add_strut(win_t *win)
251 {
252         win->type = TYPE_TOOLBAR;
253         for (list_t *cur = screens; cur; cur = cur->next) {
254                 win_t   *screen = cur->data;
255                 strut_t *strut  = &win->sys->strut;
256                 screen->x += strut->left;
257                 screen->y += strut->top;
258                 screen->w -= strut->left + strut->right;
259                 screen->h -= strut->top  + strut->bottom;
260         }
261         struts = list_insert(struts, win);
262
263 }
264
265 static void win_del_strut(win_t *win)
266 {
267         list_t *link = list_find(struts, win);
268         if (!link)
269                 return;
270         for (list_t *cur = screens; cur; cur = cur->next) {
271                 win_t   *screen = cur->data;
272                 strut_t *strut  = &win->sys->strut;
273                 screen->x -= strut->left;
274                 screen->y -= strut->top;
275                 screen->w += strut->left + strut->right;
276                 screen->h += strut->top  + strut->bottom;
277         }
278         struts = list_remove(struts, link, 0);
279 }
280
281 /****************
282  * XCB Wrappers *
283  ****************/
284
285 static void *do_query_tree(xcb_window_t xcb, xcb_window_t **kids, int *nkids)
286 {
287         xcb_query_tree_cookie_t cookie =
288                 xcb_query_tree(conn, xcb);
289         if (!cookie.sequence)
290                 return warn("do_query_tree: %d - bad cookie", xcb), NULL;
291
292         xcb_query_tree_reply_t *reply =
293                 xcb_query_tree_reply(conn, cookie, NULL);
294         if (!reply)
295                 return warn("do_query_tree: %d - no reply", xcb), NULL;
296
297         *nkids = xcb_query_tree_children_length(reply);
298         *kids  = xcb_query_tree_children(reply);
299         printf("do_query_tree: %d - n=%d\n", xcb, *nkids);
300         return reply;
301 }
302
303 static int do_get_geometry(xcb_window_t xcb,
304                 int *x, int *y, int *w, int *h)
305 {
306         xcb_get_geometry_cookie_t cookie =
307                 xcb_get_geometry(conn, xcb);
308         if (!cookie.sequence)
309                 return warn("do_get_geometry: %d - bad cookie", xcb);
310
311         xcb_get_geometry_reply_t *reply =
312                 xcb_get_geometry_reply(conn, cookie, NULL);
313         if (!reply)
314                 return warn("do_get_geometry: %d - no reply", xcb);
315
316         printf("do_get_geometry: %d - %dx%d @ %d,%d\n",
317                         xcb, reply->width, reply->height, reply->x, reply->y);
318         *x = reply->x;
319         *y = reply->y;
320         *w = reply->width;
321         *h = reply->height;
322         free(reply);
323         return 1;
324 }
325
326 static int do_get_window_attributes(xcb_window_t xcb,
327                 int *override, int *mapped)
328 {
329         xcb_get_window_attributes_cookie_t cookie =
330                 xcb_get_window_attributes(conn, xcb);
331         if (!cookie.sequence)
332                 return warn("do_get_window_attributes: %d - bad cookie", xcb);
333
334         xcb_get_window_attributes_reply_t *reply =
335                 xcb_get_window_attributes_reply(conn, cookie, NULL);
336         if (!reply)
337                 return warn("do_get_window_attributes: %d - no reply ", xcb);
338
339         printf("do_get_window_attributes: %d - %d\n",
340                         xcb, reply->override_redirect);
341         *override = reply->override_redirect;
342         *mapped   = reply->map_state != XCB_MAP_STATE_UNMAPPED;
343         free(reply);
344         return 1;
345 }
346
347 static int do_xinerama_check(void)
348 {
349         const xcb_query_extension_reply_t *data =
350                 xcb_get_extension_data(conn, &xcb_xinerama_id);
351         if (!data || !data->present)
352                 return warn("do_xinerama_check: no ext");
353
354         xcb_xinerama_is_active_cookie_t cookie =
355                 xcb_xinerama_is_active(conn);
356         if (!cookie.sequence)
357                 return warn("do_xinerama_check: no cookie");
358
359         xcb_xinerama_is_active_reply_t *reply =
360                 xcb_xinerama_is_active_reply(conn, cookie, NULL);
361         if (!reply)
362                 return warn("do_xinerama_check: no reply");
363
364         printf("do_xinerama_check: %d\n", reply->state);
365         int state = reply->state;
366         free(reply);
367         return state;
368 }
369
370 static void *do_query_screens(xcb_xinerama_screen_info_t **info, int *ninfo)
371 {
372         xcb_xinerama_query_screens_cookie_t cookie =
373                 xcb_xinerama_query_screens(conn);
374         if (!cookie.sequence)
375                 return warn("do_query_screens: bad cookie"), NULL;
376
377         xcb_xinerama_query_screens_reply_t *reply =
378                 xcb_xinerama_query_screens_reply(conn, cookie, NULL);
379         if (!reply)
380                 return warn("do_query_screens: no reply"), NULL;
381
382         *ninfo = xcb_xinerama_query_screens_screen_info_length(reply);
383         *info  = xcb_xinerama_query_screens_screen_info(reply);
384         printf("do_query_screens: %d screens\n", *ninfo);
385         return reply;
386 }
387
388 static int do_get_input_focus(void)
389 {
390         xcb_get_input_focus_cookie_t cookie =
391                 xcb_get_input_focus(conn);
392         if (!cookie.sequence)
393                 return warn("do_get_input_focus: bad cookie");
394
395         xcb_get_input_focus_reply_t *reply =
396                 xcb_get_input_focus_reply(conn, cookie, NULL);
397         if (!reply)
398                 return warn("do_get_input_focus: no reply");
399
400         int focus = reply->focus;
401         free(reply);
402         return focus;
403 }
404
405 static xcb_atom_t do_intern_atom(const char *name)
406 {
407         xcb_intern_atom_cookie_t cookie =
408                 xcb_intern_atom(conn, 0, strlen(name), name);
409         if (!cookie.sequence)
410                 return warn("do_intern_atom: bad cookie");
411
412         xcb_intern_atom_reply_t *reply =
413                 xcb_intern_atom_reply(conn, cookie, NULL);
414         if (!reply)
415                 return warn("do_intern_atom: no reply");
416
417         xcb_atom_t atom = reply->atom;
418         free(reply);
419         return atom;
420 }
421
422 static char *do_get_atom_name(xcb_atom_t atom)
423 {
424         xcb_get_atom_name_cookie_t cookie =
425                 xcb_get_atom_name(conn, atom);
426         if (!cookie.sequence)
427                 return warn("do_get_atom_name: bad cookie"), NULL;
428
429         xcb_get_atom_name_reply_t *reply =
430                 xcb_get_atom_name_reply(conn, cookie, NULL);
431         if (!reply)
432                 return warn("do_get_atom_name: no reply"), NULL;
433
434         char *name = xcb_get_atom_name_name(reply);
435         int len = xcb_get_atom_name_name_length(reply);
436         char *str = strndup(name, len);
437         free(reply);
438         return str;
439 }
440
441 static int do_ewmh_init_atoms(void)
442 {
443         xcb_intern_atom_cookie_t *cookies =
444                 xcb_ewmh_init_atoms(conn, &ewmh);
445         if (!cookies)
446                 return warn("do_ewmh_init_atoms: no cookies");
447
448         int status =
449                 xcb_ewmh_init_atoms_replies(&ewmh, cookies, NULL);
450         if (!status)
451                 return warn("do_ewmh_init_atoms: no status");
452         return status;
453 }
454
455 static int do_get_type(xcb_window_t xcb, type_t *type)
456 {
457         xcb_get_property_cookie_t cookie =
458                 xcb_ewmh_get_wm_window_type(&ewmh, xcb);
459         if (!cookie.sequence)
460                 return warn("do_get_type: bad cookie");
461
462         xcb_ewmh_get_atoms_reply_t reply;
463         if (!xcb_ewmh_get_wm_window_type_reply(&ewmh, cookie, &reply, NULL))
464                 return warn("do_get_type: no reply");
465
466         type_t prev = *type;
467         for (int i = 0; i < reply.atoms_len; i++) {
468                 if (reply.atoms[i] == ewmh._NET_WM_WINDOW_TYPE_DOCK)
469                         *type = TYPE_TOOLBAR;
470                 if (reply.atoms[i] == ewmh._NET_WM_WINDOW_TYPE_TOOLBAR)
471                         *type = TYPE_TOOLBAR;
472                 if (reply.atoms[i] == ewmh._NET_WM_WINDOW_TYPE_DIALOG)
473                         *type = TYPE_DIALOG;
474         }
475         printf("do_get_type: %d -> %d\n", prev, *type);
476         return 1;
477 }
478
479 static int do_get_icccm_state(xcb_window_t xcb, state_t *state)
480 {
481         xcb_get_property_cookie_t cookie;
482
483         cookie = xcb_icccm_get_wm_normal_hints(conn, xcb);
484         if (!cookie.sequence)
485                 return warn("do_get_icccm_state: bad cookie1");
486
487         xcb_size_hints_t sizes;
488         if (!xcb_icccm_get_wm_normal_hints_reply(conn, cookie, &sizes, NULL))
489                 return warn("do_get_icccm_state: no sizes");
490
491         state_t prev = *state;
492         printf("do_get_icccm_state: %s -> %s\n",
493                         state_map[prev], state_map[*state]);
494         return 1;
495 }
496
497 static int do_get_ewmh_state(xcb_window_t xcb, state_t *state)
498 {
499         xcb_get_property_cookie_t cookie;
500
501         cookie = xcb_ewmh_get_wm_state(&ewmh, xcb);
502         if (!cookie.sequence)
503                 return warn("do_get_ewmh_state: bad cookie2");
504
505         xcb_ewmh_get_atoms_reply_t states;
506         if (!xcb_ewmh_get_wm_state_reply(&ewmh, cookie, &states, NULL))
507                 return warn("do_get_ewmh_state: no reply2");
508
509         state_t prev = *state;
510         for (int i = 0; i < states.atoms_len; i++)
511                 if (states.atoms[i] == ewmh._NET_WM_STATE_FULLSCREEN)
512                         *state = ST_FULL;
513
514         printf("do_get_ewmh_state: %s -> %s\n",
515                         state_map[prev], state_map[*state]);
516         return 1;
517 }
518
519 static int do_get_strut(xcb_window_t xcb, strut_t *strut)
520 {
521         xcb_get_property_cookie_t cookie =
522                 xcb_ewmh_get_wm_strut(&ewmh, xcb);
523         if (!cookie.sequence)
524                 return warn("do_get_strut: bad cookie");
525
526         xcb_ewmh_get_extents_reply_t ext = {};
527         int status =
528                 xcb_ewmh_get_wm_strut_reply(&ewmh, cookie, &ext, NULL);
529         if (!status)
530                 return warn("do_get_strut: no status");
531
532         strut->left   = ext.left;
533         strut->right  = ext.right;
534         strut->top    = ext.top;
535         strut->bottom = ext.bottom;
536
537         return ext.left || ext.right || ext.top || ext.bottom;
538 }
539
540 static int do_get_transient(xcb_window_t xcb, xcb_window_t *parent)
541 {
542         xcb_get_property_cookie_t cookie;
543
544         cookie = xcb_icccm_get_wm_transient_for(conn, xcb);
545         if (!cookie.sequence)
546                 return warn("do_get_transient: bad cookie");
547
548         if (!xcb_icccm_get_wm_transient_for_reply(conn, cookie, parent, NULL))
549                 return warn("do_get_transient: no sizes");
550
551         printf("do_get_transient: %d -> %d\n", xcb, *parent);
552         return 1;
553 }
554
555 static xcb_pixmap_t do_alloc_color(uint32_t rgb)
556 {
557         uint16_t r = (rgb & 0xFF0000) >> 8;
558         uint16_t g = (rgb & 0x00FF00);
559         uint16_t b = (rgb & 0x0000FF) << 8;
560         xcb_alloc_color_cookie_t cookie =
561                 xcb_alloc_color(conn, colormap, r, g, b);
562         if (!cookie.sequence)
563                 return warn("do_alloc_color: bad cookie");
564
565         xcb_alloc_color_reply_t *reply =
566                 xcb_alloc_color_reply(conn, cookie, NULL);
567         if (!reply)
568                 return warn("do_alloc_color: no reply");
569
570         printf("do_alloc_color: %06x -> %06x\n", rgb, reply->pixel);
571         xcb_pixmap_t pixel = reply->pixel;
572         free(reply);
573         return pixel;
574 }
575
576 static void do_grab_pointer(xcb_event_mask_t mask)
577 {
578         if (!grabbed)
579                 xcb_grab_pointer(conn, 0, root, mask,
580                                 XCB_GRAB_MODE_ASYNC,
581                                 XCB_GRAB_MODE_ASYNC,
582                                 0, 0, XCB_CURRENT_TIME);
583         grabbed++;
584 }
585
586 static void do_ungrab_pointer(void)
587 {
588         grabbed--;
589         if (!grabbed)
590                 xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
591 }
592
593 static void do_configure_window(xcb_window_t xcb,
594                 int x, int y, int w, int h,
595                 int b, int s, int r)
596 {
597         int table[][2] = {
598                 { x, XCB_CONFIG_WINDOW_X            },
599                 { y, XCB_CONFIG_WINDOW_Y            },
600                 { w, XCB_CONFIG_WINDOW_WIDTH        },
601                 { h, XCB_CONFIG_WINDOW_HEIGHT       },
602                 { b, XCB_CONFIG_WINDOW_BORDER_WIDTH },
603                 { s, XCB_CONFIG_WINDOW_SIBLING      },
604                 { r, XCB_CONFIG_WINDOW_STACK_MODE   },
605         };
606
607         uint16_t mask = 0;
608         uint32_t list[countof(table)];
609         for (int i=0,j=0; i < countof(table); i++) {
610                 if (table[i][0] >= 0) {
611                         list[j++] = table[i][0];
612                         mask     |= table[i][1];
613                 }
614         }
615
616         xcb_configure_window(conn, xcb, mask, list);
617 }
618
619 static int do_client_message(xcb_window_t xcb, xcb_atom_t atom)
620 {
621         /* Get protocols */
622         xcb_get_property_cookie_t cookie =
623                 xcb_icccm_get_wm_protocols(conn, xcb, wm_protos);
624         if (!cookie.sequence)
625                 return warn("do_client_message: %d - bad cookie", xcb);
626
627         xcb_icccm_get_wm_protocols_reply_t protos = {};
628         if (!xcb_icccm_get_wm_protocols_reply(conn, cookie, &protos, NULL))
629                 return warn("do_client_message: %d - no reply", xcb);
630
631         /* Search for the atom */
632         int found = 0;
633         for (int i = 0; i < protos.atoms_len; i++)
634                 if (protos.atoms[i] == atom)
635                         found = 1;
636         xcb_icccm_get_wm_protocols_reply_wipe(&protos);
637         if (!found)
638                 return warn("do_client_message: %d - no atom", xcb);
639
640         /* Send the message */
641         xcb_client_message_event_t msg = {
642                 .response_type  = XCB_CLIENT_MESSAGE,
643                 .format         = 32,
644                 .window         = xcb,
645                 .type           = wm_protos,
646                 .data.data32[0] = atom,
647                 .data.data32[1] = XCB_CURRENT_TIME,
648         };
649         xcb_send_event(conn, 0, xcb, XCB_EVENT_MASK_NO_EVENT,
650                         (const char *)&msg);
651         return 1;
652 }
653
654 /**************************
655  * Window Manager Helpers *
656  **************************/
657
658 /* Send event info */
659 static int send_event(event_t ev, xcb_window_t ewin)
660 {
661         win_t *win = win_get(ewin);
662         do_grab_pointer(0);
663         int status = wm_handle_event(win, ev, MOD(), PTR());
664         do_ungrab_pointer();
665         return status;
666 }
667
668 /* Send event info */
669 static int send_event_info(event_t ev, xcb_mod_mask_t mask, int up, int16_t *pos,
670                 xcb_window_t rwin, xcb_window_t ewin, xcb_window_t cwin)
671 {
672         xcb_window_t xcb = ewin == rwin ? cwin : ewin;
673         win_t *win = win_get(xcb);
674         mod_t  mod = mask_to_mod(mask, up);
675         ptr_t  ptr = list_to_ptr(pos);
676         do_grab_pointer(0);
677         int status = wm_handle_event(win, ev, mod, ptr);
678         do_ungrab_pointer();
679         return status;
680 }
681
682 /* Send pointer motion info */
683 static int send_pointer(int16_t *pos,
684                 xcb_window_t rwin, xcb_window_t ewin, xcb_window_t cwin)
685 {
686         xcb_window_t xcb = ewin == rwin ? cwin : ewin;
687         win_t *win = win_get(xcb);
688         ptr_t  ptr = list_to_ptr(pos);
689         do_grab_pointer(0);
690         int status = wm_handle_ptr(win, ptr);
691         do_ungrab_pointer();
692         return status;
693 }
694
695 /* Send window state info */
696 static void send_manage(win_t *win, int managed)
697 {
698         if (win->sys->managed == managed)
699                 return;
700         if (managed)
701                 wm_insert(win);
702         else
703                 wm_remove(win);
704         win->sys->managed = managed;
705 }
706
707 /* Send window state info */
708 static void send_state(win_t *win, state_t next)
709 {
710         if (!win->sys->managed)
711                 return;
712         if (!win->sys->mapped)
713                 return;
714         if (win->state == next)
715                 return;
716         state_t prev = win->state;
717         win->state = next;
718         wm_handle_state(win, prev, next);
719 }
720
721 /**********************
722  * X11 Event Handlers *
723  **********************/
724
725 /* Specific events */
726 static void on_key_event(xcb_key_press_event_t *event, int up)
727 {
728         printf("on_key_event:         xcb=%-8u\n", event->event);
729         xcb_window_t focus = do_get_input_focus();
730         event_t ev = keycode_to_event(event->detail);
731         send_event_info(ev, event->state, up, &event->root_x,
732                 event->root, focus, event->child);
733 }
734
735 static void on_button_event(xcb_button_press_event_t *event, int up)
736 {
737         printf("on_button_event:      xcb=%-8u\n", event->event);
738         event_t ev = button_to_event(event->detail);
739         if (!send_event_info(ev, event->state, up, &event->root_x,
740                                 event->root, event->event, event->child))
741                 xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
742         else if (!up)
743                 do_grab_pointer(XCB_EVENT_MASK_POINTER_MOTION |
744                                 XCB_EVENT_MASK_BUTTON_RELEASE);
745         else
746                 do_ungrab_pointer();
747
748 }
749
750 static void on_motion_notify(xcb_motion_notify_event_t *event)
751 {
752         printf("on_motion_notify:     xcb=%-8u - %d,%d / %d.%d\n", event->event,
753                         event->event_x, event->event_y,
754                         event->root_x,  event->root_y);
755         send_pointer(&event->root_x, event->root, event->event, event->child);
756 }
757
758 static void on_enter_notify(xcb_enter_notify_event_t *event)
759 {
760         if (event->mode != XCB_NOTIFY_MODE_NORMAL)
761                 return;
762         printf("on_enter_notify:      xcb=%-8u\n", event->event);
763         send_event_info(EV_ENTER, event->state, 0, &event->root_x,
764                 event->root, event->event, event->child);
765 }
766
767 static void on_leave_notify(xcb_leave_notify_event_t *event)
768 {
769         if (event->mode != XCB_NOTIFY_MODE_NORMAL)
770                 return;
771         printf("on_leave_notify:      xcb=%-8u\n", event->event);
772         send_event_info(EV_LEAVE, event->state, 0, &event->root_x,
773                 event->root, event->event, event->child);
774 }
775
776 static void on_focus_in(xcb_focus_in_event_t *event)
777 {
778         if (event->mode != XCB_NOTIFY_MODE_NORMAL &&
779             event->mode != XCB_NOTIFY_MODE_WHILE_GRABBED)
780                 return;
781         printf("on_focus_in:          xcb=%-8u mode=%d\n", event->event, event->mode);
782         xcb_change_window_attributes(conn, event->event,
783                         XCB_CW_BORDER_PIXEL, &clr_focus);
784         if (event->mode == XCB_NOTIFY_MODE_NORMAL)
785                 send_event(EV_FOCUS, event->event);
786 }
787
788 static void on_focus_out(xcb_focus_out_event_t *event)
789 {
790         if (event->mode != XCB_NOTIFY_MODE_NORMAL &&
791             event->mode != XCB_NOTIFY_MODE_WHILE_GRABBED)
792                 return;
793         printf("on_focus_out:         xcb=%-8u mode=%d\n", event->event, event->mode);
794         xcb_change_window_attributes(conn, event->event,
795                         XCB_CW_BORDER_PIXEL, &clr_unfocus);
796         if (event->mode == XCB_NOTIFY_MODE_NORMAL)
797                 send_event(EV_UNFOCUS, event->event);
798 }
799
800 static void on_create_notify(xcb_create_notify_event_t *event)
801 {
802         printf("on_create_notify:     xcb=%-8u\n", event->window);
803
804         win_t *win = win_new(event->window);
805
806         win->x = event->x;
807         win->y = event->y;
808         win->w = event->width;
809         win->h = event->height;
810
811         win->sys->state  = ST_SHOW;
812         win->sys->events = XCB_EVENT_MASK_PROPERTY_CHANGE;
813         xcb_change_window_attributes(conn, event->window,
814                         XCB_CW_EVENT_MASK, &win->sys->events);
815
816         if (!event->override_redirect)
817                 send_manage(win, 1);
818
819         if (do_get_transient(event->window, &win->sys->parent))
820                 win->parent = win_get(win->sys->parent);
821 }
822
823 static void on_destroy_notify(xcb_destroy_notify_event_t *event)
824 {
825         win_t *win = win_get(event->window);
826         printf("on_destroy_notify:    xcb=%-8u -> win=%p\n",
827                         event->window, win);
828         if (!win) return;
829
830         send_manage(win, 0);
831         tdelete(win, &cache, win_cmp);
832         win_free(win);
833 }
834
835 static void on_unmap_notify(xcb_unmap_notify_event_t *event)
836 {
837         win_t *win = win_get(event->window);
838         printf("on_unmap_notify:      xcb=%-8u -> win=%p\n",
839                         event->window, win);
840         if (!win) return;
841
842         win_del_strut(win);
843         send_state(win, ST_HIDE);
844         win->sys->mapped = 0;
845 }
846
847 static void on_map_notify(xcb_map_notify_event_t *event)
848 {
849         win_t *win = win_get(event->window);
850         printf("on_map_notify:        xcb=%-8u -> win=%p\n",
851                         event->window, win);
852         if (!win) return;
853
854         win->sys->mapped = 1;
855         send_state(win, win->sys->state);
856 }
857
858 static void on_map_request(xcb_map_request_event_t *event)
859 {
860         win_t *win = win_get(event->window);
861         printf("on_map_request:       xcb=%-8u -> win=%p\n",
862                         event->window, win);
863         if (!win) return;
864
865         win->sys->mapped = 1;
866         send_state(win, win->sys->state);
867         xcb_map_window(conn, win->sys->xcb);
868         if (!win->sys->managed)
869                 sys_move(win, win->x, win->y, win->w, win->h);
870 }
871
872 static void on_configure_request(xcb_configure_request_event_t *event)
873 {
874         win_t *win = win_get(event->window);
875         printf("on_configure_request: xcb=%-8u -> win=%p -- %dx%d @ %d,%d\n",
876                         event->window, win,
877                         event->width, event->height,
878                         event->x, event->y);
879         if (!win) return;
880         printf("on_configure_request: xcb=%-8u -> win=%p << %dx%d @ %d,%d\n",
881                         event->window, win,
882                         win->w, win->h,
883                         win->x, win->y);
884
885         if (!win->sys->managed) {
886                 win->x = event->x;
887                 win->y = event->y;
888                 win->w = event->width;
889                 win->h = event->height;
890         }
891
892         xcb_configure_notify_event_t resp = {
893                 .response_type = XCB_CONFIGURE_NOTIFY,
894                 .event         = win->sys->xcb,
895                 .window        = win->sys->xcb,
896                 .x             = win->x,
897                 .y             = win->y,
898                 .width         = win->w,
899                 .height        = win->h,
900                 .border_width  = border,
901         };
902
903         xcb_send_event(conn, 0, win->sys->xcb,
904                         XCB_EVENT_MASK_STRUCTURE_NOTIFY,
905                         (const char *)&resp);
906 }
907
908 static void on_property_notify(xcb_property_notify_event_t *event)
909 {
910         win_t *win = win_get(event->window);
911         char *name = do_get_atom_name(event->atom);
912         printf("on_property_notify: xcb=%-8u -> win=%p - %s\n",
913                         event->window, win, name);
914         if (name) free(name);
915         if (!win) return;
916
917         /* Check window type */
918         if (event->atom == ewmh._NET_WM_WINDOW_TYPE)
919                 do_get_type(event->window, &win->type);
920
921         /* Check struts */
922         if (event->atom == ewmh._NET_WM_STRUT)
923                 if (do_get_strut(win->sys->xcb, &win->sys->strut))
924                         win_add_strut(win);
925
926         /* Check transient for */
927         if (event->atom == XCB_ATOM_WM_TRANSIENT_FOR)
928                 if (do_get_transient(event->window, &win->sys->parent))
929                         win->parent = win_get(win->sys->parent);
930
931         /* Check window state */
932         if (event->atom == wm_nhints)
933                 if (do_get_icccm_state(event->window, &win->sys->state))
934                         send_state(win, win->sys->state);
935         if (event->atom == ewmh._NET_WM_STATE)
936                 if (do_get_ewmh_state(event->window, &win->sys->state))
937                         send_state(win, win->sys->state);
938 }
939
940 static void on_client_message(xcb_client_message_event_t *event)
941 {
942         win_t *win = win_get(event->window);
943         char *name = do_get_atom_name(event->type);
944         printf("on_client_message: xcb=%-8u -> win=%p - %s=[%d,%d,%d,%d]\n",
945                         event->window, win, name,
946                         event->data.data32[0], event->data.data32[1],
947                         event->data.data32[2], event->data.data32[3]);
948         if (name) free(name);
949         if (!win) return;
950
951         /* Exit request */
952         if (event->window         == control   &&
953             event->type           == wm_protos &&
954             event->data.data32[0] == wm_delete) {
955                 printf("on_client_message: shutdown request");
956                 running = 0;
957         }
958
959         /* Close request */
960         if (event->type == ewmh._NET_CLOSE_WINDOW) {
961                 printf("on_client_message: close request");
962                 sys_show(win, ST_CLOSE);
963         }
964
965         /* Fullscreen request  */
966         if ((event->type           == ewmh._NET_WM_STATE) &&
967             (event->data.data32[1] == ewmh._NET_WM_STATE_FULLSCREEN ||
968              event->data.data32[2] == ewmh._NET_WM_STATE_FULLSCREEN)) {
969                 printf("on_client_message: fullscreen request");
970                 int full = win->state == ST_FULL;
971                 switch (event->data.data32[0]) {
972                         case XCB_EWMH_WM_STATE_REMOVE: full  = 0; break;
973                         case XCB_EWMH_WM_STATE_ADD:    full  = 1; break;
974                         case XCB_EWMH_WM_STATE_TOGGLE: full ^= 1; break;
975                 }
976                 win->sys->state = full ? ST_FULL : ST_SHOW;
977                 send_state(win, win->sys->state);
978                 sys_show(win, win->sys->state);
979         }
980
981 }
982
983 /* Generic Event */
984 static void on_event(xcb_generic_event_t *event)
985 {
986         int type = XCB_EVENT_RESPONSE_TYPE(event);
987
988         switch (type) {
989                 /* Input handling */
990                 case XCB_KEY_PRESS:
991                         on_key_event((xcb_key_press_event_t *)event, 0);
992                         break;
993                 case XCB_KEY_RELEASE:
994                         on_key_event((xcb_key_release_event_t *)event, 1);
995                         break;
996                 case XCB_BUTTON_PRESS:
997                         on_button_event((xcb_button_press_event_t *)event, 0);
998                         break;
999                 case XCB_BUTTON_RELEASE:
1000                         on_button_event((xcb_button_release_event_t *)event, 1);
1001                         break;
1002                 case XCB_MOTION_NOTIFY:
1003                         on_motion_notify((xcb_motion_notify_event_t *)event);
1004                         break;
1005                 case XCB_ENTER_NOTIFY:
1006                         on_enter_notify((xcb_enter_notify_event_t *)event);
1007                         break;
1008                 case XCB_LEAVE_NOTIFY:
1009                         on_leave_notify((xcb_leave_notify_event_t *)event);
1010                         break;
1011                 case XCB_FOCUS_IN:
1012                         on_focus_in((xcb_focus_in_event_t *)event);
1013                         break;
1014                 case XCB_FOCUS_OUT:
1015                         on_focus_out((xcb_focus_out_event_t *)event);
1016                         break;
1017
1018                 /* Window management */
1019                 case XCB_CREATE_NOTIFY:
1020                         on_create_notify((xcb_create_notify_event_t *)event);
1021                         break;
1022                 case XCB_DESTROY_NOTIFY:
1023                         on_destroy_notify((xcb_destroy_notify_event_t *)event);
1024                         break;
1025                 case XCB_UNMAP_NOTIFY:
1026                         on_unmap_notify((xcb_unmap_notify_event_t *)event);
1027                         break;
1028                 case XCB_MAP_NOTIFY:
1029                         on_map_notify((xcb_map_notify_event_t *)event);
1030                         break;
1031                 case XCB_MAP_REQUEST:
1032                         on_map_request((xcb_map_request_event_t *)event);
1033                         break;
1034                 case XCB_CONFIGURE_REQUEST:
1035                         on_configure_request((xcb_configure_request_event_t *)event);
1036                         break;
1037                 case XCB_PROPERTY_NOTIFY:
1038                         on_property_notify((xcb_property_notify_event_t *)event);
1039                         break;
1040                 case XCB_CLIENT_MESSAGE:
1041                         on_client_message((xcb_client_message_event_t *)event);
1042                         break;
1043
1044                 /* Unknown events */
1045                 default:
1046                         printf("on_event: %d:%02X -> %s\n",
1047                                 XCB_EVENT_SENT(event) != 0,
1048                                 XCB_EVENT_RESPONSE_TYPE(event),
1049                                 xcb_event_get_label(type) ?: "unknown_event");
1050                         break;
1051         }
1052 }
1053
1054 /********************
1055  * System functions *
1056  ********************/
1057
1058 void sys_move(win_t *win, int x, int y, int w, int h)
1059 {
1060         printf("sys_move:  %p - %dx%d @ %d,%d\n",
1061                         win, w, h, x, y);
1062
1063         int b = 2*border;
1064
1065         win->x = x;
1066         win->y = y;
1067         win->w = MAX(w,1+b);
1068         win->h = MAX(h,1+b);
1069         w      = MAX(w-b,1);
1070         h      = MAX(h-b,1);
1071
1072         do_configure_window(win->sys->xcb, x, y, w, h, -1, -1, -1);
1073 }
1074
1075 void sys_raise(win_t *win)
1076 {
1077         printf("sys_raise: %p\n", win);
1078
1079         uint16_t mask = XCB_CONFIG_WINDOW_STACK_MODE;
1080         uint32_t list = XCB_STACK_MODE_ABOVE;
1081
1082         xcb_configure_window(conn, win->sys->xcb, mask, &list);
1083         for (list_t *cur = struts; cur; cur = cur->next)
1084                 xcb_configure_window(conn,
1085                         ((win_t*)cur->data)->sys->xcb, mask, &list);
1086 }
1087
1088 void sys_focus(win_t *win)
1089 {
1090         printf("sys_focus: %p\n", win);
1091         xcb_window_t xcb = win ? win->sys->xcb : root;
1092
1093         xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
1094                         xcb, XCB_CURRENT_TIME);
1095 }
1096
1097 void sys_show(win_t *win, state_t state)
1098 {
1099         printf("sys_show:  %p - %s -> %s\n", win,
1100                         state_map[win->state], state_map[state]);
1101         xcb_window_t xcb = win ? win->sys->xcb : root;
1102
1103         /* Find screen */
1104         win_t full, max;
1105         if (state == ST_FULL || state == ST_MAX) {
1106                 for (list_t *cur = screens; cur; cur = cur->next) {
1107                         full = max = *(win_t*)cur->data;
1108                         if (win->x >= max.x && win->x <= max.x+max.w &&
1109                             win->y >= max.y && win->y <= max.y+max.h)
1110                                 break;
1111                 }
1112                 for (list_t *cur = struts; cur; cur = cur->next) {
1113                         strut_t *strut = &((win_t*)cur->data)->sys->strut;
1114                         full.x -= strut->left;
1115                         full.y -= strut->top;
1116                         full.w += strut->left + strut->right;
1117                         full.h += strut->top  + strut->bottom;
1118                 }
1119         }
1120
1121         /* Change window state */
1122         switch (state) {
1123                 case ST_HIDE:
1124                         xcb_unmap_window(conn, xcb);
1125                         break;
1126
1127                 case ST_SHOW:
1128                         xcb_map_window(conn, xcb);
1129                         do_configure_window(xcb, win->x, win->y,
1130                                         MAX(win->w - 2*border, 1),
1131                                         MAX(win->h - 2*border, 1),
1132                                         border, -1, -1);
1133                         break;
1134
1135                 case ST_FULL:
1136                         xcb_map_window(conn, xcb);
1137                         do_configure_window(xcb, full.x, full.y, full.w, full.h,
1138                                         0, -1, XCB_STACK_MODE_ABOVE);
1139                         break;
1140
1141                 case ST_MAX:
1142                         xcb_map_window(conn, xcb);
1143                         do_configure_window(xcb, max.x, max.y,
1144                                         MAX(max.w - 2*border, 1),
1145                                         MAX(max.h - 2*border, 1),
1146                                         border, -1, XCB_STACK_MODE_ABOVE);
1147                         break;
1148
1149                 case ST_SHADE:
1150                         xcb_map_window(conn, xcb);
1151                         do_configure_window(xcb, -1, -1, -1, stack,
1152                                         border, -1, -1);
1153                         break;
1154
1155                 case ST_ICON:
1156                         xcb_map_window(conn, xcb);
1157                         do_configure_window(xcb, -1, -1, 100, 100,
1158                                         border, -1, -1);
1159                         break;
1160
1161                 case ST_CLOSE:
1162                         if (!do_client_message(xcb, wm_delete))
1163                                 xcb_kill_client(conn, xcb);
1164                         break;
1165         }
1166
1167         /* Update state */
1168         win->sys->state = win->state = state;
1169 }
1170
1171 void sys_watch(win_t *win, event_t ev, mod_t mod)
1172 {
1173         printf("sys_watch: %p - 0x%X,0x%X\n", win, ev, mod2int(mod));
1174         xcb_window_t      xcb  = win ? win->sys->xcb     : root;
1175         xcb_event_mask_t *mask = win ? &win->sys->events : &events;
1176         xcb_mod_mask_t    mods = 0;
1177         xcb_button_t      btn  = 0;
1178         xcb_keycode_t    *code = 0;
1179
1180         switch (ev) {
1181                 case EV_ENTER:
1182                         *mask |= XCB_EVENT_MASK_ENTER_WINDOW;
1183                         xcb_change_window_attributes(conn, xcb, XCB_CW_EVENT_MASK, mask);
1184                         break;
1185
1186                 case EV_LEAVE:
1187                         *mask |= XCB_EVENT_MASK_LEAVE_WINDOW;
1188                         xcb_change_window_attributes(conn, xcb, XCB_CW_EVENT_MASK, mask);
1189                         break;
1190
1191                 case EV_FOCUS:
1192                 case EV_UNFOCUS:
1193                         *mask |= XCB_EVENT_MASK_FOCUS_CHANGE;
1194                         xcb_change_window_attributes(conn, xcb, XCB_CW_EVENT_MASK, mask);
1195                         break;
1196
1197                 case EV_MOUSE0...EV_MOUSE7:
1198                         btn    = event_to_button(ev);
1199                         mods   = mod_to_mask(mod);
1200                         *mask |= mod.up ? XCB_EVENT_MASK_BUTTON_RELEASE
1201                                         : XCB_EVENT_MASK_BUTTON_PRESS;
1202                         xcb_grab_button(conn, 0, xcb, *mask,
1203                                         XCB_GRAB_MODE_ASYNC,
1204                                         XCB_GRAB_MODE_ASYNC,
1205                                         0, 0, btn, mods);
1206                         break;
1207
1208                 default:
1209                         code = event_to_keycodes(ev);
1210                         mods = mod_to_mask(mod);
1211                         for (int i = 0; code && code[i] != XCB_NO_SYMBOL; i++)
1212                                 xcb_grab_key(conn, 1, xcb, mods, code[i],
1213                                                 XCB_GRAB_MODE_ASYNC,
1214                                                 XCB_GRAB_MODE_ASYNC);
1215                         free(code);
1216                         break;
1217         }
1218 }
1219
1220 void sys_unwatch(win_t *win, event_t ev, mod_t mod)
1221 {
1222         printf("sys_unwatch: %p - 0x%X,0x%X\n", win, ev, mod2int(mod));
1223 }
1224
1225 list_t *sys_info(void)
1226 {
1227         printf("sys_info\n");
1228
1229         if (screens == NULL && do_xinerama_check()) {
1230                 /* Add Xinerama screens */
1231                 int ninfo = 0;
1232                 xcb_xinerama_screen_info_t *info = NULL;
1233                 void *reply = do_query_screens(&info, &ninfo);
1234                 for (int i = 0; i < ninfo; i++) {
1235                         win_t *screen = new0(win_t);
1236
1237                         screen->x = info[i].x_org;
1238                         screen->y = info[i].y_org;
1239                         screen->w = info[i].width;
1240                         screen->h = info[i].height;
1241
1242                         screens = list_insert(NULL, screen);
1243
1244                         printf("sys_info: xinerama screen - %dx%d @ %d,%d\n",
1245                                         screen->w, screen->h,
1246                                         screen->x, screen->y);
1247                 }
1248                 free(reply);
1249         }
1250
1251         if (screens == NULL) {
1252                 /* No xinerama support */
1253                 const xcb_setup_t *setup = xcb_get_setup(conn);
1254                 xcb_screen_t      *geom  = xcb_setup_roots_iterator(setup).data;
1255
1256                 win_t *screen = new0(win_t);
1257
1258                 screen->w = geom->width_in_pixels;
1259                 screen->h = geom->height_in_pixels;
1260
1261                 screens = list_insert(NULL, screen);
1262
1263                 printf("sys_info: root screen - %dx%d\n",
1264                                 screen->w, screen->h);
1265         }
1266
1267         return screens;
1268 }
1269
1270 void sys_init(void)
1271 {
1272         printf("sys_init\n");
1273
1274         xcb_void_cookie_t cookie;
1275         xcb_generic_error_t *err;
1276
1277         /* Load configuration */
1278         stack      = conf_get_int("main.stack",      stack);
1279         border     = conf_get_int("main.border",     border);
1280         no_capture = conf_get_int("main.no-capture", no_capture);
1281
1282         /* Connect to display */
1283         if (!(conn = xcb_connect(NULL, NULL)))
1284                 error("xcb connect failed");
1285         if (xcb_connection_has_error(conn))
1286                 error("xcb connection has errors");
1287
1288         /* Get root window */
1289         const xcb_setup_t     *setup = xcb_get_setup(conn);
1290         xcb_screen_iterator_t  iter  = xcb_setup_roots_iterator(setup);
1291         root     = iter.data->root;
1292         colormap = iter.data->default_colormap;
1293
1294         /* Request substructure redirect */
1295         events = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
1296                  XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY;
1297         cookie = xcb_change_window_attributes_checked(conn, root,
1298                         XCB_CW_EVENT_MASK, &events);
1299         if ((err = xcb_request_check(conn, cookie)))
1300                 error("another window manager is already running");
1301
1302         /* Setup X Atoms */
1303         wm_protos = do_intern_atom("WM_PROTOCOLS");
1304         wm_delete = do_intern_atom("WM_DELETE_WINDOW");
1305         wm_nhints = do_intern_atom("WM_NORMAL_HINTS");
1306         if (!wm_protos || !wm_delete || !wm_nhints)
1307                 error("unable to setup atoms");
1308
1309         /* Setup EWMH connection */
1310         if (!do_ewmh_init_atoms())
1311                 error("ewmh setup failed");
1312         xcb_ewmh_set_supported(&ewmh, 0, 82, &ewmh._NET_SUPPORTED);
1313
1314         /* Set EWMH wm window */
1315         uint32_t override = 1;
1316         control = xcb_generate_id(conn);
1317         printf("control window: %d\n", control);
1318         cookie  = xcb_create_window_checked(conn, 0, control, root,
1319                         0, 0, 1, 1, 0, 0, 0,
1320                         XCB_CW_OVERRIDE_REDIRECT, &override);
1321         if ((err = xcb_request_check(conn, cookie)))
1322                 error("can't create control window");
1323         cookie = xcb_ewmh_set_wm_name_checked(&ewmh, control, 5, "wmpus");
1324         if ((err = xcb_request_check(conn, cookie)))
1325                 error("can't set wm name");
1326         cookie = xcb_ewmh_set_supporting_wm_check_checked(&ewmh, root, control);
1327         if ((err = xcb_request_check(conn, cookie)))
1328                 error("can't set control window");
1329
1330         /* Setup for for ST_CLOSE */
1331         xcb_set_close_down_mode(conn, XCB_CLOSE_DOWN_DESTROY_ALL);
1332
1333         /* Allocate key symbols */
1334         if (!(keysyms = xcb_key_symbols_alloc(conn)))
1335                 error("cannot allocate key symbols");
1336
1337         /* Read color information */
1338         clr_focus   = do_alloc_color(0xFF6060);
1339         clr_unfocus = do_alloc_color(0xD8D8FF);
1340         clr_urgent  = do_alloc_color(0xFF0000);
1341 }
1342
1343 void sys_run(void)
1344 {
1345         printf("sys_run\n");
1346
1347         /* Add each initial window */
1348         if (!no_capture) {
1349                 int nkids = 0;
1350                 xcb_window_t *kids = NULL;
1351                 void *reply = do_query_tree(root, &kids, &nkids);
1352                 for(int i = 0; i < nkids; i++) {
1353                         int override=0, mapped=0;
1354                         if (kids[i] == control)
1355                                 continue;
1356                         win_t *win = win_new(kids[i]);
1357                         if (do_get_strut(win->sys->xcb, &win->sys->strut))
1358                                 win_add_strut(win);
1359                         do_get_geometry(kids[i], &win->x, &win->y, &win->w, &win->h);
1360                         do_get_window_attributes(kids[i], &override, &mapped);
1361                         printf("  found %-8u %dx%d @ %d,%d --%s%s\n", kids[i],
1362                                         win->w, win->h, win->x, win->y,
1363                                         override ? " override" : "",
1364                                         mapped   ? " mapped"   : "");
1365                         state_t state = mapped ? ST_SHOW : ST_HIDE;
1366                         win->sys->mapped = mapped;
1367                         do_get_type(kids[i], &win->type);
1368                         do_get_icccm_state(kids[i], &state);
1369                         do_get_ewmh_state(kids[i], &state);
1370                         send_manage(win, !override);
1371                         send_state(win, state);
1372                 }
1373                 free(reply);
1374                 xcb_flush(conn);
1375         }
1376
1377         /* Main loop */
1378         running = 1;
1379         while (running)
1380         {
1381                 int status;
1382                 xcb_generic_event_t *event;
1383                 if (!(event = xcb_wait_for_event(conn)))
1384                         break;
1385                 on_event(event);
1386                 free(event);
1387                 if (!(status = xcb_flush(conn)))
1388                         break;
1389         }
1390 }
1391
1392 void sys_exit(void)
1393 {
1394         printf("sys_exit\n");
1395
1396         xcb_client_message_event_t msg = {
1397                 .response_type  = XCB_CLIENT_MESSAGE,
1398                 .format         = 32,
1399                 .window         = control,
1400                 .type           = wm_protos,
1401                 .data.data32[0] = wm_delete,
1402                 .data.data32[1] = XCB_CURRENT_TIME,
1403         };
1404         xcb_send_event(conn, 0, control, XCB_EVENT_MASK_NO_EVENT,
1405                         (const char *)&msg);
1406         xcb_flush(conn);
1407 }
1408
1409 void sys_free(void)
1410 {
1411         printf("sys_free\n");
1412
1413         xcb_void_cookie_t cookie;
1414         xcb_generic_error_t *err;
1415
1416         /* unregister wm */ 
1417         cookie = xcb_delete_property_checked(conn, root,
1418                         ewmh._NET_SUPPORTING_WM_CHECK);
1419         if ((err = xcb_request_check(conn, cookie)))
1420                 warn("can't remove control window");
1421
1422         cookie = xcb_destroy_window_checked(conn, control);
1423         if ((err = xcb_request_check(conn, cookie)))
1424                 warn("can't destroy control window");
1425
1426         /* close connection */
1427         xcb_ewmh_connection_wipe(&ewmh);
1428         xcb_key_symbols_free(keysyms);
1429         xcb_disconnect(conn);
1430
1431         /* free local data */
1432         while (screens)
1433                 screens = list_remove(screens, screens, 1);
1434         tdestroy(cache, (void(*)(void*))win_free);
1435 }