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