]> git.sur5r.net Git - i3/i3/blob - src/mainx.c
dbce26a159695e51e886f826d1e3504a97a28dd8
[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 /* The list of exec-lines */
56 struct autostarts_head autostarts = TAILQ_HEAD_INITIALIZER(autostarts);
57
58 /* The list of assignments */
59 struct assignments_head assignments = TAILQ_HEAD_INITIALIZER(assignments);
60
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);
63
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];
68
69 int num_screens = 0;
70
71 /*
72  * Do some sanity checks and then reparent the window.
73  *
74  */
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;
81
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)
86                         return;
87
88                 if (attr->map_state != XCB_MAP_STATE_VIEWABLE)
89                         goto out;
90
91                 wa.tag = TAG_VALUE;
92                 wa.u.override_redirect = attr->override_redirect;
93         }
94
95         /* Don’t manage clients with the override_redirect flag */
96         if (wa.u.override_redirect)
97                 goto out;
98
99         /* Check if the window is already managed */
100         if (table_get(&by_child, window))
101                 goto out;
102
103         /* Get the initial geometry (position, size, …) */
104         geomc = xcb_get_geometry(conn, d);
105         if (!attr) {
106                 wa.tag = TAG_COOKIE;
107                 wa.u.cookie = xcb_get_window_attributes(conn, window);
108                 if ((attr = xcb_get_window_attributes_reply(conn, wa.u.cookie, 0)) == NULL)
109                         return;
110         }
111         if ((geom = xcb_get_geometry_reply(conn, geomc, 0)) == NULL)
112                 goto out;
113
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);
117
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]);
123
124         free(geom);
125 out:
126         free(attr);
127         return;
128 }
129
130 /*
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.
133  *
134  * Essentially, this is the point where we take over control.
135  *
136  */
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) {
140
141         xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie,
142                                   utf8_title_cookie, title_cookie, class_cookie;
143         uint32_t mask = 0;
144         uint32_t values[3];
145
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);
150
151         /* Map the window first to avoid flickering */
152         xcb_map_window(conn, child);
153
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);
161
162         Client *new = table_get(&by_child, child);
163
164         /* Events for already managed windows should already be filtered in manage_window() */
165         assert(new == NULL);
166
167         LOG("reparenting new client\n");
168         new = calloc(sizeof(Client), 1);
169         new->force_reconfigure = true;
170
171         /* Update the data structures */
172         Client *old_focused = CUR_CELL->currently_focused;
173
174         new->container = CUR_CELL;
175         new->workspace = new->container->workspace;
176
177         new->frame = xcb_generate_id(conn);
178         new->child = child;
179         new->rect.width = width;
180         new->rect.height = height;
181
182         mask = 0;
183
184         /* Don’t generate events for our new window, it should *not* be managed */
185         mask |= XCB_CW_OVERRIDE_REDIRECT;
186         values[0] = 1;
187
188         /* We want to know when… */
189         mask |= XCB_CW_EVENT_MASK;
190         values[1] = FRAME_EVENT_MASK;
191
192         LOG("Reparenting 0x%08x under 0x%08x.\n", child, new->frame);
193
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);
197
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 */
201
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);
204
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);
209
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);
214
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);
218
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);
225                 free(new);
226                 return;
227         }
228
229         /* Put our data structure (Client) into the table */
230         table_put(&by_parent, new->frame, new);
231         table_put(&by_child, child, new);
232
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 */);
238
239         /* Get _NET_WM_WINDOW_TYPE (to see if it’s a dock) */
240         xcb_atom_t *atom;
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])
245                                 continue;
246                         LOG("Window is a dock.\n");
247                         new->dock = true;
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);
252                 }
253         }
254
255         if (new->dock) {
256                 /* Get _NET_WM_STRUT_PARTIAL to determine the client’s requested height */
257                 uint32_t *strut;
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;
268                         }
269                         LOG("the client wants to be %d pixels high\n", new->desired_height);
270                 } else {
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;
273                 }
274         } else {
275                 /* If it’s not a dock, we can check on which workspace we should put it. */
276
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);
283
284                 preply = xcb_get_property_reply(conn, title_cookie, NULL);
285                 handle_windowname_change_legacy(NULL, conn, 0, new->child, WM_NAME, preply);
286
287                 preply = xcb_get_property_reply(conn, class_cookie, NULL);
288                 handle_windowclass_change(NULL, conn, 0, new->child, WM_CLASS, preply);
289
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)
294                                 continue;
295
296                         LOG("Assignment \"%s\" matches, so putting it on workspace %d\n",
297                             assign->windowclass_title, assign->workspace);
298
299                         if (c_ws->screen->current_workspace == (assign->workspace-1)) {
300                                 LOG("We are already there, no need to do anything\n");
301                                 break;
302                         }
303
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));
311                         }
312
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);
316                         break;
317                 }
318         }
319
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);
325                 }
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);
332                 }
333         }
334
335         /* Insert into the currently active container, if it’s not a dock window */
336         if (!new->dock) {
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);
342
343                 SLIST_INSERT_HEAD(&(new->container->workspace->focus_stack), new, focus_clients);
344         }
345
346         /* Check if the window already got the fullscreen hint set */
347         xcb_atom_t *state;
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])
353                                 continue;
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
356                            anything anyways */
357                         toggle_fullscreen(conn, new);
358                         return;
359                 }
360
361         render_layout(conn);
362 }
363
364 /*
365  * Go through all existing windows (if the window manager is restarted) and manage them
366  *
367  */
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;
370         int i, len;
371         xcb_window_t *children;
372         xcb_get_window_attributes_cookie_t *cookies;
373
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)
376                 return;
377
378         len = xcb_query_tree_children_length(reply);
379         cookies = smalloc(len * sizeof(*cookies));
380
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]);
385
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);
390         }
391
392         free(reply);
393         free(cookies);
394 }
395
396 int main(int argc, char *argv[], char *env[]) {
397         int i, screens, opt;
398         char *override_configpath = NULL;
399         xcb_connection_t *conn;
400         xcb_property_handlers_t prophs;
401         xcb_window_t root;
402         xcb_intern_atom_cookie_t atom_cookies[NUM_ATOMS];
403
404         setlocale(LC_ALL, "");
405
406         /* Disable output buffering to make redirects in .xsession actually useful for debugging */
407         if (!isatty(fileno(stdout)))
408                 setbuf(stdout, NULL);
409
410         start_argv = argv;
411
412         while ((opt = getopt(argc, argv, "c:v")) != -1) {
413                 switch (opt) {
414                         case 'c':
415                                 override_configpath = sstrdup(optarg);
416                                 break;
417                         case 'v':
418                                 printf("i3 version " I3_VERSION " © 2009 Michael Stapelberg and contributors\n");
419                                 exit(EXIT_SUCCESS);
420                         default:
421                                 fprintf(stderr, "Usage: %s [-c configfile]\n", argv[0]);
422                                 exit(EXIT_FAILURE);
423                 }
424         }
425
426         LOG("i3 version " I3_VERSION " starting\n");
427
428         /* Initialize the table data structures for each workspace */
429         init_table();
430
431         memset(&evenths, 0, sizeof(xcb_event_handlers_t));
432         memset(&prophs, 0, sizeof(xcb_property_handlers_t));
433
434         load_configuration(override_configpath);
435
436         conn = xcb_connect(NULL, &screens);
437
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);
440
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);
454
455         /* TODO: this has to be more beautiful somewhen */
456         int major, minor, error;
457
458         major = XkbMajorVersion;
459         minor = XkbMinorVersion;
460
461         int evBase, errBase;
462
463         if ((xkbdpy = XkbOpenDisplay(getenv("DISPLAY"), &evBase, &errBase, &major, &minor, &error)) == NULL) {
464                 fprintf(stderr, "XkbOpenDisplay() failed\n");
465                 return 1;
466         }
467
468         int i1;
469         if (!XkbQueryExtension(xkbdpy,&i1,&evBase,&errBase,&major,&minor)) {
470                 fprintf(stderr, "XKB not supported by X-server\n");
471                 return 1;
472         }
473         /* end of ugliness */
474
475         xcb_event_handlers_init(conn, &evenths);
476
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);
480
481         for (i = 0; i < 256; ++i)
482                 xcb_event_set_error_handler(&evenths, i, (xcb_generic_error_handler_t)handle_event, 0);
483
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);
486
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);
490
491         /* Enter window = user moved his mouse over the window */
492         xcb_event_set_enter_notify_handler(&evenths, handle_enter_notify, NULL);
493
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);
496
497         /* Map notify = there is a new window */
498         xcb_event_set_map_request_handler(&evenths, handle_map_request, &prophs);
499
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);
503
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);
507
508         /* Configure request = window tried to change size on its own */
509         xcb_event_set_configure_request_handler(&evenths, handle_configure_request, NULL);
510
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);
514
515         /* Initialize the property handlers */
516         xcb_property_handlers_init(&prophs, &evenths);
517
518         /* Watch size hints (to obey correct aspect ratio) */
519         xcb_property_set_handler(&prophs, WM_NORMAL_HINTS, UINT_MAX, handle_normal_hints, NULL);
520
521         /* Get the root window and set the event mask */
522         root = xcb_aux_get_screen(conn, screens)->root;
523
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
528                                                                            ConfigureNotify */
529                               XCB_EVENT_MASK_PROPERTY_CHANGE |
530                               XCB_EVENT_MASK_ENTER_WINDOW };
531         xcb_change_window_attributes(conn, root, mask, values);
532
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); \
536                 if (!reply) { \
537                         LOG("Could not get atom " #name "\n"); \
538                         exit(-1); \
539                 } \
540                 atoms[name] = reply->atom; \
541                 free(reply); \
542         }
543
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);
556         GET_ATOM(WM_STATE);
557
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 */
560
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);
563
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);
566
567         /* Watch WM_CLASS (= class of the window) */
568         xcb_property_set_handler(&prophs, WM_CLASS, 128, handle_windowclass_change, NULL);
569
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");
576
577         xcb_get_numlock_mask(conn);
578
579         /* Grab the bound keys */
580         Binding *bind;
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);
585                 else {
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);
592                 }
593         }
594
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);
600         }
601
602         /* check for Xinerama */
603         LOG("Checking for Xinerama...\n");
604         initialize_xinerama(conn);
605
606         xcb_flush(conn);
607
608         manage_existing_windows(conn, &prophs, root);
609
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");
614                 return 1;
615         }
616
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);
620                 return 0;
621         }
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];
625         }
626
627         /* Enter xcb’s event handler */
628         xcb_event_wait_for_event_loop(&evenths);
629
630         /* not reached */
631         return 0;
632 }