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/xinerama.h>
31 static int border = 2;
32 static int stack = 25;
33 static int no_capture = 0;
35 /* Internal structures */
38 int override; // normal vs override redirect
39 int managed; // window is currently managed by wm
43 static xcb_connection_t *conn;
44 static xcb_window_t root;
45 static list_t *screens;
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",
76 static int win_cmp(const void *_a, const void *_b)
80 if (a->sys->xcb < b->sys->xcb) return -1;
81 if (a->sys->xcb > b->sys->xcb) return 1;
85 static win_t *win_get(xcb_window_t xcb)
87 win_sys_t sys = { .xcb = xcb };
88 win_t key = { .sys = &sys };
90 win_t **win = tfind(&key, &cache, win_cmp);
93 warn("no window for %u", xcb);
104 static int do_query_tree(xcb_window_t win, xcb_window_t **kids)
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);
111 xcb_query_tree_reply_t *reply =
112 xcb_query_tree_reply(conn, cookie, NULL);
114 return warn("do_query_tree: %d - no reply", win);
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);
122 static int do_get_geometry(xcb_window_t win,
123 int *x, int *y, int *w, int *h)
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);
130 xcb_get_geometry_reply_t *reply =
131 xcb_get_geometry_reply(conn, cookie, NULL);
133 return warn("do_get_geometry: %d - no reply", win);
135 printf("do_get_geometry: %d - %dx%d @ %d,%d\n",
136 win, reply->width, reply->height, reply->x, reply->y);
144 static int do_get_window_attributes(xcb_window_t win,
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);
152 xcb_get_window_attributes_reply_t *reply =
153 xcb_get_window_attributes_reply(conn, cookie, NULL);
155 return warn("do_get_window_attributes: %d - no reply ", win);
157 printf("do_get_window_attributes: %d - %d\n",
158 win, reply->override_redirect);
159 *override = reply->override_redirect;
163 static int do_xinerama_check(void)
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");
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");
175 xcb_xinerama_is_active_reply_t *reply =
176 xcb_xinerama_is_active_reply(conn, cookie, NULL);
178 warn("do_xinerama_check: no reply");
180 printf("do_xinerama_check: %d\n", reply->state);
181 return reply && reply->state;
184 static int do_query_screens(xcb_xinerama_screen_info_t **info)
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");
191 xcb_xinerama_query_screens_reply_t *reply =
192 xcb_xinerama_query_screens_reply(conn, cookie, NULL);
194 return warn("do_query_screens: no reply");
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);
202 /**********************
203 * X11 Event Handlers *
204 **********************/
206 /* Specific events */
207 static void on_create_notify(xcb_create_notify_event_t *event)
209 win_t *win = new0(win_t);
210 win_sys_t *sys = new0(win_sys_t);
212 printf("on_create_notify: xcb=%u -> win=%p\n",
217 win->w = event->width;
218 win->h = event->height;
221 sys->xcb = event->window;
222 sys->override = event->override_redirect;
224 tsearch(win, &cache, win_cmp);
227 static void on_destroy_notify(win_t *win, xcb_destroy_notify_event_t *event)
229 printf("on_destroy_notify: xcb=%u -> win=%p\n",
232 tdelete(win, &cache, win_cmp);
238 static void on_map_request(win_t *win, xcb_map_request_event_t *event)
240 printf("on_map_request: xcb=%u -> win=%p\n",
243 if (!win->sys->managed) {
245 win->sys->managed = 1;
248 xcb_map_window(conn, win->sys->xcb);
249 sys_move(win, win->x, win->y, win->w, win->h);
252 static void on_configure_request(win_t *win, xcb_configure_request_event_t *event)
254 printf("on_configure_request: xcb=%u -> win=%p -- %dx%d @ %d,%d\n",
256 event->width, event->height,
261 win->w = event->width;
262 win->h = event->height;
264 xcb_configure_notify_event_t resp = {
265 .response_type = XCB_CONFIGURE_NOTIFY,
266 .event = win->sys->xcb,
267 .window = win->sys->xcb,
272 .border_width = border,
275 xcb_send_event(conn, 0, win->sys->xcb,
276 XCB_EVENT_MASK_STRUCTURE_NOTIFY,
277 (const char *)&resp);
281 static void on_event(xcb_generic_event_t *event)
284 switch (event->response_type) {
285 case XCB_CREATE_NOTIFY:
286 on_create_notify((xcb_create_notify_event_t *)event);
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);
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);
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);
301 printf("on_%s\n", event_names[event->response_type] ?: "unknown_event");
306 /********************
308 ********************/
310 void sys_move(win_t *win, int x, int y, int w, int h)
312 printf("sys_move: %p - %dx%d @ %d,%d\n",
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};
326 xcb_configure_window(conn, win->sys->xcb, mask, list);
329 void sys_raise(win_t *win)
331 printf("sys_raise: %p\n", win);
334 void sys_focus(win_t *win)
336 printf("sys_focus: %p\n", win);
339 void sys_show(win_t *win, state_t state)
341 printf("sys_show: %p - %d\n", win, state);
344 void sys_watch(win_t *win, event_t ev, mod_t mod)
346 printf("sys_watch: %p - 0x%X,0x%X\n", win, ev, mod2int(mod));
349 void sys_unwatch(win_t *win, event_t ev, mod_t mod)
351 printf("sys_unwatch: %p - 0x%X,0x%X\n", win, ev, mod2int(mod));
354 list_t *sys_info(void)
356 printf("sys_info\n");
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);
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;
370 screens = list_insert(NULL, screen);
372 printf("sys_info: xinerama screen - %dx%d @ %d,%d\n",
373 screen->w, screen->h,
374 screen->x, screen->y);
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;
383 win_t *screen = new0(win_t);
385 screen->w = geom->width_in_pixels;
386 screen->h = geom->height_in_pixels;
388 screens = list_insert(NULL, screen);
390 printf("sys_info: root screen - %dx%d\n",
391 screen->w, screen->h);
399 printf("sys_init\n");
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);
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");
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;
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");
433 /* Add each initial window */
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) {
446 win->sys->managed = 1;
456 xcb_generic_event_t *event;
457 if (!(event = xcb_wait_for_event(conn)))
461 if (!(status = xcb_flush(conn)))
468 printf("sys_exit\n");
470 xcb_disconnect(conn);
476 printf("sys_free\n");
478 xcb_disconnect(conn);