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