]> Pileus Git - wmpus/blob - sys-xcb.c
Add more checks and cleanup xcb wrappers
[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/xinerama.h>
23
24 #include "util.h"
25 #include "conf.h"
26 #include "types.h"
27 #include "sys.h"
28 #include "wm.h"
29
30 /* Configuration */
31 static int border     = 2;
32 static int stack      = 25;
33 static int no_capture = 0;
34
35 /* Internal structures */
36 struct win_sys {
37         xcb_window_t xcb;
38         int override; // normal vs override redirect
39         int managed;  // window is currently managed by wm
40 };
41
42 /* Global data */
43 static xcb_connection_t *conn;
44 static xcb_window_t      root;
45 static list_t           *screens;
46 static void             *cache;
47
48 /*****************
49  * Constant data *
50  *****************/
51
52 const char *event_names[] = {
53         [XCB_KEY_PRESS        ] "key_press",         [XCB_KEY_RELEASE      ] "key_release",
54         [XCB_BUTTON_PRESS     ] "button_press",      [XCB_BUTTON_RELEASE   ] "button_release",
55         [XCB_MOTION_NOTIFY    ] "motion_notify",     [XCB_ENTER_NOTIFY     ] "enter_notify",
56         [XCB_LEAVE_NOTIFY     ] "leave_notify",      [XCB_FOCUS_IN         ] "focus_in",
57         [XCB_FOCUS_OUT        ] "focus_out",         [XCB_KEYMAP_NOTIFY    ] "keymap_notify",
58         [XCB_EXPOSE           ] "expose",            [XCB_GRAPHICS_EXPOSURE] "graphics_exposure",
59         [XCB_NO_EXPOSURE      ] "no_exposure",       [XCB_VISIBILITY_NOTIFY] "visibility_notify",
60         [XCB_CREATE_NOTIFY    ] "create_notify",     [XCB_DESTROY_NOTIFY   ] "destroy_notify",
61         [XCB_UNMAP_NOTIFY     ] "unmap_notify",      [XCB_MAP_NOTIFY       ] "map_notify",
62         [XCB_MAP_REQUEST      ] "map_request",       [XCB_REPARENT_NOTIFY  ] "reparent_notify",
63         [XCB_CONFIGURE_NOTIFY ] "configure_notify",  [XCB_CONFIGURE_REQUEST] "configure_request",
64         [XCB_GRAVITY_NOTIFY   ] "gravity_notify",    [XCB_RESIZE_REQUEST   ] "resize_request",
65         [XCB_CIRCULATE_NOTIFY ] "circulate_notify",  [XCB_CIRCULATE_REQUEST] "circulate_request",
66         [XCB_PROPERTY_NOTIFY  ] "property_notify",   [XCB_SELECTION_CLEAR  ] "selection_clear",
67         [XCB_SELECTION_REQUEST] "selection_request", [XCB_SELECTION_NOTIFY ] "selection_notify",
68         [XCB_COLORMAP_NOTIFY  ] "colormap_notify",   [XCB_CLIENT_MESSAGE   ] "client_message",
69         [XCB_MAPPING_NOTIFY   ] "mapping_notify",    [XCB_GE_GENERIC       ] "ge_generic",
70 };
71
72 /********************
73  * Window functions *
74  ********************/
75
76 static int win_cmp(const void *_a, const void *_b)
77 {
78         const win_t *a = _a;
79         const win_t *b = _b;
80         if (a->sys->xcb < b->sys->xcb) return -1;
81         if (a->sys->xcb > b->sys->xcb) return  1;
82         return 0;
83 }
84
85 static win_t *win_get(xcb_window_t xcb)
86 {
87         win_sys_t sys = { .xcb =  xcb };
88         win_t     key = { .sys = &sys };
89
90         win_t   **win = tfind(&key, &cache, win_cmp);
91
92         if (!win) {
93                 warn("no window for %u", xcb);
94                 return NULL;
95         }
96
97         return *win;
98 }
99
100 /****************
101  * XCB Wrappers *
102  ****************/
103
104 static int do_query_tree(xcb_window_t win, xcb_window_t **kids)
105 {
106         xcb_query_tree_cookie_t cookie =
107                 xcb_query_tree(conn, win);
108         if (!cookie.sequence)
109                 return warn("do_query_tree: %d - bad cookie", win);
110
111         xcb_query_tree_reply_t *reply =
112                 xcb_query_tree_reply(conn, cookie, NULL);
113         if (!reply)
114                 return warn("do_query_tree: %d - no reply", win);
115
116         int nkids = xcb_query_tree_children_length(reply);
117         *kids = xcb_query_tree_children(reply);
118         printf("do_query_tree: %d - n=%d\n", win, nkids);
119         return nkids;
120 }
121
122 static int do_get_geometry(xcb_window_t win,
123                 int *x, int *y, int *w, int *h)
124 {
125         xcb_get_geometry_cookie_t cookie =
126                 xcb_get_geometry(conn, win);
127         if (!cookie.sequence)
128                 return warn("do_get_geometry: %d - bad cookie", win);
129
130         xcb_get_geometry_reply_t *reply =
131                 xcb_get_geometry_reply(conn, cookie, NULL);
132         if (!reply)
133                 return warn("do_get_geometry: %d - no reply", win);
134
135         printf("do_get_geometry: %d - %dx%d @ %d,%d\n",
136                         win, reply->width, reply->height, reply->x, reply->y);
137         *x = reply->x;
138         *y = reply->y;
139         *w = reply->width;
140         *h = reply->height;
141         return 1;
142 }
143
144 static int do_get_window_attributes(xcb_window_t win,
145                 int *override)
146 {
147         xcb_get_window_attributes_cookie_t cookie =
148                 xcb_get_window_attributes(conn, win);
149         if (!cookie.sequence)
150                 return warn("do_get_window_attributes: %d - bad cookie", win);
151
152         xcb_get_window_attributes_reply_t *reply =
153                 xcb_get_window_attributes_reply(conn, cookie, NULL);
154         if (!reply)
155                 return warn("do_get_window_attributes: %d - no reply ", win);
156
157         printf("do_get_window_attributes: %d - %d\n",
158                         win, reply->override_redirect);
159         *override = reply->override_redirect;
160         return 1;
161 }
162
163 static int do_xinerama_check(void)
164 {
165         const xcb_query_extension_reply_t *data =
166                 xcb_get_extension_data(conn, &xcb_xinerama_id);
167         if (!data || !data->present)
168                 return warn("do_xinerama_check: no ext");
169
170         xcb_xinerama_is_active_cookie_t cookie =
171                 xcb_xinerama_is_active(conn);
172         if (!cookie.sequence)
173                 return warn("do_xinerama_check: no cookie");
174
175         xcb_xinerama_is_active_reply_t *reply =
176                 xcb_xinerama_is_active_reply(conn, cookie, NULL);
177         if (!reply)
178                 warn("do_xinerama_check: no reply");
179
180         printf("do_xinerama_check: %d\n", reply->state);
181         return reply && reply->state;
182 }
183
184 static int do_query_screens(xcb_xinerama_screen_info_t **info)
185 {
186         xcb_xinerama_query_screens_cookie_t cookie =
187                 xcb_xinerama_query_screens(conn);
188         if (!cookie.sequence)
189                 return warn("do_query_screens: bad cookie");
190
191         xcb_xinerama_query_screens_reply_t *reply =
192                 xcb_xinerama_query_screens_reply(conn, cookie, NULL);
193         if (!reply)
194                 return warn("do_query_screens: no reply");
195
196         int ninfo = xcb_xinerama_query_screens_screen_info_length(reply);
197         *info = xcb_xinerama_query_screens_screen_info(reply);
198         printf("do_query_screens: %d screens\n", ninfo);
199         return ninfo;
200 }
201
202 /**********************
203  * X11 Event Handlers *
204  **********************/
205
206 /* Specific events */
207 static void on_create_notify(xcb_create_notify_event_t *event)
208 {
209         win_t     *win = new0(win_t);
210         win_sys_t *sys = new0(win_sys_t);
211
212         printf("on_create_notify:     xcb=%u -> win=%p\n",
213                         event->window, win);
214
215         win->x        = event->x;
216         win->y        = event->y;
217         win->w        = event->width;
218         win->h        = event->height;
219         win->sys      = sys;
220
221         sys->xcb      = event->window;
222         sys->override = event->override_redirect;
223
224         tsearch(win, &cache, win_cmp);
225 }
226
227 static void on_destroy_notify(win_t *win, xcb_destroy_notify_event_t *event)
228 {
229         printf("on_destroy_notify:    xcb=%u -> win=%p\n",
230                         event->window, win);
231
232         tdelete(win, &cache, win_cmp);
233
234         free(win->sys);
235         free(win);
236 }
237
238 static void on_map_request(win_t *win, xcb_map_request_event_t *event)
239 {
240         printf("on_map_request:       xcb=%u -> win=%p\n",
241                         event->window, win);
242
243         if (!win->sys->managed) {
244                 wm_insert(win);
245                 win->sys->managed = 1;
246         }
247
248         xcb_map_window(conn, win->sys->xcb);
249         sys_move(win, win->x, win->y, win->w, win->h);
250 }
251
252 static void on_configure_request(win_t *win, xcb_configure_request_event_t *event)
253 {
254         printf("on_configure_request: xcb=%u -> win=%p -- %dx%d @ %d,%d\n",
255                         event->window, win,
256                         event->width, event->height,
257                         event->x, event->y);
258
259         win->x = event->x;
260         win->y = event->y;
261         win->w = event->width;
262         win->h = event->height;
263
264         xcb_configure_notify_event_t resp = {
265                 .response_type = XCB_CONFIGURE_NOTIFY,
266                 .event         = win->sys->xcb,
267                 .window        = win->sys->xcb,
268                 .x             = win->x,
269                 .y             = win->y,
270                 .width         = win->w,
271                 .height        = win->h,
272                 .border_width  = border,
273         };
274
275         xcb_send_event(conn, 0, win->sys->xcb,
276                         XCB_EVENT_MASK_STRUCTURE_NOTIFY,
277                         (const char *)&resp);
278 }
279
280 /* Generic Event */
281 static void on_event(xcb_generic_event_t *event)
282 {
283         win_t *win = NULL;
284         switch (event->response_type) {
285                 case XCB_CREATE_NOTIFY:
286                         on_create_notify((xcb_create_notify_event_t *)event);
287                         break;
288                 case XCB_DESTROY_NOTIFY:
289                         if ((win = win_get(((xcb_destroy_notify_event_t *)event)->window)))
290                                 on_destroy_notify(win, (xcb_destroy_notify_event_t *)event);
291                         break;
292                 case XCB_MAP_REQUEST:
293                         if ((win = win_get(((xcb_map_request_event_t *)event)->window)))
294                                 on_map_request(win, (xcb_map_request_event_t *)event);
295                         break;
296                 case XCB_CONFIGURE_REQUEST:
297                         if ((win = win_get(((xcb_configure_request_event_t *)event)->window)))
298                                 on_configure_request(win, (xcb_configure_request_event_t *)event);
299                         break;
300                 default:
301                         printf("on_%s\n", event_names[event->response_type] ?: "unknown_event");
302                         break;
303         }
304 }
305
306 /********************
307  * System functions *
308  ********************/
309
310 void sys_move(win_t *win, int x, int y, int w, int h)
311 {
312         printf("sys_move: %p - %dx%d @ %d,%d\n",
313                         win, w, h, x, y);
314
315         win->x = x;
316         win->y = y;
317         win->w = w;
318         win->h = h;
319
320         uint16_t mask   = XCB_CONFIG_WINDOW_X
321                         | XCB_CONFIG_WINDOW_Y
322                         | XCB_CONFIG_WINDOW_WIDTH
323                         | XCB_CONFIG_WINDOW_HEIGHT;
324         uint32_t list[] = {x, y, w, h};
325
326         xcb_configure_window(conn, win->sys->xcb, mask, list);
327 }
328
329 void sys_raise(win_t *win)
330 {
331         printf("sys_raise: %p\n", win);
332 }
333
334 void sys_focus(win_t *win)
335 {
336         printf("sys_focus: %p\n", win);
337 }
338
339 void sys_show(win_t *win, state_t state)
340 {
341         printf("sys_show: %p - %d\n", win, state);
342 }
343
344 void sys_watch(win_t *win, event_t ev, mod_t mod)
345 {
346         printf("sys_watch: %p - 0x%X,0x%X\n", win, ev, mod2int(mod));
347 }
348
349 void sys_unwatch(win_t *win, event_t ev, mod_t mod)
350 {
351         printf("sys_unwatch: %p - 0x%X,0x%X\n", win, ev, mod2int(mod));
352 }
353
354 list_t *sys_info(void)
355 {
356         printf("sys_info\n");
357
358         if (screens == NULL && do_xinerama_check()) {
359                 /* Add Xinerama screens */
360                 xcb_xinerama_screen_info_t *info = NULL;
361                 int ninfo = do_query_screens(&info);
362                 for (int i = 0; i < ninfo; i++) {
363                         win_t *screen = new0(win_t);
364
365                         screen->x = info[i].x_org;
366                         screen->y = info[i].y_org;
367                         screen->w = info[i].width;
368                         screen->h = info[i].height;
369
370                         screens = list_insert(NULL, screen);
371
372                         printf("sys_info: xinerama screen - %dx%d @ %d,%d\n",
373                                         screen->w, screen->h,
374                                         screen->x, screen->y);
375                 }
376         }
377
378         if (screens == NULL) {
379                 /* No xinerama support */
380                 const xcb_setup_t *setup = xcb_get_setup(conn);
381                 xcb_screen_t      *geom  = xcb_setup_roots_iterator(setup).data;
382
383                 win_t *screen = new0(win_t);
384
385                 screen->w = geom->width_in_pixels;
386                 screen->h = geom->height_in_pixels;
387
388                 screens = list_insert(NULL, screen);
389
390                 printf("sys_info: root screen - %dx%d\n",
391                                 screen->w, screen->h);
392         }
393
394         return screens;
395 }
396
397 void sys_init(void)
398 {
399         printf("sys_init\n");
400
401         /* Load configuration */
402         stack      = conf_get_int("main.stack",      stack);
403         border     = conf_get_int("main.border",     border);
404         no_capture = conf_get_int("main.no-capture", no_capture);
405
406         /* Connect to display */
407         if (!(conn = xcb_connect(NULL, NULL)))
408                 error("xcb connect failed");
409         if (xcb_connection_has_error(conn))
410                 error("xcb connection has errors");
411
412         /* Get root window */
413         const xcb_setup_t     *setup = xcb_get_setup(conn);
414         xcb_screen_iterator_t  iter  = xcb_setup_roots_iterator(setup);
415         root = iter.data->root;
416
417         /* Request substructure redirect */
418         xcb_event_mask_t     mask;
419         xcb_void_cookie_t    cookie;
420         xcb_generic_error_t *err;
421         mask   = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
422                  XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY;
423         cookie = xcb_change_window_attributes_checked(conn, root,
424                         XCB_CW_EVENT_MASK, &mask);
425         if ((err = xcb_request_check(conn, cookie)))
426                 error("Another window manager is already running");
427 }
428
429 void sys_run(void)
430 {
431         printf("sys_run\n");
432
433         /* Add each initial window */
434         if (!no_capture) {
435                 xcb_window_t *kids = NULL;
436                 int nkids = do_query_tree(root, &kids);
437                 for(int i = 0; i < nkids; i++) {
438                         win_t *win = new0(win_t);
439                         win->sys = new0(win_sys_t);
440                         win->sys->xcb = kids[i];
441                         do_get_geometry(kids[i], &win->x, &win->y, &win->w, &win->h);
442                         do_get_window_attributes(kids[i], &win->sys->override);
443                         tsearch(win, &cache, win_cmp);
444                         if (!win->sys->override) {
445                                 wm_insert(win);
446                                 win->sys->managed = 1;
447                         }
448                 }
449                 xcb_flush(conn);
450         }
451
452         /* Main loop */
453         while (1)
454         {
455                 int status;
456                 xcb_generic_event_t *event;
457                 if (!(event = xcb_wait_for_event(conn)))
458                         break;
459                 on_event(event);
460                 free(event);
461                 if (!(status = xcb_flush(conn)))
462                         break;
463         }
464 }
465
466 void sys_exit(void)
467 {
468         printf("sys_exit\n");
469         if (conn)
470                 xcb_disconnect(conn);
471         conn = NULL;
472 }
473
474 void sys_free(void)
475 {
476         printf("sys_free\n");
477         if (conn)
478                 xcb_disconnect(conn);
479         conn = NULL;
480 }