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