2 * Copyright (c) 2015 Andy Spencer <andy753421@gmail.com>
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.
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
22 #include <xcb/xcb_event.h>
23 #include <xcb/xinerama.h>
32 static int border = 2;
33 static int stack = 25;
34 static int no_capture = 0;
36 /* Internal structures */
39 int override; // normal vs override redirect
40 int managed; // window is currently managed by wm
44 static xcb_connection_t *conn;
45 static xcb_window_t root;
46 static list_t *screens;
53 static int win_cmp(const void *_a, const void *_b)
57 if (a->sys->xcb < b->sys->xcb) return -1;
58 if (a->sys->xcb > b->sys->xcb) return 1;
62 static win_t *win_get(xcb_window_t xcb)
64 win_sys_t sys = { .xcb = xcb };
65 win_t key = { .sys = &sys };
67 win_t **win = tfind(&key, &cache, win_cmp);
70 warn("no window for %u", xcb);
81 static int do_query_tree(xcb_window_t win, xcb_window_t **kids)
83 xcb_query_tree_cookie_t cookie =
84 xcb_query_tree(conn, win);
86 return warn("do_query_tree: %d - bad cookie", win);
88 xcb_query_tree_reply_t *reply =
89 xcb_query_tree_reply(conn, cookie, NULL);
91 return warn("do_query_tree: %d - no reply", win);
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);
99 static int do_get_geometry(xcb_window_t win,
100 int *x, int *y, int *w, int *h)
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);
107 xcb_get_geometry_reply_t *reply =
108 xcb_get_geometry_reply(conn, cookie, NULL);
110 return warn("do_get_geometry: %d - no reply", win);
112 printf("do_get_geometry: %d - %dx%d @ %d,%d\n",
113 win, reply->width, reply->height, reply->x, reply->y);
121 static int do_get_window_attributes(xcb_window_t win,
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);
129 xcb_get_window_attributes_reply_t *reply =
130 xcb_get_window_attributes_reply(conn, cookie, NULL);
132 return warn("do_get_window_attributes: %d - no reply ", win);
134 printf("do_get_window_attributes: %d - %d\n",
135 win, reply->override_redirect);
136 *override = reply->override_redirect;
140 static int do_xinerama_check(void)
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");
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");
152 xcb_xinerama_is_active_reply_t *reply =
153 xcb_xinerama_is_active_reply(conn, cookie, NULL);
155 warn("do_xinerama_check: no reply");
157 printf("do_xinerama_check: %d\n", reply->state);
158 return reply && reply->state;
161 static int do_query_screens(xcb_xinerama_screen_info_t **info)
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");
168 xcb_xinerama_query_screens_reply_t *reply =
169 xcb_xinerama_query_screens_reply(conn, cookie, NULL);
171 return warn("do_query_screens: no reply");
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);
179 /**********************
180 * X11 Event Handlers *
181 **********************/
183 /* Specific events */
184 static void on_create_notify(xcb_create_notify_event_t *event)
186 win_t *win = new0(win_t);
187 win_sys_t *sys = new0(win_sys_t);
189 printf("on_create_notify: xcb=%u -> win=%p\n",
194 win->w = event->width;
195 win->h = event->height;
198 sys->xcb = event->window;
199 sys->override = event->override_redirect;
201 tsearch(win, &cache, win_cmp);
204 static void on_destroy_notify(win_t *win, xcb_destroy_notify_event_t *event)
206 printf("on_destroy_notify: xcb=%u -> win=%p\n",
209 tdelete(win, &cache, win_cmp);
215 static void on_map_request(win_t *win, xcb_map_request_event_t *event)
217 printf("on_map_request: xcb=%u -> win=%p\n",
220 if (!win->sys->managed) {
222 win->sys->managed = 1;
225 xcb_map_window(conn, win->sys->xcb);
226 sys_move(win, win->x, win->y, win->w, win->h);
229 static void on_configure_request(win_t *win, xcb_configure_request_event_t *event)
231 printf("on_configure_request: xcb=%u -> win=%p -- %dx%d @ %d,%d\n",
233 event->width, event->height,
238 win->w = event->width;
239 win->h = event->height;
241 xcb_configure_notify_event_t resp = {
242 .response_type = XCB_CONFIGURE_NOTIFY,
243 .event = win->sys->xcb,
244 .window = win->sys->xcb,
249 .border_width = border,
252 xcb_send_event(conn, 0, win->sys->xcb,
253 XCB_EVENT_MASK_STRUCTURE_NOTIFY,
254 (const char *)&resp);
258 static void on_event(xcb_generic_event_t *event)
262 int type = XCB_EVENT_RESPONSE_TYPE(event);
263 int sent = XCB_EVENT_SENT(event);
264 const char *name = NULL;
267 case XCB_CREATE_NOTIFY:
268 on_create_notify((xcb_create_notify_event_t *)event);
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);
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);
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);
283 name = xcb_event_get_label(type);
284 printf("on_event: %d:%02X -> %s\n",
285 !!sent, type, name?:"unknown_event");
290 /********************
292 ********************/
294 void sys_move(win_t *win, int x, int y, int w, int h)
296 printf("sys_move: %p - %dx%d @ %d,%d\n",
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};
310 xcb_configure_window(conn, win->sys->xcb, mask, list);
313 void sys_raise(win_t *win)
315 printf("sys_raise: %p\n", win);
318 void sys_focus(win_t *win)
320 printf("sys_focus: %p\n", win);
323 void sys_show(win_t *win, state_t state)
325 printf("sys_show: %p - %d\n", win, state);
328 void sys_watch(win_t *win, event_t ev, mod_t mod)
330 printf("sys_watch: %p - 0x%X,0x%X\n", win, ev, mod2int(mod));
333 void sys_unwatch(win_t *win, event_t ev, mod_t mod)
335 printf("sys_unwatch: %p - 0x%X,0x%X\n", win, ev, mod2int(mod));
338 list_t *sys_info(void)
340 printf("sys_info\n");
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);
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;
354 screens = list_insert(NULL, screen);
356 printf("sys_info: xinerama screen - %dx%d @ %d,%d\n",
357 screen->w, screen->h,
358 screen->x, screen->y);
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;
367 win_t *screen = new0(win_t);
369 screen->w = geom->width_in_pixels;
370 screen->h = geom->height_in_pixels;
372 screens = list_insert(NULL, screen);
374 printf("sys_info: root screen - %dx%d\n",
375 screen->w, screen->h);
383 printf("sys_init\n");
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);
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");
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;
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");
417 /* Add each initial window */
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) {
430 win->sys->managed = 1;
440 xcb_generic_event_t *event;
441 if (!(event = xcb_wait_for_event(conn)))
445 if (!(status = xcb_flush(conn)))
452 printf("sys_exit\n");
454 xcb_disconnect(conn);
460 printf("sys_free\n");
462 xcb_disconnect(conn);