]> Pileus Git - wmpus/blob - sys-xcb.c
Add input focus
[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
21 #include <xcb/xcb.h>
22 #include <xcb/xcb_event.h>
23 #include <xcb/xcb_keysyms.h>
24 #include <xcb/xinerama.h>
25
26 #include "util.h"
27 #include "conf.h"
28 #include "types.h"
29 #include "sys.h"
30 #include "wm.h"
31
32 /* Configuration */
33 static int border     = 2;
34 static int stack      = 25;
35 static int no_capture = 0;
36
37 /* Internal structures */
38 struct win_sys {
39         xcb_window_t     xcb;    // xcb window id
40         xcb_event_mask_t events; // currently watch events
41         int override;            // normal vs override redirect
42         int mapped;              // window is currently mapped
43 };
44
45 /* Global data */
46 static xcb_connection_t  *conn;
47 static xcb_key_symbols_t *keysyms;
48 static xcb_window_t       root;
49 static xcb_event_mask_t   events;
50 static list_t            *screens;
51 static void              *cache;
52
53
54 /************************
55  * Conversion functions *
56  ************************/
57
58 /* Key presses */
59 static struct {
60         event_t      ev;
61         xcb_keysym_t sym;
62 } keysym_map[] = {
63         { EV_LEFT,     0xFF51 },
64         { EV_RIGHT,    0xFF53 },
65         { EV_UP,       0xFF52 },
66         { EV_DOWN,     0xFF54 },
67         { EV_HOME,     0xFF50 },
68         { EV_END,      0xFF57 },
69         { EV_PAGEUP,   0xFF55 },
70         { EV_PAGEDOWN, 0xFF56 },
71         { EV_F1,       0xFFBE },
72         { EV_F2,       0xFFBF },
73         { EV_F3,       0xFFC0 },
74         { EV_F4,       0xFFC1 },
75         { EV_F5,       0xFFC2 },
76         { EV_F6,       0xFFC3 },
77         { EV_F7,       0xFFC4 },
78         { EV_F8,       0xFFC5 },
79         { EV_F9,       0xFFC6 },
80         { EV_F10,      0xFFC7 },
81         { EV_F11,      0xFFC8 },
82         { EV_F12,      0xFFC9 },
83 };
84
85 static xcb_keycode_t *event_to_keycodes(event_t ev)
86 {
87         xcb_keycode_t *codes = NULL;
88
89         /* Get keysym */
90         xcb_keysym_t keysym = map_get(keysym_map, ev, ev, sym, ev);
91
92         /* Get keycodes */
93         if (!(codes = xcb_key_symbols_get_keycode(keysyms, keysym)))
94                 warn("no keycode found for %d->%d", ev, keysym);
95
96         return codes;
97 }
98
99 static event_t keycode_to_event(xcb_keycode_t code)
100 {
101         /* Get keysym */
102         xcb_keysym_t keysym = xcb_key_symbols_get_keysym(keysyms, code, 0);
103
104         /* Get event */
105         return map_get(keysym_map, sym,keysym, ev,keysym);
106 }
107
108 /* Button presses */
109 static event_t button_to_event(xcb_button_t btn)
110 {
111         return EV_MOUSE0 + btn;
112 }
113
114 static xcb_button_t event_to_button(event_t ev)
115 {
116         return ev - EV_MOUSE0;
117 }
118
119 /* Modifier masks */
120 static xcb_mod_mask_t mod_to_mask(mod_t mod)
121 {
122         xcb_mod_mask_t mask = 0;
123         if (mod.alt)   mask |= XCB_MOD_MASK_1;
124         if (mod.ctrl)  mask |= XCB_MOD_MASK_CONTROL;
125         if (mod.shift) mask |= XCB_MOD_MASK_SHIFT;
126         if (mod.win)   mask |= XCB_MOD_MASK_4;
127         return mask;
128 }
129
130 static mod_t mask_to_mod(xcb_mod_mask_t mask, int up)
131 {
132         mod_t mod = { .up = up };
133         if (mask & XCB_MOD_MASK_1)       mod.alt   = 1;
134         if (mask & XCB_MOD_MASK_CONTROL) mod.ctrl  = 1;
135         if (mask & XCB_MOD_MASK_SHIFT)   mod.shift = 1;
136         if (mask & XCB_MOD_MASK_4)       mod.win   = 1;
137         return mod;
138 }
139
140 /* Mouse pointers */
141 static ptr_t list_to_ptr(int16_t *list)
142 {
143         ptr_t ptr = {};
144         ptr.rx = list[0]; // root_x
145         ptr.ry = list[1]; // root_y
146         ptr.x  = list[2]; // event_x
147         ptr.y  = list[3]; // event_y
148         return ptr;
149 }
150
151 /********************
152  * Window functions *
153  ********************/
154
155 static int win_cmp(const void *_a, const void *_b)
156 {
157         const win_t *a = _a;
158         const win_t *b = _b;
159         if (a->sys->xcb < b->sys->xcb) return -1;
160         if (a->sys->xcb > b->sys->xcb) return  1;
161         return 0;
162 }
163
164 static win_t *win_get(xcb_window_t xcb)
165 {
166         win_sys_t sys = { .xcb =  xcb };
167         win_t     key = { .sys = &sys };
168
169         win_t   **win = tfind(&key, &cache, win_cmp);
170
171         if (!win) {
172                 warn("no window for %u", xcb);
173                 return NULL;
174         }
175
176         return *win;
177 }
178
179 /****************
180  * XCB Wrappers *
181  ****************/
182
183 static int do_query_tree(xcb_window_t win, xcb_window_t **kids)
184 {
185         xcb_query_tree_cookie_t cookie =
186                 xcb_query_tree(conn, win);
187         if (!cookie.sequence)
188                 return warn("do_query_tree: %d - bad cookie", win);
189
190         xcb_query_tree_reply_t *reply =
191                 xcb_query_tree_reply(conn, cookie, NULL);
192         if (!reply)
193                 return warn("do_query_tree: %d - no reply", win);
194
195         int nkids = xcb_query_tree_children_length(reply);
196         *kids = xcb_query_tree_children(reply);
197         printf("do_query_tree: %d - n=%d\n", win, nkids);
198         return nkids;
199 }
200
201 static int do_get_geometry(xcb_window_t win,
202                 int *x, int *y, int *w, int *h)
203 {
204         xcb_get_geometry_cookie_t cookie =
205                 xcb_get_geometry(conn, win);
206         if (!cookie.sequence)
207                 return warn("do_get_geometry: %d - bad cookie", win);
208
209         xcb_get_geometry_reply_t *reply =
210                 xcb_get_geometry_reply(conn, cookie, NULL);
211         if (!reply)
212                 return warn("do_get_geometry: %d - no reply", win);
213
214         printf("do_get_geometry: %d - %dx%d @ %d,%d\n",
215                         win, reply->width, reply->height, reply->x, reply->y);
216         *x = reply->x;
217         *y = reply->y;
218         *w = reply->width;
219         *h = reply->height;
220         return 1;
221 }
222
223 static int do_get_window_attributes(xcb_window_t win,
224                 int *override, int *mapped)
225 {
226         xcb_get_window_attributes_cookie_t cookie =
227                 xcb_get_window_attributes(conn, win);
228         if (!cookie.sequence)
229                 return warn("do_get_window_attributes: %d - bad cookie", win);
230
231         xcb_get_window_attributes_reply_t *reply =
232                 xcb_get_window_attributes_reply(conn, cookie, NULL);
233         if (!reply)
234                 return warn("do_get_window_attributes: %d - no reply ", win);
235
236         printf("do_get_window_attributes: %d - %d\n",
237                         win, reply->override_redirect);
238         *override = reply->override_redirect;
239         *mapped   = reply->map_state != XCB_MAP_STATE_UNMAPPED;
240         return 1;
241 }
242
243 static int do_xinerama_check(void)
244 {
245         const xcb_query_extension_reply_t *data =
246                 xcb_get_extension_data(conn, &xcb_xinerama_id);
247         if (!data || !data->present)
248                 return warn("do_xinerama_check: no ext");
249
250         xcb_xinerama_is_active_cookie_t cookie =
251                 xcb_xinerama_is_active(conn);
252         if (!cookie.sequence)
253                 return warn("do_xinerama_check: no cookie");
254
255         xcb_xinerama_is_active_reply_t *reply =
256                 xcb_xinerama_is_active_reply(conn, cookie, NULL);
257         if (!reply)
258                 warn("do_xinerama_check: no reply");
259
260         printf("do_xinerama_check: %d\n", reply->state);
261         return reply && reply->state;
262 }
263
264 static int do_query_screens(xcb_xinerama_screen_info_t **info)
265 {
266         xcb_xinerama_query_screens_cookie_t cookie =
267                 xcb_xinerama_query_screens(conn);
268         if (!cookie.sequence)
269                 return warn("do_query_screens: bad cookie");
270
271         xcb_xinerama_query_screens_reply_t *reply =
272                 xcb_xinerama_query_screens_reply(conn, cookie, NULL);
273         if (!reply)
274                 return warn("do_query_screens: no reply");
275
276         int ninfo = xcb_xinerama_query_screens_screen_info_length(reply);
277         *info = xcb_xinerama_query_screens_screen_info(reply);
278         printf("do_query_screens: %d screens\n", ninfo);
279         return ninfo;
280 }
281
282 /**********************
283  * X11 Event Handlers *
284  **********************/
285
286 /* Specific events */
287 static void on_key_event(xcb_key_press_event_t *event, int up)
288 {
289         xcb_window_t xcb = event->event == root ?
290                 event->child : event->event;
291         win_t  *win = win_get(xcb);
292         event_t ev  = keycode_to_event(event->detail);
293         mod_t   mod = mask_to_mod(event->state, up);
294         ptr_t   ptr = list_to_ptr(&event->root_x);
295         printf("on_key_event:         xcb=%u -> win=%p\n", xcb, win);
296         wm_handle_event(win, ev, mod, ptr);
297 }
298
299 static void on_button_event(xcb_button_press_event_t *event, int up)
300 {
301         xcb_window_t xcb = event->event == root ?
302                 event->child : event->event;
303         win_t  *win = win_get(xcb);
304         event_t ev  = button_to_event(event->detail);
305         mod_t   mod = mask_to_mod(event->state, up);
306         ptr_t   ptr = list_to_ptr(&event->root_x);
307         printf("on_button_event:      xcb=%u -> win=%p\n", xcb, win);
308
309         if (!wm_handle_event(win, ev, mod, ptr))
310                 xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
311         else if (!up)
312                 xcb_grab_pointer(conn, 1, xcb,
313                                 XCB_EVENT_MASK_POINTER_MOTION |
314                                 XCB_EVENT_MASK_BUTTON_RELEASE,
315                                 XCB_GRAB_MODE_ASYNC,
316                                 XCB_GRAB_MODE_ASYNC,
317                                 0, 0, XCB_CURRENT_TIME);
318         else
319                 xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
320
321 }
322
323 static void on_motion_notify(xcb_motion_notify_event_t *event)
324 {
325         xcb_window_t xcb = event->event == root ?
326                 event->child : event->event;
327         win_t *win = win_get(xcb);
328         ptr_t  ptr = list_to_ptr(&event->root_x);
329         printf("on_motion_notify:     xcb=%u -> win=%p - %d,%d / %d.%d\n", xcb, win,
330                         ptr.x, ptr.y, ptr.rx, ptr.ry);
331         wm_handle_ptr(win, ptr);
332 }
333
334 static void on_create_notify(xcb_create_notify_event_t *event)
335 {
336         win_t     *win = new0(win_t);
337         win_sys_t *sys = new0(win_sys_t);
338
339         printf("on_create_notify:     xcb=%u -> win=%p\n",
340                         event->window, win);
341
342         win->x        = event->x;
343         win->y        = event->y;
344         win->w        = event->width;
345         win->h        = event->height;
346         win->sys      = sys;
347
348         sys->xcb      = event->window;
349         sys->override = event->override_redirect;
350
351         tsearch(win, &cache, win_cmp);
352 }
353
354 static void on_destroy_notify(win_t *win, xcb_destroy_notify_event_t *event)
355 {
356         printf("on_destroy_notify:    xcb=%u -> win=%p\n",
357                         event->window, win);
358
359         tdelete(win, &cache, win_cmp);
360
361         free(win->sys);
362         free(win);
363 }
364
365 static void on_map_request(win_t *win, xcb_map_request_event_t *event)
366 {
367         printf("on_map_request:       xcb=%u -> win=%p\n",
368                         event->window, win);
369
370         if (!win->sys->override && !win->sys->mapped)
371                 wm_insert(win);
372         win->sys->mapped = 1;
373
374         xcb_map_window(conn, win->sys->xcb);
375         sys_move(win, win->x, win->y, win->w, win->h);
376 }
377
378 static void on_configure_request(win_t *win, xcb_configure_request_event_t *event)
379 {
380         printf("on_configure_request: xcb=%u -> win=%p -- %dx%d @ %d,%d\n",
381                         event->window, win,
382                         event->width, event->height,
383                         event->x, event->y);
384
385         win->x = event->x;
386         win->y = event->y;
387         win->w = event->width;
388         win->h = event->height;
389
390         xcb_configure_notify_event_t resp = {
391                 .response_type = XCB_CONFIGURE_NOTIFY,
392                 .event         = win->sys->xcb,
393                 .window        = win->sys->xcb,
394                 .x             = win->x,
395                 .y             = win->y,
396                 .width         = win->w,
397                 .height        = win->h,
398                 .border_width  = border,
399         };
400
401         xcb_send_event(conn, 0, win->sys->xcb,
402                         XCB_EVENT_MASK_STRUCTURE_NOTIFY,
403                         (const char *)&resp);
404 }
405
406 /* Generic Event */
407 static void on_event(xcb_generic_event_t *event)
408 {
409         win_t   *win = NULL;
410
411         int type = XCB_EVENT_RESPONSE_TYPE(event);
412         int sent = XCB_EVENT_SENT(event);
413         const char *name = NULL;
414
415         switch (type) {
416                 /* Input handling */
417                 case XCB_KEY_PRESS:
418                         on_key_event((xcb_key_press_event_t *)event, 0);
419                         break;
420                 case XCB_KEY_RELEASE:
421                         on_key_event((xcb_key_release_event_t *)event, 1);
422                         break;
423                 case XCB_BUTTON_PRESS:
424                         on_button_event((xcb_button_press_event_t *)event, 0);
425                         break;
426                 case XCB_BUTTON_RELEASE:
427                         on_button_event((xcb_button_release_event_t *)event, 1);
428                         break;
429                 case XCB_MOTION_NOTIFY:
430                         on_motion_notify((xcb_motion_notify_event_t *)event);
431                         break;
432
433                 /* Window management */
434                 case XCB_CREATE_NOTIFY:
435                         on_create_notify((xcb_create_notify_event_t *)event);
436                         break;
437                 case XCB_DESTROY_NOTIFY:
438                         if ((win = win_get(((xcb_destroy_notify_event_t *)event)->window)))
439                                 on_destroy_notify(win, (xcb_destroy_notify_event_t *)event);
440                         break;
441                 case XCB_MAP_REQUEST:
442                         if ((win = win_get(((xcb_map_request_event_t *)event)->window)))
443                                 on_map_request(win, (xcb_map_request_event_t *)event);
444                         break;
445                 case XCB_CONFIGURE_REQUEST:
446                         if ((win = win_get(((xcb_configure_request_event_t *)event)->window)))
447                                 on_configure_request(win, (xcb_configure_request_event_t *)event);
448                         break;
449
450                 /* Unknown events */
451                 default:
452                         name = xcb_event_get_label(type);
453                         printf("on_event: %d:%02X -> %s\n",
454                                 !!sent, type, name?:"unknown_event");
455                         break;
456         }
457 }
458
459 /********************
460  * System functions *
461  ********************/
462
463 void sys_move(win_t *win, int x, int y, int w, int h)
464 {
465         printf("sys_move:  %p - %dx%d @ %d,%d\n",
466                         win, w, h, x, y);
467
468         win->x = x;
469         win->y = y;
470         win->w = w;
471         win->h = h;
472
473         uint16_t mask   = XCB_CONFIG_WINDOW_X
474                         | XCB_CONFIG_WINDOW_Y
475                         | XCB_CONFIG_WINDOW_WIDTH
476                         | XCB_CONFIG_WINDOW_HEIGHT;
477         uint32_t list[] = {x, y, w, h};
478
479         xcb_configure_window(conn, win->sys->xcb, mask, list);
480 }
481
482 void sys_raise(win_t *win)
483 {
484         printf("sys_raise: %p\n", win);
485         xcb_circulate_window(conn, XCB_CIRCULATE_RAISE_LOWEST, win->sys->xcb);
486 }
487
488 void sys_focus(win_t *win)
489 {
490         printf("sys_focus: %p\n", win);
491         xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
492                         win->sys->xcb, XCB_CURRENT_TIME);
493 }
494
495 void sys_show(win_t *win, state_t state)
496 {
497         printf("sys_show:  %p - %d\n", win, state);
498 }
499
500 void sys_watch(win_t *win, event_t ev, mod_t mod)
501 {
502         printf("sys_watch: %p - 0x%X,0x%X\n", win, ev, mod2int(mod));
503         xcb_window_t      xcb  = win ? win->sys->xcb     : root;
504         xcb_event_mask_t *mask = win ? &win->sys->events : &events;
505         xcb_mod_mask_t    mods = 0;
506         xcb_button_t      btn  = 0;
507         xcb_keycode_t    *code = 0;
508
509         switch (ev) {
510                 case EV_ENTER:
511                         *mask |= XCB_EVENT_MASK_ENTER_WINDOW;
512                         xcb_change_window_attributes(conn, xcb, XCB_CW_EVENT_MASK, mask);
513                         break;
514
515                 case EV_LEAVE:
516                         *mask |= XCB_EVENT_MASK_LEAVE_WINDOW;
517                         xcb_change_window_attributes(conn, xcb, XCB_CW_EVENT_MASK, mask);
518                         break;
519
520                 case EV_FOCUS:
521                 case EV_UNFOCUS:
522                         *mask |= XCB_EVENT_MASK_FOCUS_CHANGE;
523                         xcb_change_window_attributes(conn, xcb, XCB_CW_EVENT_MASK, mask);
524                         break;
525
526                 case EV_MOUSE0...EV_MOUSE7:
527                         btn    = event_to_button(ev);
528                         mods   = mod_to_mask(mod);
529                         *mask |= mod.up ? XCB_EVENT_MASK_BUTTON_RELEASE
530                                         : XCB_EVENT_MASK_BUTTON_PRESS;
531                         xcb_grab_button(conn, 0, xcb, *mask,
532                                         XCB_GRAB_MODE_ASYNC,
533                                         XCB_GRAB_MODE_ASYNC,
534                                         0, 0, btn, mods);
535                         break;
536
537                 default:
538                         code = event_to_keycodes(ev);
539                         mods = mod_to_mask(mod);
540                         for (int i = 0; code && code[i] != XCB_NO_SYMBOL; i++)
541                                 xcb_grab_key(conn, 1, xcb, mods, code[i],
542                                                 XCB_GRAB_MODE_ASYNC,
543                                                 XCB_GRAB_MODE_ASYNC);
544
545                         break;
546         }
547 }
548
549 void sys_unwatch(win_t *win, event_t ev, mod_t mod)
550 {
551         printf("sys_unwatch: %p - 0x%X,0x%X\n", win, ev, mod2int(mod));
552 }
553
554 list_t *sys_info(void)
555 {
556         printf("sys_info\n");
557
558         if (screens == NULL && do_xinerama_check()) {
559                 /* Add Xinerama screens */
560                 xcb_xinerama_screen_info_t *info = NULL;
561                 int ninfo = do_query_screens(&info);
562                 for (int i = 0; i < ninfo; i++) {
563                         win_t *screen = new0(win_t);
564
565                         screen->x = info[i].x_org;
566                         screen->y = info[i].y_org;
567                         screen->w = info[i].width;
568                         screen->h = info[i].height;
569
570                         screens = list_insert(NULL, screen);
571
572                         printf("sys_info: xinerama screen - %dx%d @ %d,%d\n",
573                                         screen->w, screen->h,
574                                         screen->x, screen->y);
575                 }
576         }
577
578         if (screens == NULL) {
579                 /* No xinerama support */
580                 const xcb_setup_t *setup = xcb_get_setup(conn);
581                 xcb_screen_t      *geom  = xcb_setup_roots_iterator(setup).data;
582
583                 win_t *screen = new0(win_t);
584
585                 screen->w = geom->width_in_pixels;
586                 screen->h = geom->height_in_pixels;
587
588                 screens = list_insert(NULL, screen);
589
590                 printf("sys_info: root screen - %dx%d\n",
591                                 screen->w, screen->h);
592         }
593
594         return screens;
595 }
596
597 void sys_init(void)
598 {
599         printf("sys_init\n");
600
601         /* Load configuration */
602         stack      = conf_get_int("main.stack",      stack);
603         border     = conf_get_int("main.border",     border);
604         no_capture = conf_get_int("main.no-capture", no_capture);
605
606         /* Connect to display */
607         if (!(conn = xcb_connect(NULL, NULL)))
608                 error("xcb connect failed");
609         if (xcb_connection_has_error(conn))
610                 error("xcb connection has errors");
611
612         /* Allocate key symbols */
613         if (!(keysyms = xcb_key_symbols_alloc(conn)))
614                 error("cannot allocate key symbols");
615
616         /* Get root window */
617         const xcb_setup_t     *setup = xcb_get_setup(conn);
618         xcb_screen_iterator_t  iter  = xcb_setup_roots_iterator(setup);
619         root = iter.data->root;
620
621         /* Request substructure redirect */
622         xcb_void_cookie_t cookie;
623         xcb_generic_error_t *err;
624         events = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
625                  XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY;
626         cookie = xcb_change_window_attributes_checked(conn, root,
627                         XCB_CW_EVENT_MASK, &events);
628         if ((err = xcb_request_check(conn, cookie)))
629                 error("another window manager is already running");
630 }
631
632 void sys_run(void)
633 {
634         printf("sys_run\n");
635
636         /* Add each initial window */
637         if (!no_capture) {
638                 xcb_window_t *kids = NULL;
639                 int nkids = do_query_tree(root, &kids);
640                 for(int i = 0; i < nkids; i++) {
641                         win_t *win = new0(win_t);
642                         win->sys = new0(win_sys_t);
643                         win->sys->xcb = kids[i];
644                         do_get_geometry(kids[i], &win->x, &win->y, &win->w, &win->h);
645                         do_get_window_attributes(kids[i],
646                                 &win->sys->override, &win->sys->mapped);
647                         tsearch(win, &cache, win_cmp);
648                         if (!win->sys->override && win->sys->mapped)
649                                 wm_insert(win);
650                 }
651                 xcb_flush(conn);
652         }
653
654         /* Main loop */
655         while (1)
656         {
657                 int status;
658                 xcb_generic_event_t *event;
659                 if (!(event = xcb_wait_for_event(conn)))
660                         break;
661                 on_event(event);
662                 free(event);
663                 if (!(status = xcb_flush(conn)))
664                         break;
665         }
666 }
667
668 void sys_exit(void)
669 {
670         printf("sys_exit\n");
671         if (conn)
672                 xcb_disconnect(conn);
673         conn = NULL;
674 }
675
676 void sys_free(void)
677 {
678         printf("sys_free\n");
679         if (conn)
680                 xcb_disconnect(conn);
681         conn = NULL;
682 }