4 * i3 - an improved dynamic tiling window manager
6 * © 2009 Michael Stapelberg and contributors
8 * See file LICENSE for license information.
15 #include <sys/types.h>
22 #include <X11/XKBlib.h>
23 #include <X11/extensions/XKB.h>
26 #include <xcb/xcb_atom.h>
27 #include <xcb/xcb_aux.h>
28 #include <xcb/xcb_event.h>
29 #include <xcb/xcb_property.h>
30 #include <xcb/xcb_keysyms.h>
31 #include <xcb/xcb_icccm.h>
32 #include <xcb/xinerama.h>
46 /* This is the path to i3, copied from argv[0] when starting up */
49 /* This is our connection to X11 for use with XKB */
52 /* The list of key bindings */
53 struct bindings_head bindings = TAILQ_HEAD_INITIALIZER(bindings);
55 /* The list of exec-lines */
56 struct autostarts_head autostarts = TAILQ_HEAD_INITIALIZER(autostarts);
58 /* The list of assignments */
59 struct assignments_head assignments = TAILQ_HEAD_INITIALIZER(assignments);
61 /* This is a list of Stack_Windows, global, for easier/faster access on expose events */
62 struct stack_wins_head stack_wins = SLIST_HEAD_INITIALIZER(stack_wins);
64 /* The event handlers need to be global because they are accessed by our custom event handler
65 in handle_button_press(), needed for graphical resizing */
66 xcb_event_handlers_t evenths;
67 xcb_atom_t atoms[NUM_ATOMS];
72 * Do some sanity checks and then reparent the window.
75 void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *conn, xcb_window_t window, window_attributes_t wa) {
76 LOG("managing window.\n");
77 xcb_drawable_t d = { window };
78 xcb_get_geometry_cookie_t geomc;
79 xcb_get_geometry_reply_t *geom;
80 xcb_get_window_attributes_reply_t *attr = 0;
82 if (wa.tag == TAG_COOKIE) {
83 /* Check if the window is mapped (it could be not mapped when intializing and
84 calling manage_window() for every window) */
85 if ((attr = xcb_get_window_attributes_reply(conn, wa.u.cookie, 0)) == NULL)
88 if (attr->map_state != XCB_MAP_STATE_VIEWABLE)
92 wa.u.override_redirect = attr->override_redirect;
95 /* Don’t manage clients with the override_redirect flag */
96 if (wa.u.override_redirect)
99 /* Check if the window is already managed */
100 if (table_get(&by_child, window))
103 /* Get the initial geometry (position, size, …) */
104 geomc = xcb_get_geometry(conn, d);
107 wa.u.cookie = xcb_get_window_attributes(conn, window);
108 if ((attr = xcb_get_window_attributes_reply(conn, wa.u.cookie, 0)) == NULL)
111 if ((geom = xcb_get_geometry_reply(conn, geomc, 0)) == NULL)
114 /* Reparent the window and add it to our list of managed windows */
115 reparent_window(conn, window, attr->visual, geom->root, geom->depth,
116 geom->x, geom->y, geom->width, geom->height);
118 /* Generate callback events for every property we watch */
119 xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_CLASS);
120 xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NAME);
121 xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NORMAL_HINTS);
122 xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, atoms[_NET_WM_NAME]);
131 * reparent_window() gets called when a new window was opened and becomes a child of the root
132 * window, or it gets called by us when we manage the already existing windows at startup.
134 * Essentially, this is the point where we take over control.
137 void reparent_window(xcb_connection_t *conn, xcb_window_t child,
138 xcb_visualid_t visual, xcb_window_t root, uint8_t depth,
139 int16_t x, int16_t y, uint16_t width, uint16_t height) {
141 xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie,
142 utf8_title_cookie, title_cookie, class_cookie;
146 /* We are interested in property changes */
147 mask = XCB_CW_EVENT_MASK;
148 values[0] = CHILD_EVENT_MASK;
149 xcb_change_window_attributes(conn, child, mask, values);
151 /* Map the window first to avoid flickering */
152 xcb_map_window(conn, child);
154 /* Place requests for properties ASAP */
155 wm_type_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_WINDOW_TYPE], UINT32_MAX);
156 strut_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STRUT_PARTIAL], UINT32_MAX);
157 state_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STATE], UINT32_MAX);
158 utf8_title_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_NAME], 128);
159 title_cookie = xcb_get_any_property_unchecked(conn, false, child, WM_NAME, 128);
160 class_cookie = xcb_get_any_property_unchecked(conn, false, child, WM_CLASS, 128);
162 Client *new = table_get(&by_child, child);
164 /* Events for already managed windows should already be filtered in manage_window() */
167 LOG("reparenting new client\n");
168 new = calloc(sizeof(Client), 1);
169 new->force_reconfigure = true;
171 /* Update the data structures */
172 Client *old_focused = CUR_CELL->currently_focused;
174 new->container = CUR_CELL;
175 new->workspace = new->container->workspace;
177 new->frame = xcb_generate_id(conn);
179 new->rect.width = width;
180 new->rect.height = height;
184 /* Don’t generate events for our new window, it should *not* be managed */
185 mask |= XCB_CW_OVERRIDE_REDIRECT;
188 /* We want to know when… */
189 mask |= XCB_CW_EVENT_MASK;
190 values[1] = FRAME_EVENT_MASK;
192 LOG("Reparenting 0x%08x under 0x%08x.\n", child, new->frame);
194 i3Font *font = load_font(conn, config.font);
195 width = min(width, c_ws->rect.x + c_ws->rect.width);
196 height = min(height, c_ws->rect.y + c_ws->rect.height);
198 Rect framerect = {x, y,
199 width + 2 + 2, /* 2 px border at each side */
200 height + 2 + 2 + font->height}; /* 2 px border plus font’s height */
202 /* Yo dawg, I heard you like windows, so I create a window around your window… */
203 new->frame = create_window(conn, framerect, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_CURSOR_LEFT_PTR, mask, values);
205 /* Set WM_STATE_NORMAL because GTK applications don’t want to drag & drop if we don’t.
206 * Also, xprop(1) needs that to work. */
207 long data[] = { XCB_WM_STATE_NORMAL, XCB_NONE };
208 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, new->child, atoms[WM_STATE], atoms[WM_STATE], 32, 2, data);
210 /* Put the client inside the save set. Upon termination (whether killed or normal exit
211 does not matter) of the window manager, these clients will be correctly reparented
212 to their most closest living ancestor (= cleanup) */
213 xcb_change_save_set(conn, XCB_SET_MODE_INSERT, child);
215 /* Generate a graphics context for the titlebar */
216 new->titlegc = xcb_generate_id(conn);
217 xcb_create_gc(conn, new->titlegc, new->frame, 0, 0);
219 /* Moves the original window into the new frame we've created for it */
220 new->awaiting_useless_unmap = true;
221 xcb_void_cookie_t cookie = xcb_reparent_window_checked(conn, child, new->frame, 0, font->height);
222 if (xcb_request_check(conn, cookie) != NULL) {
223 LOG("Could not reparent the window, aborting\n");
224 xcb_destroy_window(conn, new->frame);
229 /* Put our data structure (Client) into the table */
230 table_put(&by_parent, new->frame, new);
231 table_put(&by_child, child, new);
233 /* We need to grab the mouse buttons for click to focus */
234 xcb_grab_button(conn, false, child, XCB_EVENT_MASK_BUTTON_PRESS,
235 XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
236 1 /* left mouse button */,
237 XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */);
239 /* Get _NET_WM_WINDOW_TYPE (to see if it’s a dock) */
241 xcb_get_property_reply_t *preply = xcb_get_property_reply(conn, wm_type_cookie, NULL);
242 if (preply != NULL && preply->value_len > 0 && (atom = xcb_get_property_value(preply))) {
243 for (int i = 0; i < xcb_get_property_value_length(preply); i++) {
244 if (atom[i] != atoms[_NET_WM_WINDOW_TYPE_DOCK])
246 LOG("Window is a dock.\n");
248 new->titlebar_position = TITLEBAR_OFF;
249 new->force_reconfigure = true;
250 new->container = NULL;
251 SLIST_INSERT_HEAD(&(c_ws->screen->dock_clients), new, dock_clients);
256 /* Get _NET_WM_STRUT_PARTIAL to determine the client’s requested height */
258 preply = xcb_get_property_reply(conn, strut_cookie, NULL);
259 if (preply != NULL && preply->value_len > 0 && (strut = xcb_get_property_value(preply))) {
260 /* We only use a subset of the provided values, namely the reserved space at the top/bottom
261 of the screen. This is because the only possibility for bars is at to be at the top/bottom
262 with maximum horizontal size.
263 TODO: bars at the top */
264 new->desired_height = strut[3];
265 if (new->desired_height == 0) {
266 LOG("Client wanted to be 0 pixels high, using the window's height (%d)\n", height);
267 new->desired_height = height;
269 LOG("the client wants to be %d pixels high\n", new->desired_height);
271 LOG("The client didn't specify space to reserve at the screen edge, using its height (%d)\n", height);
272 new->desired_height = height;
275 /* If it’s not a dock, we can check on which workspace we should put it. */
277 /* Firstly, we need to get the window’s class / title. We asked for the properties at the
278 * top of this function, get them now and pass them to our callback function for window class / title
279 * changes. It is important that the client was already inserted into the by_child table,
280 * because the callbacks won’t work otherwise. */
281 preply = xcb_get_property_reply(conn, utf8_title_cookie, NULL);
282 handle_windowname_change(NULL, conn, 0, new->child, atoms[_NET_WM_NAME], preply);
284 preply = xcb_get_property_reply(conn, title_cookie, NULL);
285 handle_windowname_change_legacy(NULL, conn, 0, new->child, WM_NAME, preply);
287 preply = xcb_get_property_reply(conn, class_cookie, NULL);
288 handle_windowclass_change(NULL, conn, 0, new->child, WM_CLASS, preply);
290 LOG("DEBUG: should have all infos now\n");
291 struct Assignment *assign;
292 TAILQ_FOREACH(assign, &assignments, assignments) {
293 if (get_matching_client(conn, assign->windowclass_title, new) == NULL)
296 LOG("Assignment \"%s\" matches, so putting it on workspace %d\n",
297 assign->windowclass_title, assign->workspace);
299 if (c_ws->screen->current_workspace == (assign->workspace-1)) {
300 LOG("We are already there, no need to do anything\n");
304 LOG("Changin container/workspace and unmapping the client\n");
305 Workspace *t_ws = &(workspaces[assign->workspace-1]);
306 if (t_ws->screen == NULL) {
307 LOG("initializing new workspace, setting num to %d\n", assign->workspace);
308 t_ws->screen = c_ws->screen;
309 /* Copy the dimensions from the virtual screen */
310 memcpy(&(t_ws->rect), &(t_ws->screen->rect), sizeof(Rect));
313 new->container = t_ws->table[t_ws->current_col][t_ws->current_row];
314 new->workspace = t_ws;
315 xcb_unmap_window(conn, new->frame);
320 if (CUR_CELL->workspace->fullscreen_client != NULL) {
321 if (new->container == CUR_CELL) {
322 /* If we are in fullscreen, we should lower the window to not be annoying */
323 uint32_t values[] = { XCB_STACK_MODE_BELOW };
324 xcb_configure_window(conn, new->frame, XCB_CONFIG_WINDOW_STACK_MODE, values);
326 } else if (!new->dock) {
327 /* Focus the new window if we’re not in fullscreen mode and if it is not a dock window */
328 if (new->container->workspace->fullscreen_client == NULL) {
329 new->container->currently_focused = new;
330 if (new->container == CUR_CELL)
331 xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, new->child, XCB_CURRENT_TIME);
335 /* Insert into the currently active container, if it’s not a dock window */
337 /* Insert after the old active client, if existing. If it does not exist, the
338 container is empty and it does not matter, where we insert it */
339 if (old_focused != NULL && !old_focused->dock)
340 CIRCLEQ_INSERT_AFTER(&(new->container->clients), old_focused, new, clients);
341 else CIRCLEQ_INSERT_TAIL(&(new->container->clients), new, clients);
343 SLIST_INSERT_HEAD(&(new->container->workspace->focus_stack), new, focus_clients);
346 /* Check if the window already got the fullscreen hint set */
348 if ((preply = xcb_get_property_reply(conn, state_cookie, NULL)) != NULL &&
349 (state = xcb_get_property_value(preply)) != NULL)
350 /* Check all set _NET_WM_STATEs */
351 for (int i = 0; i < xcb_get_property_value_length(preply); i++) {
352 if (state[i] != atoms[_NET_WM_STATE_FULLSCREEN])
354 /* If the window got the fullscreen state, we just toggle fullscreen
355 and don’t event bother to redraw the layout – that would not change
357 toggle_fullscreen(conn, new);
365 * Go through all existing windows (if the window manager is restarted) and manage them
368 void manage_existing_windows(xcb_connection_t *conn, xcb_property_handlers_t *prophs, xcb_window_t root) {
369 xcb_query_tree_reply_t *reply;
371 xcb_window_t *children;
372 xcb_get_window_attributes_cookie_t *cookies;
374 /* Get the tree of windows whose parent is the root window (= all) */
375 if ((reply = xcb_query_tree_reply(conn, xcb_query_tree(conn, root), 0)) == NULL)
378 len = xcb_query_tree_children_length(reply);
379 cookies = smalloc(len * sizeof(*cookies));
381 /* Request the window attributes for every window */
382 children = xcb_query_tree_children(reply);
383 for(i = 0; i < len; ++i)
384 cookies[i] = xcb_get_window_attributes(conn, children[i]);
386 /* Call manage_window with the attributes for every window */
387 for(i = 0; i < len; ++i) {
388 window_attributes_t wa = { TAG_COOKIE, { cookies[i] } };
389 manage_window(prophs, conn, children[i], wa);
396 int main(int argc, char *argv[], char *env[]) {
398 char *override_configpath = NULL;
399 xcb_connection_t *conn;
400 xcb_property_handlers_t prophs;
402 xcb_intern_atom_cookie_t atom_cookies[NUM_ATOMS];
404 setlocale(LC_ALL, "");
406 /* Disable output buffering to make redirects in .xsession actually useful for debugging */
407 if (!isatty(fileno(stdout)))
408 setbuf(stdout, NULL);
412 while ((opt = getopt(argc, argv, "c:v")) != -1) {
415 override_configpath = sstrdup(optarg);
418 printf("i3 version " I3_VERSION " © 2009 Michael Stapelberg and contributors\n");
421 fprintf(stderr, "Usage: %s [-c configfile]\n", argv[0]);
426 LOG("i3 version " I3_VERSION " starting\n");
428 /* Initialize the table data structures for each workspace */
431 memset(&evenths, 0, sizeof(xcb_event_handlers_t));
432 memset(&prophs, 0, sizeof(xcb_property_handlers_t));
434 load_configuration(override_configpath);
436 conn = xcb_connect(NULL, &screens);
438 /* Place requests for the atoms we need as soon as possible */
439 #define REQUEST_ATOM(name) atom_cookies[name] = xcb_intern_atom(conn, 0, strlen(#name), #name);
441 REQUEST_ATOM(_NET_SUPPORTED);
442 REQUEST_ATOM(_NET_WM_STATE_FULLSCREEN);
443 REQUEST_ATOM(_NET_SUPPORTING_WM_CHECK);
444 REQUEST_ATOM(_NET_WM_NAME);
445 REQUEST_ATOM(_NET_WM_STATE);
446 REQUEST_ATOM(_NET_WM_WINDOW_TYPE);
447 REQUEST_ATOM(_NET_WM_DESKTOP);
448 REQUEST_ATOM(_NET_WM_WINDOW_TYPE_DOCK);
449 REQUEST_ATOM(_NET_WM_STRUT_PARTIAL);
450 REQUEST_ATOM(WM_PROTOCOLS);
451 REQUEST_ATOM(WM_DELETE_WINDOW);
452 REQUEST_ATOM(UTF8_STRING);
453 REQUEST_ATOM(WM_STATE);
455 /* TODO: this has to be more beautiful somewhen */
456 int major, minor, error;
458 major = XkbMajorVersion;
459 minor = XkbMinorVersion;
463 if ((xkbdpy = XkbOpenDisplay(getenv("DISPLAY"), &evBase, &errBase, &major, &minor, &error)) == NULL) {
464 fprintf(stderr, "XkbOpenDisplay() failed\n");
469 if (!XkbQueryExtension(xkbdpy,&i1,&evBase,&errBase,&major,&minor)) {
470 fprintf(stderr, "XKB not supported by X-server\n");
473 /* end of ugliness */
475 xcb_event_handlers_init(conn, &evenths);
477 /* DEBUG: Trap all events and print them */
478 for (i = 2; i < 128; ++i)
479 xcb_event_set_handler(&evenths, i, handle_event, 0);
481 for (i = 0; i < 256; ++i)
482 xcb_event_set_error_handler(&evenths, i, (xcb_generic_error_handler_t)handle_event, 0);
484 /* Expose = an Application should redraw itself, in this case it’s our titlebars. */
485 xcb_event_set_expose_handler(&evenths, handle_expose_event, NULL);
487 /* Key presses/releases are pretty obvious, I think */
488 xcb_event_set_key_press_handler(&evenths, handle_key_press, NULL);
489 xcb_event_set_key_release_handler(&evenths, handle_key_release, NULL);
491 /* Enter window = user moved his mouse over the window */
492 xcb_event_set_enter_notify_handler(&evenths, handle_enter_notify, NULL);
494 /* Button press = user pushed a mouse button over one of our windows */
495 xcb_event_set_button_press_handler(&evenths, handle_button_press, NULL);
497 /* Map notify = there is a new window */
498 xcb_event_set_map_request_handler(&evenths, handle_map_request, &prophs);
500 /* Unmap notify = window disappeared. When sent from a client, we don’t manage
501 it any longer. Usually, the client destroys the window shortly afterwards. */
502 xcb_event_set_unmap_notify_handler(&evenths, handle_unmap_notify_event, NULL);
504 /* Configure notify = window’s configuration (geometry, stacking, …). We only need
505 it to set up ignore the following enter_notify events */
506 xcb_event_set_configure_notify_handler(&evenths, handle_configure_event, NULL);
508 /* Configure request = window tried to change size on its own */
509 xcb_event_set_configure_request_handler(&evenths, handle_configure_request, NULL);
511 /* Client message are sent to the root window. The only interesting client message
512 for us is _NET_WM_STATE, we honour _NET_WM_STATE_FULLSCREEN */
513 xcb_event_set_client_message_handler(&evenths, handle_client_message, NULL);
515 /* Initialize the property handlers */
516 xcb_property_handlers_init(&prophs, &evenths);
518 /* Watch size hints (to obey correct aspect ratio) */
519 xcb_property_set_handler(&prophs, WM_NORMAL_HINTS, UINT_MAX, handle_normal_hints, NULL);
521 /* Get the root window and set the event mask */
522 root = xcb_aux_get_screen(conn, screens)->root;
524 uint32_t mask = XCB_CW_EVENT_MASK;
525 uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
526 XCB_EVENT_MASK_STRUCTURE_NOTIFY | /* when the user adds a screen (e.g. video
527 projector), the root window gets a
529 XCB_EVENT_MASK_PROPERTY_CHANGE |
530 XCB_EVENT_MASK_ENTER_WINDOW };
531 xcb_change_window_attributes(conn, root, mask, values);
533 /* Setup NetWM atoms */
534 #define GET_ATOM(name) { \
535 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, atom_cookies[name], NULL); \
537 LOG("Could not get atom " #name "\n"); \
540 atoms[name] = reply->atom; \
544 GET_ATOM(_NET_SUPPORTED);
545 GET_ATOM(_NET_WM_STATE_FULLSCREEN);
546 GET_ATOM(_NET_SUPPORTING_WM_CHECK);
547 GET_ATOM(_NET_WM_NAME);
548 GET_ATOM(_NET_WM_STATE);
549 GET_ATOM(_NET_WM_WINDOW_TYPE);
550 GET_ATOM(_NET_WM_DESKTOP);
551 GET_ATOM(_NET_WM_WINDOW_TYPE_DOCK);
552 GET_ATOM(_NET_WM_STRUT_PARTIAL);
553 GET_ATOM(WM_PROTOCOLS);
554 GET_ATOM(WM_DELETE_WINDOW);
555 GET_ATOM(UTF8_STRING);
558 xcb_property_set_handler(&prophs, atoms[_NET_WM_WINDOW_TYPE], UINT_MAX, handle_window_type, NULL);
559 /* TODO: In order to comply with EWMH, we have to watch _NET_WM_STRUT_PARTIAL */
561 /* Watch _NET_WM_NAME (= title of the window in UTF-8) property */
562 xcb_property_set_handler(&prophs, atoms[_NET_WM_NAME], 128, handle_windowname_change, NULL);
564 /* Watch WM_NAME (= title of the window in compound text) property for legacy applications */
565 xcb_watch_wm_name(&prophs, 128, handle_windowname_change_legacy, NULL);
567 /* Watch WM_CLASS (= class of the window) */
568 xcb_property_set_handler(&prophs, WM_CLASS, 128, handle_windowclass_change, NULL);
570 /* Set up the atoms we support */
571 check_error(conn, xcb_change_property_checked(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTED],
572 ATOM, 32, 7, atoms), "Could not set _NET_SUPPORTED");
573 /* Set up the window manager’s name */
574 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTING_WM_CHECK], WINDOW, 32, 1, &root);
575 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_WM_NAME], atoms[UTF8_STRING], 8, strlen("i3"), "i3");
577 xcb_get_numlock_mask(conn);
579 /* Grab the bound keys */
581 TAILQ_FOREACH(bind, &bindings, bindings) {
582 LOG("Grabbing %d\n", bind->keycode);
583 if (bind->mods & BIND_MODE_SWITCH)
584 xcb_grab_key(conn, 0, root, 0, bind->keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_SYNC);
586 /* Grab the key in all combinations */
587 #define GRAB_KEY(modifier) xcb_grab_key(conn, 0, root, modifier, bind->keycode, \
588 XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC)
589 GRAB_KEY(bind->mods);
590 GRAB_KEY(bind->mods | xcb_numlock_mask);
591 GRAB_KEY(bind->mods | xcb_numlock_mask | XCB_MOD_MASK_LOCK);
595 /* Autostarting exec-lines */
596 struct Autostart *exec;
597 TAILQ_FOREACH(exec, &autostarts, autostarts) {
598 LOG("auto-starting %s\n", exec->command);
599 start_application(exec->command);
602 /* check for Xinerama */
603 LOG("Checking for Xinerama...\n");
604 initialize_xinerama(conn);
608 manage_existing_windows(conn, &prophs, root);
610 /* Get pointer position to see on which screen we’re starting */
611 xcb_query_pointer_reply_t *reply;
612 if ((reply = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, root), NULL)) == NULL) {
613 LOG("Could not get pointer position\n");
617 i3Screen *screen = get_screen_containing(reply->root_x, reply->root_y);
618 if (screen == NULL) {
619 LOG("ERROR: No screen at %d x %d\n", reply->root_x, reply->root_y);
622 if (screen->current_workspace != 0) {
623 LOG("Ok, I need to go to the other workspace\n");
624 c_ws = &workspaces[screen->current_workspace];
627 /* Enter xcb’s event handler */
628 xcb_event_wait_for_event_loop(&evenths);