]> git.sur5r.net Git - i3/i3/blob - src/mainx.c
e89eaab02e70ff017db28b939df1888ad5649bdc
[i3/i3] / src / mainx.c
1 /*
2  * vim:ts=8:expandtab
3  *
4  * i3 - an improved dynamic tiling window manager
5  *
6  * © 2009 Michael Stapelberg and contributors
7  *
8  * See file LICENSE for license information.
9  *
10  */
11 #include <stdio.h>
12 #include <assert.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #include <unistd.h>
17 #include <stdbool.h>
18 #include <assert.h>
19 #include <limits.h>
20 #include <locale.h>
21
22 #include <X11/XKBlib.h>
23 #include <X11/extensions/XKB.h>
24
25 #include <xcb/xcb.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>
33
34 #include "config.h"
35 #include "data.h"
36 #include "debug.h"
37 #include "handlers.h"
38 #include "i3.h"
39 #include "layout.h"
40 #include "queue.h"
41 #include "table.h"
42 #include "util.h"
43 #include "xcb.h"
44 #include "xinerama.h"
45
46 /* This is the path to i3, copied from argv[0] when starting up */
47 char **start_argv;
48
49 /* This is our connection to X11 for use with XKB */
50 Display *xkbdpy;
51
52 /* The list of key bindings */
53 struct bindings_head bindings = TAILQ_HEAD_INITIALIZER(bindings);
54
55 /* This is a list of Stack_Windows, global, for easier/faster access on expose events */
56 struct stack_wins_head stack_wins = SLIST_HEAD_INITIALIZER(stack_wins);
57
58 /* The event handlers need to be global because they are accessed by our custom event handler
59    in handle_button_press(), needed for graphical resizing */
60 xcb_event_handlers_t evenths;
61 xcb_atom_t atoms[NUM_ATOMS];
62
63 int num_screens = 0;
64
65 /*
66  * Do some sanity checks and then reparent the window.
67  *
68  */
69 void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *conn, xcb_window_t window, window_attributes_t wa) {
70         LOG("managing window.\n");
71         xcb_drawable_t d = { window };
72         xcb_get_geometry_cookie_t geomc;
73         xcb_get_geometry_reply_t *geom;
74         xcb_get_window_attributes_reply_t *attr = 0;
75
76         if (wa.tag == TAG_COOKIE) {
77                 /* Check if the window is mapped (it could be not mapped when intializing and
78                    calling manage_window() for every window) */
79                 if ((attr = xcb_get_window_attributes_reply(conn, wa.u.cookie, 0)) == NULL)
80                         return;
81
82                 if (attr->map_state != XCB_MAP_STATE_VIEWABLE)
83                         goto out;
84
85                 wa.tag = TAG_VALUE;
86                 wa.u.override_redirect = attr->override_redirect;
87         }
88
89         /* Don’t manage clients with the override_redirect flag */
90         if (wa.u.override_redirect)
91                 goto out;
92
93         /* Check if the window is already managed */
94         if (table_get(&by_child, window))
95                 goto out;
96
97         /* Get the initial geometry (position, size, …) */
98         geomc = xcb_get_geometry(conn, d);
99         if (!attr) {
100                 wa.tag = TAG_COOKIE;
101                 wa.u.cookie = xcb_get_window_attributes(conn, window);
102                 attr = xcb_get_window_attributes_reply(conn, wa.u.cookie, 0);
103         }
104         geom = xcb_get_geometry_reply(conn, geomc, 0);
105         if (attr && geom) {
106                 reparent_window(conn, window, attr->visual, geom->root, geom->depth,
107                                 geom->x, geom->y, geom->width, geom->height);
108                 xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_CLASS);
109                 xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NAME);
110                 xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NORMAL_HINTS);
111                 xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, atoms[_NET_WM_NAME]);
112         }
113
114         free(geom);
115 out:
116         free(attr);
117         return;
118 }
119
120 /*
121  * reparent_window() gets called when a new window was opened and becomes a child of the root
122  * window, or it gets called by us when we manage the already existing windows at startup.
123  *
124  * Essentially, this is the point where we take over control.
125  *
126  */
127 void reparent_window(xcb_connection_t *conn, xcb_window_t child,
128                      xcb_visualid_t visual, xcb_window_t root, uint8_t depth,
129                      int16_t x, int16_t y, uint16_t width, uint16_t height) {
130
131         xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie;
132         uint32_t mask = 0;
133         uint32_t values[3];
134
135         /* We are interested in property changes */
136         mask = XCB_CW_EVENT_MASK;
137         values[0] = CHILD_EVENT_MASK;
138         xcb_change_window_attributes(conn, child, mask, values);
139
140         /* Map the window first to avoid flickering */
141         xcb_map_window(conn, child);
142
143         /* Place requests for properties ASAP */
144         wm_type_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_WINDOW_TYPE], UINT32_MAX);
145         strut_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STRUT_PARTIAL], UINT32_MAX);
146         state_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STATE], UINT32_MAX);
147
148         Client *new = table_get(&by_child, child);
149
150         /* Events for already managed windows should already be filtered in manage_window() */
151         assert(new == NULL);
152
153         LOG("reparenting new client\n");
154         new = calloc(sizeof(Client), 1);
155         new->force_reconfigure = true;
156
157         /* Update the data structures */
158         Client *old_focused = CUR_CELL->currently_focused;
159
160         new->container = CUR_CELL;
161         new->workspace = new->container->workspace;
162
163         new->frame = xcb_generate_id(conn);
164         new->child = child;
165         new->rect.width = width;
166         new->rect.height = height;
167
168         mask = 0;
169
170         /* Don’t generate events for our new window, it should *not* be managed */
171         mask |= XCB_CW_OVERRIDE_REDIRECT;
172         values[0] = 1;
173
174         /* We want to know when… */
175         mask |= XCB_CW_EVENT_MASK;
176         values[1] = FRAME_EVENT_MASK;
177
178         LOG("Reparenting 0x%08x under 0x%08x.\n", child, new->frame);
179
180         i3Font *font = load_font(conn, config.font);
181         width = min(width, c_ws->rect.x + c_ws->rect.width);
182         height = min(height, c_ws->rect.y + c_ws->rect.height);
183
184         Rect framerect = {x, y,
185                           width + 2 + 2,                  /* 2 px border at each side */
186                           height + 2 + 2 + font->height}; /* 2 px border plus font’s height */
187
188         /* Yo dawg, I heard you like windows, so I create a window around your window… */
189         new->frame = create_window(conn, framerect, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_CURSOR_LEFT_PTR, mask, values);
190
191         long data[] = { XCB_WM_STATE_NORMAL, XCB_NONE };
192         xcb_change_property(conn, XCB_PROP_MODE_REPLACE, new->child, atoms[WM_STATE], atoms[WM_STATE], 32, 2, data);
193
194         /* Put the client inside the save set. Upon termination (whether killed or normal exit
195            does not matter) of the window manager, these clients will be correctly reparented
196            to their most closest living ancestor (= cleanup) */
197         xcb_change_save_set(conn, XCB_SET_MODE_INSERT, child);
198
199         /* Generate a graphics context for the titlebar */
200         new->titlegc = xcb_generate_id(conn);
201         xcb_create_gc(conn, new->titlegc, new->frame, 0, 0);
202
203         /* Moves the original window into the new frame we've created for it */
204         new->awaiting_useless_unmap = true;
205         xcb_void_cookie_t cookie = xcb_reparent_window_checked(conn, child, new->frame, 0, font->height);
206         if (xcb_request_check(conn, cookie) != NULL) {
207                 LOG("Could not reparent the window, aborting\n");
208                 xcb_destroy_window(conn, new->frame);
209                 free(new);
210                 return;
211         }
212
213         /* Put our data structure (Client) into the table */
214         table_put(&by_parent, new->frame, new);
215         table_put(&by_child, child, new);
216
217         /* We need to grab the mouse buttons for click to focus */
218         xcb_grab_button(conn, false, child, XCB_EVENT_MASK_BUTTON_PRESS,
219                         XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
220                         1 /* left mouse button */,
221                         XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */);
222
223         /* Get _NET_WM_WINDOW_TYPE (to see if it’s a dock) */
224         xcb_atom_t *atom;
225         xcb_get_property_reply_t *preply = xcb_get_property_reply(conn, wm_type_cookie, NULL);
226         if (preply != NULL && preply->value_len > 0 && (atom = xcb_get_property_value(preply))) {
227                 for (int i = 0; i < xcb_get_property_value_length(preply); i++)
228                         if (atom[i] == atoms[_NET_WM_WINDOW_TYPE_DOCK]) {
229                                 LOG("Window is a dock.\n");
230                                 new->dock = true;
231                                 new->titlebar_position = TITLEBAR_OFF;
232                                 new->force_reconfigure = true;
233                                 new->container = NULL;
234                                 SLIST_INSERT_HEAD(&(c_ws->screen->dock_clients), new, dock_clients);
235                         }
236         }
237
238         if (new->dock) {
239                 /* Get _NET_WM_STRUT_PARTIAL to determine the client’s requested height */
240                 uint32_t *strut;
241                 preply = xcb_get_property_reply(conn, strut_cookie, NULL);
242                 if (preply != NULL && preply->value_len > 0 && (strut = xcb_get_property_value(preply))) {
243                         /* We only use a subset of the provided values, namely the reserved space at the top/bottom
244                            of the screen. This is because the only possibility for bars is at to be at the top/bottom
245                            with maximum horizontal size.
246                            TODO: bars at the top */
247                         new->desired_height = strut[3];
248                         if (new->desired_height == 0) {
249                                 LOG("Client wanted to be 0 pixels high, using the window's height (%d)\n", height);
250                                 new->desired_height = height;
251                         }
252                         LOG("the client wants to be %d pixels high\n", new->desired_height);
253                 } else {
254                         LOG("The client didn't specify space to reserve at the screen edge, using its height (%d)\n", height);
255                         new->desired_height = height;
256                 }
257         }
258
259         /* Focus the new window if we’re not in fullscreen mode and if it is not a dock window */
260         if (CUR_CELL->workspace->fullscreen_client == NULL) {
261                 if (!new->dock) {
262                         CUR_CELL->currently_focused = new;
263                         xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, new->child, XCB_CURRENT_TIME);
264                 }
265         } else {
266                 /* If we are in fullscreen, we should lower the window to not be annoying */
267                 uint32_t values[] = { XCB_STACK_MODE_BELOW };
268                 xcb_configure_window(conn, new->frame, XCB_CONFIG_WINDOW_STACK_MODE, values);
269         }
270
271         /* Insert into the currently active container, if it’s not a dock window */
272         if (!new->dock) {
273                 /* Insert after the old active client, if existing. If it does not exist, the
274                    container is empty and it does not matter, where we insert it */
275                 if (old_focused != NULL && !old_focused->dock)
276                         CIRCLEQ_INSERT_AFTER(&(CUR_CELL->clients), old_focused, new, clients);
277                 else CIRCLEQ_INSERT_TAIL(&(CUR_CELL->clients), new, clients);
278
279                 SLIST_INSERT_HEAD(&(new->container->workspace->focus_stack), new, focus_clients);
280         }
281
282         /* Check if the window already got the fullscreen hint set */
283         xcb_atom_t *state;
284         if ((preply = xcb_get_property_reply(conn, state_cookie, NULL)) != NULL &&
285             (state = xcb_get_property_value(preply)) != NULL)
286                 /* Check all set _NET_WM_STATEs */
287                 for (int i = 0; i < xcb_get_property_value_length(preply); i++)
288                         if (state[i] == atoms[_NET_WM_STATE_FULLSCREEN]) {
289                                 /* If the window got the fullscreen state, we just toggle fullscreen
290                                    and don’t event bother to redraw the layout – that would not change
291                                    anything anyways */
292                                 toggle_fullscreen(conn, new);
293                                 return;
294                         }
295
296         render_layout(conn);
297 }
298
299 /*
300  * Go through all existing windows (if the window manager is restarted) and manage them
301  *
302  */
303 void manage_existing_windows(xcb_connection_t *conn, xcb_property_handlers_t *prophs, xcb_window_t root) {
304         xcb_query_tree_reply_t *reply;
305         int i, len;
306         xcb_window_t *children;
307         xcb_get_window_attributes_cookie_t *cookies;
308
309         /* Get the tree of windows whose parent is the root window (= all) */
310         if ((reply = xcb_query_tree_reply(conn, xcb_query_tree(conn, root), 0)) == NULL)
311                 return;
312
313         len = xcb_query_tree_children_length(reply);
314         cookies = smalloc(len * sizeof(*cookies));
315
316         /* Request the window attributes for every window */
317         children = xcb_query_tree_children(reply);
318         for(i = 0; i < len; ++i)
319                 cookies[i] = xcb_get_window_attributes(conn, children[i]);
320
321         /* Call manage_window with the attributes for every window */
322         for(i = 0; i < len; ++i) {
323                 window_attributes_t wa = { TAG_COOKIE, { cookies[i] } };
324                 manage_window(prophs, conn, children[i], wa);
325         }
326
327         free(reply);
328         free(cookies);
329 }
330
331 int main(int argc, char *argv[], char *env[]) {
332         int i, screens, opt;
333         char *override_configpath = NULL;
334         xcb_connection_t *conn;
335         xcb_property_handlers_t prophs;
336         xcb_window_t root;
337         xcb_intern_atom_cookie_t atom_cookies[NUM_ATOMS];
338
339         setlocale(LC_ALL, "");
340
341         /* Disable output buffering to make redirects in .xsession actually useful for debugging */
342         if (!isatty(fileno(stdout)))
343                 setbuf(stdout, NULL);
344
345         start_argv = argv;
346
347         while ((opt = getopt(argc, argv, "c:v")) != -1) {
348                 switch (opt) {
349                         case 'c':
350                                 override_configpath = sstrdup(optarg);
351                                 break;
352                         case 'v':
353                                 printf("i3 version " I3_VERSION " © 2009 Michael Stapelberg and contributors\n");
354                                 exit(EXIT_SUCCESS);
355                         default:
356                                 fprintf(stderr, "Usage: %s [-c configfile]\n", argv[0]);
357                                 exit(EXIT_FAILURE);
358                 }
359         }
360
361         LOG("i3 version " I3_VERSION " starting\n");
362
363         /* Initialize the table data structures for each workspace */
364         init_table();
365
366         memset(&evenths, 0, sizeof(xcb_event_handlers_t));
367         memset(&prophs, 0, sizeof(xcb_property_handlers_t));
368
369         load_configuration(override_configpath);
370
371         conn = xcb_connect(NULL, &screens);
372
373         /* Place requests for the atoms we need as soon as possible */
374         #define REQUEST_ATOM(name) atom_cookies[name] = xcb_intern_atom(conn, 0, strlen(#name), #name);
375
376         REQUEST_ATOM(_NET_SUPPORTED);
377         REQUEST_ATOM(_NET_WM_STATE_FULLSCREEN);
378         REQUEST_ATOM(_NET_SUPPORTING_WM_CHECK);
379         REQUEST_ATOM(_NET_WM_NAME);
380         REQUEST_ATOM(_NET_WM_STATE);
381         REQUEST_ATOM(_NET_WM_WINDOW_TYPE);
382         REQUEST_ATOM(_NET_WM_DESKTOP);
383         REQUEST_ATOM(_NET_WM_WINDOW_TYPE_DOCK);
384         REQUEST_ATOM(_NET_WM_STRUT_PARTIAL);
385         REQUEST_ATOM(WM_PROTOCOLS);
386         REQUEST_ATOM(WM_DELETE_WINDOW);
387         REQUEST_ATOM(UTF8_STRING);
388         REQUEST_ATOM(WM_STATE);
389
390         /* TODO: this has to be more beautiful somewhen */
391         int major, minor, error;
392
393         major = XkbMajorVersion;
394         minor = XkbMinorVersion;
395
396         int evBase, errBase;
397
398         if ((xkbdpy = XkbOpenDisplay(getenv("DISPLAY"), &evBase, &errBase, &major, &minor, &error)) == NULL) {
399                 fprintf(stderr, "XkbOpenDisplay() failed\n");
400                 return 1;
401         }
402
403         int i1;
404         if (!XkbQueryExtension(xkbdpy,&i1,&evBase,&errBase,&major,&minor)) {
405                 fprintf(stderr, "XKB not supported by X-server\n");
406                 return 1;
407         }
408         /* end of ugliness */
409
410         xcb_event_handlers_init(conn, &evenths);
411
412         /* DEBUG: Trap all events and print them */
413         for (i = 2; i < 128; ++i)
414                 xcb_event_set_handler(&evenths, i, handle_event, 0);
415
416         for (i = 0; i < 256; ++i)
417                 xcb_event_set_error_handler(&evenths, i, (xcb_generic_error_handler_t)handle_event, 0);
418
419         /* Expose = an Application should redraw itself, in this case it’s our titlebars. */
420         xcb_event_set_expose_handler(&evenths, handle_expose_event, NULL);
421
422         /* Key presses/releases are pretty obvious, I think */
423         xcb_event_set_key_press_handler(&evenths, handle_key_press, NULL);
424         xcb_event_set_key_release_handler(&evenths, handle_key_release, NULL);
425
426         /* Enter window = user moved his mouse over the window */
427         xcb_event_set_enter_notify_handler(&evenths, handle_enter_notify, NULL);
428
429         /* Button press = user pushed a mouse button over one of our windows */
430         xcb_event_set_button_press_handler(&evenths, handle_button_press, NULL);
431
432         /* Map notify = there is a new window */
433         xcb_event_set_map_request_handler(&evenths, handle_map_request, &prophs);
434
435         /* Unmap notify = window disappeared. When sent from a client, we don’t manage
436            it any longer. Usually, the client destroys the window shortly afterwards. */
437         xcb_event_set_unmap_notify_handler(&evenths, handle_unmap_notify_event, NULL);
438
439         /* Configure notify = window’s configuration (geometry, stacking, …). We only need
440            it to set up ignore the following enter_notify events */
441         xcb_event_set_configure_notify_handler(&evenths, handle_configure_event, NULL);
442
443         /* Configure request = window tried to change size on its own */
444         xcb_event_set_configure_request_handler(&evenths, handle_configure_request, NULL);
445
446         /* Client message are sent to the root window. The only interesting client message
447            for us is _NET_WM_STATE, we honour _NET_WM_STATE_FULLSCREEN */
448         xcb_event_set_client_message_handler(&evenths, handle_client_message, NULL);
449
450         /* Initialize the property handlers */
451         xcb_property_handlers_init(&prophs, &evenths);
452
453         /* Watch size hints (to obey correct aspect ratio) */
454         xcb_property_set_handler(&prophs, WM_NORMAL_HINTS, UINT_MAX, handle_normal_hints, NULL);
455
456         /* Get the root window and set the event mask */
457         root = xcb_aux_get_screen(conn, screens)->root;
458
459         uint32_t mask = XCB_CW_EVENT_MASK;
460         uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
461                               XCB_EVENT_MASK_STRUCTURE_NOTIFY |         /* when the user adds a screen (e.g. video
462                                                                            projector), the root window gets a
463                                                                            ConfigureNotify */
464                               XCB_EVENT_MASK_PROPERTY_CHANGE |
465                               XCB_EVENT_MASK_ENTER_WINDOW };
466         xcb_change_window_attributes(conn, root, mask, values);
467
468         /* Setup NetWM atoms */
469         #define GET_ATOM(name) { \
470                 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, atom_cookies[name], NULL); \
471                 if (!reply) { \
472                         LOG("Could not get atom " #name "\n"); \
473                         exit(-1); \
474                 } \
475                 atoms[name] = reply->atom; \
476                 free(reply); \
477         }
478
479         GET_ATOM(_NET_SUPPORTED);
480         GET_ATOM(_NET_WM_STATE_FULLSCREEN);
481         GET_ATOM(_NET_SUPPORTING_WM_CHECK);
482         GET_ATOM(_NET_WM_NAME);
483         GET_ATOM(_NET_WM_STATE);
484         GET_ATOM(_NET_WM_WINDOW_TYPE);
485         GET_ATOM(_NET_WM_DESKTOP);
486         GET_ATOM(_NET_WM_WINDOW_TYPE_DOCK);
487         GET_ATOM(_NET_WM_STRUT_PARTIAL);
488         GET_ATOM(WM_PROTOCOLS);
489         GET_ATOM(WM_DELETE_WINDOW);
490         GET_ATOM(UTF8_STRING);
491         GET_ATOM(WM_STATE);
492
493         xcb_property_set_handler(&prophs, atoms[_NET_WM_WINDOW_TYPE], UINT_MAX, handle_window_type, NULL);
494         /* TODO: In order to comply with EWMH, we have to watch _NET_WM_STRUT_PARTIAL */
495
496         /* Watch _NET_WM_NAME (= title of the window in UTF-8) property */
497         xcb_property_set_handler(&prophs, atoms[_NET_WM_NAME], 128, handle_windowname_change, NULL);
498
499         /* Watch WM_NAME (= title of the window in compound text) property for legacy applications */
500         xcb_watch_wm_name(&prophs, 128, handle_windowname_change_legacy, NULL);
501
502         /* Watch WM_CLASS (= class of the window) */
503         xcb_property_set_handler(&prophs, WM_CLASS, 128, handle_windowclass_change, NULL);
504
505         /* Set up the atoms we support */
506         check_error(conn, xcb_change_property_checked(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTED],
507                        ATOM, 32, 7, atoms), "Could not set _NET_SUPPORTED");
508         /* Set up the window manager’s name */
509         xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTING_WM_CHECK], WINDOW, 32, 1, &root);
510         xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_WM_NAME], atoms[UTF8_STRING], 8, strlen("i3"), "i3");
511
512         xcb_get_numlock_mask(conn);
513
514         /* Grab the bound keys */
515         Binding *bind;
516         TAILQ_FOREACH(bind, &bindings, bindings) {
517                 LOG("Grabbing %d\n", bind->keycode);
518                 if (bind->mods & BIND_MODE_SWITCH)
519                         xcb_grab_key(conn, 0, root, 0, bind->keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_SYNC);
520                 else {
521                         /* Grab the key in all combinations */
522                         #define GRAB_KEY(modifier) xcb_grab_key(conn, 0, root, modifier, bind->keycode, \
523                                                                 XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC)
524                         GRAB_KEY(bind->mods);
525                         GRAB_KEY(bind->mods | xcb_numlock_mask);
526                         GRAB_KEY(bind->mods | xcb_numlock_mask | XCB_MOD_MASK_LOCK);
527                 }
528         }
529
530         /* check for Xinerama */
531         LOG("Checking for Xinerama...\n");
532         initialize_xinerama(conn);
533
534         xcb_flush(conn);
535
536         manage_existing_windows(conn, &prophs, root);
537
538         /* Get pointer position to see on which screen we’re starting */
539         xcb_query_pointer_reply_t *reply;
540         if ((reply = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, root), NULL)) == NULL) {
541                 LOG("Could not get pointer position\n");
542                 return 1;
543         }
544
545         i3Screen *screen = get_screen_containing(reply->root_x, reply->root_y);
546         if (screen == NULL) {
547                 LOG("ERROR: No screen at %d x %d\n", reply->root_x, reply->root_y);
548                 return 0;
549         }
550         if (screen->current_workspace != 0) {
551                 LOG("Ok, I need to go to the other workspace\n");
552                 c_ws = &workspaces[screen->current_workspace];
553         }
554
555         /* Enter xcb’s event handler */
556         xcb_event_wait_for_event_loop(&evenths);
557
558         /* not reached */
559         return 0;
560 }