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