]> git.sur5r.net Git - i3/i3/blob - src/manage.c
940013e29efb69d0973051b2e696a2647957d7f4
[i3/i3] / src / manage.c
1 /*
2  * vim:ts=4:sw=4:expandtab
3  *
4  * i3 - an improved dynamic tiling window manager
5  * © 2009-2010 Michael Stapelberg and contributors (see also: LICENSE)
6  *
7  * manage.c: Contains all functions for initially managing new windows
8  *           (or existing ones on restart).
9  *
10  */
11
12 #include "all.h"
13
14 /*
15  * Go through all existing windows (if the window manager is restarted) and manage them
16  *
17  */
18 void manage_existing_windows(xcb_window_t root) {
19     xcb_query_tree_reply_t *reply;
20     int i, len;
21     xcb_window_t *children;
22     xcb_get_window_attributes_cookie_t *cookies;
23
24     /* Get the tree of windows whose parent is the root window (= all) */
25     if ((reply = xcb_query_tree_reply(conn, xcb_query_tree(conn, root), 0)) == NULL)
26         return;
27
28     len = xcb_query_tree_children_length(reply);
29     cookies = smalloc(len * sizeof(*cookies));
30
31     /* Request the window attributes for every window */
32     children = xcb_query_tree_children(reply);
33     for (i = 0; i < len; ++i)
34         cookies[i] = xcb_get_window_attributes(conn, children[i]);
35
36     /* Call manage_window with the attributes for every window */
37     for (i = 0; i < len; ++i)
38         manage_window(children[i], cookies[i], true);
39
40     free(reply);
41     free(cookies);
42 }
43
44 /*
45  * Restores the geometry of each window by reparenting it to the root window
46  * at the position of its frame.
47  *
48  * This is to be called *only* before exiting/restarting i3 because of evil
49  * side-effects which are to be expected when continuing to run i3.
50  *
51  */
52 void restore_geometry() {
53     LOG("Restoring geometry\n");
54
55     Con *con;
56     TAILQ_FOREACH(con, &all_cons, all_cons)
57         if (con->window) {
58             printf("placing window at %d %d\n", con->rect.x, con->rect.y);
59             xcb_reparent_window(conn, con->window->id, root,
60                                 con->rect.x, con->rect.y);
61         }
62
63     /* Make sure our changes reach the X server, we restart/exit now */
64     xcb_flush(conn);
65 }
66
67 /*
68  * Do some sanity checks and then reparent the window.
69  *
70  */
71 void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cookie,
72                    bool needs_to_be_mapped) {
73     xcb_drawable_t d = { window };
74     xcb_get_geometry_cookie_t geomc;
75     xcb_get_geometry_reply_t *geom;
76     xcb_get_window_attributes_reply_t *attr = 0;
77
78     printf("---> looking at window 0x%08x\n", window);
79
80     xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie,
81                               utf8_title_cookie, title_cookie,
82                               class_cookie, leader_cookie;
83
84     wm_type_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[_NET_WM_WINDOW_TYPE], UINT32_MAX);
85     strut_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[_NET_WM_STRUT_PARTIAL], UINT32_MAX);
86     state_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[_NET_WM_STATE], UINT32_MAX);
87     utf8_title_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[_NET_WM_NAME], 128);
88     leader_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[WM_CLIENT_LEADER], UINT32_MAX);
89     title_cookie = xcb_get_any_property_unchecked(conn, false, window, WM_NAME, 128);
90     class_cookie = xcb_get_any_property_unchecked(conn, false, window, WM_CLASS, 128);
91
92
93     geomc = xcb_get_geometry(conn, d);
94
95     /* Check if the window is mapped (it could be not mapped when intializing and
96        calling manage_window() for every window) */
97     if ((attr = xcb_get_window_attributes_reply(conn, cookie, 0)) == NULL) {
98         LOG("Could not get attributes\n");
99         return;
100     }
101
102     if (needs_to_be_mapped && attr->map_state != XCB_MAP_STATE_VIEWABLE) {
103         LOG("map_state unviewable\n");
104         goto out;
105     }
106
107     /* Don’t manage clients with the override_redirect flag */
108     LOG("override_redirect is %d\n", attr->override_redirect);
109     if (attr->override_redirect)
110         goto out;
111
112     /* Check if the window is already managed */
113     if (con_by_window_id(window) != NULL) {
114         LOG("already managed\n");
115         goto out;
116     }
117
118     /* Get the initial geometry (position, size, …) */
119     if ((geom = xcb_get_geometry_reply(conn, geomc, 0)) == NULL) {
120         LOG("could not get geometry\n");
121         goto out;
122     }
123
124     LOG("reparenting!\n");
125     uint32_t mask = 0;
126     uint32_t values[1];
127     mask = XCB_CW_EVENT_MASK;
128     values[0] = CHILD_EVENT_MASK;
129     xcb_change_window_attributes(conn, window, mask, values);
130
131     i3Window *cwindow = scalloc(sizeof(i3Window));
132     cwindow->id = window;
133
134     /* update as much information as possible so far (some replies may be NULL) */
135     window_update_class(cwindow, xcb_get_property_reply(conn, class_cookie, NULL));
136     window_update_name_legacy(cwindow, xcb_get_property_reply(conn, title_cookie, NULL));
137     window_update_name(cwindow, xcb_get_property_reply(conn, utf8_title_cookie, NULL));
138
139     Con *nc;
140     Match *match;
141
142     /* TODO: assignments */
143     /* TODO: two matches for one container */
144     /* See if any container swallows this new window */
145     nc = con_for_window(cwindow, &match);
146     if (nc == NULL) {
147         if (focused->type == CT_CON && con_accepts_window(focused)) {
148             LOG("using current container, focused = %p, focused->name = %s\n",
149                             focused, focused->name);
150             nc = focused;
151         } else nc = tree_open_con(NULL);
152     } else {
153         if (match != NULL && match->insert_where == M_ACTIVE) {
154             /* We need to go down the focus stack starting from nc */
155             while (TAILQ_FIRST(&(nc->focus_head)) != TAILQ_END(&(nc->focus_head))) {
156                 printf("walking down one step...\n");
157                 nc = TAILQ_FIRST(&(nc->focus_head));
158             }
159             /* We need to open a new con */
160             /* TODO: make a difference between match-once containers (directly assign
161              * cwindow) and match-multiple (tree_open_con first) */
162             nc = tree_open_con(nc->parent);
163         }
164     }
165     nc->window = cwindow;
166     x_reinit(nc);
167
168     xcb_void_cookie_t rcookie = xcb_reparent_window_checked(conn, window, nc->frame, 0, 0);
169     if (xcb_request_check(conn, rcookie) != NULL) {
170         LOG("Could not reparent the window, aborting\n");
171         goto out;
172         //xcb_destroy_window(conn, nc->frame);
173     }
174
175     xcb_atom_t *state;
176     xcb_get_property_reply_t *preply;
177     if ((preply = xcb_get_property_reply(conn, state_cookie, NULL)) != NULL &&
178         (state = xcb_get_property_value(preply)) != NULL) {
179         /* Check all set _NET_WM_STATEs */
180         for (int i = 0; i < xcb_get_property_value_length(preply); i++) {
181             if (state[i] != atoms[_NET_WM_STATE_FULLSCREEN])
182                     continue;
183             con_toggle_fullscreen(nc);
184             break;
185         }
186     }
187
188     xcb_change_save_set(conn, XCB_SET_MODE_INSERT, window);
189
190     tree_render();
191
192 #if 0
193     /* Reparent the window and add it to our list of managed windows */
194     reparent_window(conn, window, attr->visual, geom->root, geom->depth,
195                     geom->x, geom->y, geom->width, geom->height,
196                     geom->border_width);
197 #endif
198
199     free(geom);
200 out:
201     free(attr);
202     return;
203 }
204
205 #if 0
206 /*
207  * reparent_window() gets called when a new window was opened and becomes a child of the root
208  * window, or it gets called by us when we manage the already existing windows at startup.
209  *
210  * Essentially, this is the point where we take over control.
211  *
212  */
213 void reparent_window(xcb_connection_t *conn, xcb_window_t child,
214                      xcb_visualid_t visual, xcb_window_t root, uint8_t depth,
215                      int16_t x, int16_t y, uint16_t width, uint16_t height,
216                      uint32_t border_width) {
217
218         xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie,
219                                   utf8_title_cookie, title_cookie,
220                                   class_cookie, leader_cookie;
221         uint32_t mask = 0;
222         uint32_t values[3];
223         uint16_t original_height = height;
224         bool map_frame = true;
225
226         /* We are interested in property changes */
227         mask = XCB_CW_EVENT_MASK;
228         values[0] = CHILD_EVENT_MASK;
229         xcb_change_window_attributes(conn, child, mask, values);
230
231         /* Place requests for properties ASAP */
232         wm_type_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_WINDOW_TYPE], UINT32_MAX);
233         strut_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STRUT_PARTIAL], UINT32_MAX);
234         state_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STATE], UINT32_MAX);
235         utf8_title_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_NAME], 128);
236         leader_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[WM_CLIENT_LEADER], UINT32_MAX);
237         title_cookie = xcb_get_any_property_unchecked(conn, false, child, WM_NAME, 128);
238         class_cookie = xcb_get_any_property_unchecked(conn, false, child, WM_CLASS, 128);
239
240         Client *new = table_get(&by_child, child);
241
242         /* Events for already managed windows should already be filtered in manage_window() */
243         assert(new == NULL);
244
245         LOG("Managing window 0x%08x\n", child);
246         DLOG("x = %d, y = %d, width = %d, height = %d\n", x, y, width, height);
247         new = scalloc(sizeof(Client));
248         new->force_reconfigure = true;
249
250         /* Update the data structures */
251         Client *old_focused = CUR_CELL->currently_focused;
252
253         new->container = CUR_CELL;
254         new->workspace = new->container->workspace;
255
256         /* Minimum useful size for managed windows is 75x50 (primarily affects floating) */
257         width = max(width, 75);
258         height = max(height, 50);
259
260         new->frame = xcb_generate_id(conn);
261         new->child = child;
262         new->rect.width = width;
263         new->rect.height = height;
264         new->width_increment = 1;
265         new->height_increment = 1;
266         new->border_width = border_width;
267         /* Pre-initialize the values for floating */
268         new->floating_rect.x = -1;
269         new->floating_rect.width = width;
270         new->floating_rect.height = height;
271
272         if (config.default_border != NULL)
273                 client_init_border(conn, new, config.default_border[1]);
274
275         mask = 0;
276
277         /* Don’t generate events for our new window, it should *not* be managed */
278         mask |= XCB_CW_OVERRIDE_REDIRECT;
279         values[0] = 1;
280
281         /* We want to know when… */
282         mask |= XCB_CW_EVENT_MASK;
283         values[1] = FRAME_EVENT_MASK;
284
285         i3Font *font = load_font(conn, config.font);
286         width = min(width, c_ws->rect.x + c_ws->rect.width);
287         height = min(height, c_ws->rect.y + c_ws->rect.height);
288
289         Rect framerect = {x, y,
290                           width + 2 + 2,                  /* 2 px border at each side */
291                           height + 2 + 2 + font->height}; /* 2 px border plus font’s height */
292
293         /* Yo dawg, I heard you like windows, so I create a window around your window… */
294         new->frame = create_window(conn, framerect, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_CURSOR_LEFT_PTR, false, mask, values);
295
296         /* Put the client inside the save set. Upon termination (whether killed or normal exit
297            does not matter) of the window manager, these clients will be correctly reparented
298            to their most closest living ancestor (= cleanup) */
299         xcb_change_save_set(conn, XCB_SET_MODE_INSERT, child);
300
301         /* Generate a graphics context for the titlebar */
302         new->titlegc = xcb_generate_id(conn);
303         xcb_create_gc(conn, new->titlegc, new->frame, 0, 0);
304
305         /* Moves the original window into the new frame we've created for it */
306         new->awaiting_useless_unmap = true;
307         xcb_void_cookie_t cookie = xcb_reparent_window_checked(conn, child, new->frame, 0, font->height);
308         if (xcb_request_check(conn, cookie) != NULL) {
309                 DLOG("Could not reparent the window, aborting\n");
310                 xcb_destroy_window(conn, new->frame);
311                 free(new);
312                 return;
313         }
314
315         /* Put our data structure (Client) into the table */
316         table_put(&by_parent, new->frame, new);
317         table_put(&by_child, child, new);
318
319         /* We need to grab the mouse buttons for click to focus */
320         xcb_grab_button(conn, false, child, XCB_EVENT_MASK_BUTTON_PRESS,
321                         XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
322                         1 /* left mouse button */,
323                         XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */);
324
325         xcb_grab_button(conn, false, child, XCB_EVENT_MASK_BUTTON_PRESS,
326                         XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
327                         3 /* right mouse button */,
328                         XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */);
329
330         /* Get _NET_WM_WINDOW_TYPE (to see if it’s a dock) */
331         xcb_atom_t *atom;
332         xcb_get_property_reply_t *preply = xcb_get_property_reply(conn, wm_type_cookie, NULL);
333         if (preply != NULL && preply->value_len > 0 && (atom = xcb_get_property_value(preply))) {
334                 for (int i = 0; i < xcb_get_property_value_length(preply); i++)
335                         if (atom[i] == atoms[_NET_WM_WINDOW_TYPE_DOCK]) {
336                                 DLOG("Window is a dock.\n");
337                                 Output *t_out = get_output_containing(x, y);
338                                 if (t_out != c_ws->output) {
339                                         DLOG("Dock client requested to be on output %s by geometry (%d, %d)\n",
340                                                         t_out->name, x, y);
341                                         new->workspace = t_out->current_workspace;
342                                 }
343                                 new->dock = true;
344                                 new->borderless = true;
345                                 new->titlebar_position = TITLEBAR_OFF;
346                                 new->force_reconfigure = true;
347                                 new->container = NULL;
348                                 SLIST_INSERT_HEAD(&(t_out->dock_clients), new, dock_clients);
349                                 /* If it’s a dock we can’t make it float, so we break */
350                                 new->floating = FLOATING_AUTO_OFF;
351                                 break;
352                         } else if (atom[i] == atoms[_NET_WM_WINDOW_TYPE_DIALOG] ||
353                                    atom[i] == atoms[_NET_WM_WINDOW_TYPE_UTILITY] ||
354                                    atom[i] == atoms[_NET_WM_WINDOW_TYPE_TOOLBAR] ||
355                                    atom[i] == atoms[_NET_WM_WINDOW_TYPE_SPLASH]) {
356                                 /* Set the dialog window to automatically floating, will be used below */
357                                 new->floating = FLOATING_AUTO_ON;
358                                 DLOG("dialog/utility/toolbar/splash window, automatically floating\n");
359                         }
360         }
361
362         /* All clients which have a leader should be floating */
363         if (!new->dock && !client_is_floating(new) && new->leader != 0) {
364                 DLOG("Client has WM_CLIENT_LEADER hint set, setting floating\n");
365                 new->floating = FLOATING_AUTO_ON;
366         }
367
368         if (new->workspace->auto_float) {
369                 new->floating = FLOATING_AUTO_ON;
370                 DLOG("workspace is in autofloat mode, setting floating\n");
371         }
372
373         if (new->dock) {
374                 /* Get _NET_WM_STRUT_PARTIAL to determine the client’s requested height */
375                 uint32_t *strut;
376                 preply = xcb_get_property_reply(conn, strut_cookie, NULL);
377                 if (preply != NULL && preply->value_len > 0 && (strut = xcb_get_property_value(preply))) {
378                         /* We only use a subset of the provided values, namely the reserved space at the top/bottom
379                            of the screen. This is because the only possibility for bars is at to be at the top/bottom
380                            with maximum horizontal size.
381                            TODO: bars at the top */
382                         new->desired_height = strut[3];
383                         if (new->desired_height == 0) {
384                                 DLOG("Client wanted to be 0 pixels high, using the window's height (%d)\n", original_height);
385                                 new->desired_height = original_height;
386                         }
387                         DLOG("the client wants to be %d pixels high\n", new->desired_height);
388                 } else {
389                         DLOG("The client didn't specify space to reserve at the screen edge, using its height (%d)\n", original_height);
390                         new->desired_height = original_height;
391                 }
392         } else {
393                 /* If it’s not a dock, we can check on which workspace we should put it. */
394
395                 /* Firstly, we need to get the window’s class / title. We asked for the properties at the
396                  * top of this function, get them now and pass them to our callback function for window class / title
397                  * changes. It is important that the client was already inserted into the by_child table,
398                  * because the callbacks won’t work otherwise. */
399                 preply = xcb_get_property_reply(conn, utf8_title_cookie, NULL);
400                 handle_windowname_change(NULL, conn, 0, new->child, atoms[_NET_WM_NAME], preply);
401
402                 preply = xcb_get_property_reply(conn, title_cookie, NULL);
403                 handle_windowname_change_legacy(NULL, conn, 0, new->child, WM_NAME, preply);
404
405                 preply = xcb_get_property_reply(conn, class_cookie, NULL);
406                 handle_windowclass_change(NULL, conn, 0, new->child, WM_CLASS, preply);
407
408                 preply = xcb_get_property_reply(conn, leader_cookie, NULL);
409                 handle_clientleader_change(NULL, conn, 0, new->child, atoms[WM_CLIENT_LEADER], preply);
410
411                 /* if WM_CLIENT_LEADER is set, we put the new window on the
412                  * same window as its leader. This might be overwritten by
413                  * assignments afterwards. */
414                 if (new->leader != XCB_NONE) {
415                         DLOG("client->leader is set (to 0x%08x)\n", new->leader);
416                         Client *parent = table_get(&by_child, new->leader);
417                         if (parent != NULL && parent->container != NULL) {
418                                 Workspace *t_ws = parent->workspace;
419                                 new->container = t_ws->table[parent->container->col][parent->container->row];
420                                 new->workspace = t_ws;
421                                 old_focused = new->container->currently_focused;
422                                 map_frame = workspace_is_visible(t_ws);
423                                 new->urgent = true;
424                                 /* This is a little tricky: we cannot use
425                                  * workspace_update_urgent_flag() because the
426                                  * new window was not yet inserted into the
427                                  * focus stack on t_ws. */
428                                 t_ws->urgent = true;
429                         } else {
430                                 DLOG("parent is not usable\n");
431                         }
432                 }
433
434                 struct Assignment *assign;
435                 TAILQ_FOREACH(assign, &assignments, assignments) {
436                         if (get_matching_client(conn, assign->windowclass_title, new) == NULL)
437                                 continue;
438
439                         if (assign->floating == ASSIGN_FLOATING_ONLY ||
440                             assign->floating == ASSIGN_FLOATING) {
441                                 new->floating = FLOATING_AUTO_ON;
442                                 LOG("Assignment matches, putting client into floating mode\n");
443                                 if (assign->floating == ASSIGN_FLOATING_ONLY)
444                                         break;
445                         }
446
447                         LOG("Assignment \"%s\" matches, so putting it on workspace %d\n",
448                             assign->windowclass_title, assign->workspace);
449
450                         if (c_ws->output->current_workspace->num == (assign->workspace-1)) {
451                                 DLOG("We are already there, no need to do anything\n");
452                                 break;
453                         }
454
455                         DLOG("Changing container/workspace and unmapping the client\n");
456                         Workspace *t_ws = workspace_get(assign->workspace-1);
457                         workspace_initialize(t_ws, c_ws->output, false);
458
459                         new->container = t_ws->table[t_ws->current_col][t_ws->current_row];
460                         new->workspace = t_ws;
461                         old_focused = new->container->currently_focused;
462
463                         map_frame = workspace_is_visible(t_ws);
464                         break;
465                 }
466         }
467
468         if (new->workspace->fullscreen_client != NULL) {
469                 DLOG("Setting below fullscreen window\n");
470
471                 /* If we are in fullscreen, we should place the window below
472                  * the fullscreen window to not be annoying */
473                 uint32_t values[] = {
474                         new->workspace->fullscreen_client->frame,
475                         XCB_STACK_MODE_BELOW
476                 };
477                 xcb_configure_window(conn, new->frame,
478                                      XCB_CONFIG_WINDOW_SIBLING |
479                                      XCB_CONFIG_WINDOW_STACK_MODE, values);
480         }
481
482         /* Insert into the currently active container, if it’s not a dock window */
483         if (!new->dock && !client_is_floating(new)) {
484                 /* Insert after the old active client, if existing. If it does not exist, the
485                    container is empty and it does not matter, where we insert it */
486                 if (old_focused != NULL && !old_focused->dock)
487                         CIRCLEQ_INSERT_AFTER(&(new->container->clients), old_focused, new, clients);
488                 else CIRCLEQ_INSERT_TAIL(&(new->container->clients), new, clients);
489
490                 if (new->container->workspace->fullscreen_client != NULL)
491                         SLIST_INSERT_AFTER(new->container->workspace->fullscreen_client, new, focus_clients);
492                 else SLIST_INSERT_HEAD(&(new->container->workspace->focus_stack), new, focus_clients);
493
494                 client_set_below_floating(conn, new);
495         }
496
497         if (client_is_floating(new)) {
498                 SLIST_INSERT_HEAD(&(new->workspace->focus_stack), new, focus_clients);
499
500                 /* Add the client to the list of floating clients for its workspace */
501                 TAILQ_INSERT_TAIL(&(new->workspace->floating_clients), new, floating_clients);
502
503                 new->container = NULL;
504
505                 new->rect.width = new->floating_rect.width + 2 + 2;
506                 new->rect.height = new->floating_rect.height + (font->height + 2 + 2) + 2;
507
508                 /* Some clients (like GIMP’s color picker window) get mapped
509                  * to (0, 0), so we push them to a reasonable position
510                  * (centered over their leader) */
511                 if (new->leader != 0 && x == 0 && y == 0) {
512                         DLOG("Floating client wants to (0x0), moving it over its leader instead\n");
513                         Client *leader = table_get(&by_child, new->leader);
514                         if (leader == NULL) {
515                                 DLOG("leader is NULL, centering it over current workspace\n");
516
517                                 x = c_ws->rect.x + (c_ws->rect.width / 2) - (new->rect.width / 2);
518                                 y = c_ws->rect.y + (c_ws->rect.height / 2) - (new->rect.height / 2);
519                         } else {
520                                 x = leader->rect.x + (leader->rect.width / 2) - (new->rect.width / 2);
521                                 y = leader->rect.y + (leader->rect.height / 2) - (new->rect.height / 2);
522                         }
523                 }
524                 new->floating_rect.x = new->rect.x = x;
525                 new->floating_rect.y = new->rect.y = y;
526                 DLOG("copying floating_rect from tiling (%d, %d) size (%d, %d)\n",
527                                 new->floating_rect.x, new->floating_rect.y,
528                                 new->floating_rect.width, new->floating_rect.height);
529                 DLOG("outer rect (%d, %d) size (%d, %d)\n",
530                                 new->rect.x, new->rect.y, new->rect.width, new->rect.height);
531
532                 /* Make sure it is on top of the other windows */
533                 xcb_raise_window(conn, new->frame);
534                 reposition_client(conn, new);
535                 resize_client(conn, new);
536                 /* redecorate_window flushes */
537                 redecorate_window(conn, new);
538         }
539
540         new->initialized = true;
541
542         /* Check if the window already got the fullscreen hint set */
543         xcb_atom_t *state;
544         if ((preply = xcb_get_property_reply(conn, state_cookie, NULL)) != NULL &&
545             (state = xcb_get_property_value(preply)) != NULL)
546                 /* Check all set _NET_WM_STATEs */
547                 for (int i = 0; i < xcb_get_property_value_length(preply); i++) {
548                         if (state[i] != atoms[_NET_WM_STATE_FULLSCREEN])
549                                 continue;
550                         /* If the window got the fullscreen state, we just toggle fullscreen
551                            and don’t event bother to redraw the layout – that would not change
552                            anything anyways */
553                         client_toggle_fullscreen(conn, new);
554                         goto map;
555                 }
556
557         render_layout(conn);
558
559 map:
560         /* Map the window first to avoid flickering */
561         xcb_map_window(conn, child);
562         if (map_frame)
563                 client_map(conn, new);
564
565         if ((CUR_CELL->workspace->fullscreen_client == NULL || new->fullscreen) && !new->dock) {
566                 /* Focus the new window if we’re not in fullscreen mode and if it is not a dock window */
567                 if ((new->workspace->fullscreen_client == NULL) || new->fullscreen) {
568                         if (!client_is_floating(new)) {
569                                 new->container->currently_focused = new;
570                                 if (map_frame)
571                                         render_container(conn, new->container);
572                         }
573                         if (new->container == CUR_CELL || client_is_floating(new)) {
574                                 xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, new->child, XCB_CURRENT_TIME);
575                                 ewmh_update_active_window(new->child);
576                         }
577                 }
578         }
579
580         xcb_flush(conn);
581 }
582 #endif