i3
floating.c
Go to the documentation of this file.
1 /*
2  * vim:ts=4:sw=4:expandtab
3  *
4  * i3 - an improved dynamic tiling window manager
5  * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
6  *
7  * floating.c: Floating windows.
8  *
9  */
10 #include "all.h"
11 
12 extern xcb_connection_t *conn;
13 
14 /*
15  * Calculates sum of heights and sum of widths of all currently active outputs
16  *
17  */
19  Output *output;
20  /* Use Rect to encapsulate dimensions, ignoring x/y */
21  Rect outputs_dimensions = {0, 0, 0, 0};
22  TAILQ_FOREACH(output, &outputs, outputs) {
23  outputs_dimensions.height += output->rect.height;
24  outputs_dimensions.width += output->rect.width;
25  }
26  return outputs_dimensions;
27 }
28 
29 void floating_enable(Con *con, bool automatic) {
30  bool set_focus = (con == focused);
31 
32  if (con->parent && con->parent->type == CT_DOCKAREA) {
33  LOG("Container is a dock window, not enabling floating mode.\n");
34  return;
35  }
36 
37  if (con_is_floating(con)) {
38  LOG("Container is already in floating mode, not doing anything.\n");
39  return;
40  }
41 
42  /* 1: If the container is a workspace container, we need to create a new
43  * split-container with the same orientation and make that one floating. We
44  * cannot touch the workspace container itself because floating containers
45  * are children of the workspace. */
46  if (con->type == CT_WORKSPACE) {
47  LOG("This is a workspace, creating new container around content\n");
48  if (con_num_children(con) == 0) {
49  LOG("Workspace is empty, aborting\n");
50  return;
51  }
52  /* TODO: refactor this with src/con.c:con_set_layout */
53  Con *new = con_new(NULL, NULL);
54  new->parent = con;
55  new->orientation = con->orientation;
56 
57  /* since the new container will be set into floating mode directly
58  * afterwards, we need to copy the workspace rect. */
59  memcpy(&(new->rect), &(con->rect), sizeof(Rect));
60 
61  Con *old_focused = TAILQ_FIRST(&(con->focus_head));
62  if (old_focused == TAILQ_END(&(con->focus_head)))
63  old_focused = NULL;
64 
65  /* 4: move the existing cons of this workspace below the new con */
66  DLOG("Moving cons\n");
67  Con *child;
68  while (!TAILQ_EMPTY(&(con->nodes_head))) {
69  child = TAILQ_FIRST(&(con->nodes_head));
70  con_detach(child);
71  con_attach(child, new, true);
72  }
73 
74  /* 4: attach the new split container to the workspace */
75  DLOG("Attaching new split to ws\n");
76  con_attach(new, con, false);
77 
78  if (old_focused)
79  con_focus(old_focused);
80 
81  con = new;
82  set_focus = false;
83  }
84 
85  /* 1: detach the container from its parent */
86  /* TODO: refactor this with tree_close() */
87  TAILQ_REMOVE(&(con->parent->nodes_head), con, nodes);
88  TAILQ_REMOVE(&(con->parent->focus_head), con, focused);
89 
90  con_fix_percent(con->parent);
91 
92  /* 2: create a new container to render the decoration on, add
93  * it as a floating window to the workspace */
94  Con *nc = con_new(NULL, NULL);
95  /* we need to set the parent afterwards instead of passing it as an
96  * argument to con_new() because nc would be inserted into the tiling layer
97  * otherwise. */
98  Con *ws = con_get_workspace(con);
99  nc->parent = ws;
101  nc->type = CT_FLOATING_CON;
102  /* We insert nc already, even though its rect is not yet calculated. This
103  * is necessary because otherwise the workspace might be empty (and get
104  * closed in tree_close()) even though it’s not. */
105  TAILQ_INSERT_TAIL(&(ws->floating_head), nc, floating_windows);
106  TAILQ_INSERT_TAIL(&(ws->focus_head), nc, focused);
107 
108  /* check if the parent container is empty and close it if so */
109  if ((con->parent->type == CT_CON || con->parent->type == CT_FLOATING_CON) &&
110  con_num_children(con->parent) == 0) {
111  DLOG("Old container empty after setting this child to floating, closing\n");
112  tree_close(con->parent, DONT_KILL_WINDOW, false, false);
113  }
114 
115  char *name;
116  sasprintf(&name, "[i3 con] floatingcon around %p", con);
117  x_set_name(nc, name);
118  free(name);
119 
120  /* find the height for the decorations */
121  int deco_height = config.font.height + 5;
122 
123  DLOG("Original rect: (%d, %d) with %d x %d\n", con->rect.x, con->rect.y, con->rect.width, con->rect.height);
124  DLOG("Geometry = (%d, %d) with %d x %d\n", con->geometry.x, con->geometry.y, con->geometry.width, con->geometry.height);
125  Rect zero = { 0, 0, 0, 0 };
126  nc->rect = con->geometry;
127  /* If the geometry was not set (split containers), we need to determine a
128  * sensible one by combining the geometry of all children */
129  if (memcmp(&(nc->rect), &zero, sizeof(Rect)) == 0) {
130  DLOG("Geometry not set, combining children\n");
131  Con *child;
132  TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
133  DLOG("child geometry: %d x %d\n", child->geometry.width, child->geometry.height);
134  nc->rect.width += child->geometry.width;
135  nc->rect.height = max(nc->rect.height, child->geometry.height);
136  }
137  }
138 
139  /* Define reasonable minimal and maximal sizes for floating windows */
140  const int floating_sane_min_height = 50;
141  const int floating_sane_min_width = 75;
142 
143  Rect floating_sane_max_dimensions;
144  floating_sane_max_dimensions = total_outputs_dimensions();
145 
146  /* Unless user requests otherwise (-1), ensure width/height do not exceed
147  * configured maxima or, if unconfigured, limit to combined width of all
148  * outputs */
149  if (config.floating_maximum_height != -1) {
151  nc->rect.height = min(nc->rect.height, floating_sane_max_dimensions.height);
152  else
154  }
155  if (config.floating_maximum_width != -1) {
157  nc->rect.width = min(nc->rect.width, floating_sane_max_dimensions.width);
158  else
160  }
161 
162  /* Unless user requests otherwise (-1), raise the width/height to
163  * reasonable minimum dimensions */
164  if (config.floating_minimum_height != -1) {
166  nc->rect.height = max(nc->rect.height, floating_sane_min_height);
167  else
169  }
170  if (config.floating_minimum_width != -1) {
172  nc->rect.width = max(nc->rect.width, floating_sane_min_width);
173  else
175  }
176 
177  /* add pixels for the decoration */
178  /* TODO: don’t add them when the user automatically puts new windows into
179  * 1pixel/borderless mode */
180  nc->rect.height += deco_height + 2;
181  nc->rect.width += 4;
182 
183  /* Honor the X11 border */
184  nc->rect.height += con->border_width * 2;
185  nc->rect.width += con->border_width * 2;
186 
187  /* Some clients (like GIMP’s color picker window) get mapped
188  * to (0, 0), so we push them to a reasonable position
189  * (centered over their leader) */
190  if (nc->rect.x == 0 && nc->rect.y == 0) {
191  Con *leader;
192  if (con->window && con->window->leader != XCB_NONE &&
193  (leader = con_by_window_id(con->window->leader)) != NULL) {
194  DLOG("Centering above leader\n");
195  nc->rect.x = leader->rect.x + (leader->rect.width / 2) - (nc->rect.width / 2);
196  nc->rect.y = leader->rect.y + (leader->rect.height / 2) - (nc->rect.height / 2);
197  } else {
198  /* center the window on workspace as fallback */
199  nc->rect.x = ws->rect.x + (ws->rect.width / 2) - (nc->rect.width / 2);
200  nc->rect.y = ws->rect.y + (ws->rect.height / 2) - (nc->rect.height / 2);
201  }
202  }
203 
204  /* Sanity check: Are the coordinates on the appropriate output? If not, we
205  * need to change them */
206  Output *current_output = get_output_containing(nc->rect.x, nc->rect.y);
207  Con *correct_output = con_get_output(ws);
208  if (!current_output || current_output->con != correct_output) {
209  DLOG("This floating window is on the wrong output, fixing coordinates (currently (%d, %d))\n",
210  nc->rect.x, nc->rect.y);
211  /* Take the relative coordinates of the current output, then add them
212  * to the coordinate space of the correct output */
213  uint32_t rel_x = (nc->rect.x - (current_output ? current_output->con->rect.x : 0));
214  uint32_t rel_y = (nc->rect.y - (current_output ? current_output->con->rect.y : 0));
215  nc->rect.x = correct_output->rect.x + rel_x;
216  nc->rect.y = correct_output->rect.y + rel_y;
217  }
218 
219  DLOG("Floating rect: (%d, %d) with %d x %d\n", nc->rect.x, nc->rect.y, nc->rect.width, nc->rect.height);
220 
221  /* 3: attach the child to the new parent container */
222  con->parent = nc;
223  con->percent = 1.0;
224  con->floating = FLOATING_USER_ON;
225 
226  /* 4: set the border style as specified with new_float */
227  if (automatic)
229 
230  /* 5: Subtract the deco_height in order to make the floating window appear
231  * at precisely the position it specified in its original geometry (which
232  * is what applications might remember). */
233  deco_height = (con->border_style == BS_NORMAL ? config.font.height + 5 : 0);
234  nc->rect.y -= deco_height;
235 
236  DLOG("Corrected y = %d (deco_height = %d)\n", nc->rect.y, deco_height);
237 
238  TAILQ_INSERT_TAIL(&(nc->nodes_head), con, nodes);
239  TAILQ_INSERT_TAIL(&(nc->focus_head), con, focused);
240 
241  /* render the cons to get initial window_rect correct */
242  render_con(nc, false);
243  render_con(con, false);
244 
245  if (set_focus)
246  con_focus(con);
247 
248  /* Check if we need to re-assign it to a different workspace because of its
249  * coordinates and exit if that was done successfully. */
251  return;
252 
253  /* Sanitize coordinates: Check if they are on any output */
254  if (get_output_containing(nc->rect.x, nc->rect.y) != NULL)
255  return;
256 
257  ELOG("No output found at destination coordinates, centering floating window on current ws\n");
258  nc->rect.x = ws->rect.x + (ws->rect.width / 2) - (nc->rect.width / 2);
259  nc->rect.y = ws->rect.y + (ws->rect.height / 2) - (nc->rect.height / 2);
260 }
261 
262 void floating_disable(Con *con, bool automatic) {
263  if (!con_is_floating(con)) {
264  LOG("Container isn't floating, not doing anything.\n");
265  return;
266  }
267 
268  Con *ws = con_get_workspace(con);
269 
270  /* 1: detach from parent container */
271  TAILQ_REMOVE(&(con->parent->nodes_head), con, nodes);
272  TAILQ_REMOVE(&(con->parent->focus_head), con, focused);
273 
274  /* 2: kill parent container */
275  TAILQ_REMOVE(&(con->parent->parent->floating_head), con->parent, floating_windows);
276  TAILQ_REMOVE(&(con->parent->parent->focus_head), con->parent, focused);
277  tree_close(con->parent, DONT_KILL_WINDOW, true, false);
278 
279  /* 3: re-attach to the parent of the currently focused con on the workspace
280  * this floating con was on */
282 
283  /* if there is no other container on this workspace, focused will be the
284  * workspace itself */
285  if (focused->type == CT_WORKSPACE)
286  con->parent = focused;
287  else con->parent = focused->parent;
288 
289  /* con_fix_percent will adjust the percent value */
290  con->percent = 0.0;
291 
292  con->floating = FLOATING_USER_OFF;
293 
294  con_attach(con, con->parent, false);
295 
296  con_fix_percent(con->parent);
297  // TODO: don’t influence focus handling when Con was not focused before.
298  con_focus(con);
299 }
300 
301 /*
302  * Toggles floating mode for the given container.
303  *
304  * If the automatic flag is set to true, this was an automatic update by a change of the
305  * window class from the application which can be overwritten by the user.
306  *
307  */
308 void toggle_floating_mode(Con *con, bool automatic) {
309  /* see if the client is already floating */
310  if (con_is_floating(con)) {
311  LOG("already floating, re-setting to tiling\n");
312 
313  floating_disable(con, automatic);
314  return;
315  }
316 
317  floating_enable(con, automatic);
318 }
319 
320 /*
321  * Raises the given container in the list of floating containers
322  *
323  */
325  DLOG("Raising floating con %p / %s\n", con, con->name);
326  TAILQ_REMOVE(&(con->parent->floating_head), con, floating_windows);
327  TAILQ_INSERT_TAIL(&(con->parent->floating_head), con, floating_windows);
328 }
329 
330 /*
331  * Checks if con’s coordinates are within its workspace and re-assigns it to
332  * the actual workspace if not.
333  *
334  */
336  Output *output = get_output_containing(
337  con->rect.x + (con->rect.width / 2),
338  con->rect.y + (con->rect.height / 2));
339 
340  if (!output) {
341  ELOG("No output found at destination coordinates?\n");
342  return false;
343  }
344 
345  if (con_get_output(con) == output->con) {
346  DLOG("still the same ws\n");
347  return false;
348  }
349 
350  DLOG("Need to re-assign!\n");
351 
352  Con *content = output_get_content(output->con);
353  Con *ws = TAILQ_FIRST(&(content->focus_head));
354  DLOG("Moving con %p / %s to workspace %p / %s\n", con, con->name, ws, ws->name);
355  con_move_to_workspace(con, ws, false, true);
357  return true;
358 }
359 
360 DRAGGING_CB(drag_window_callback) {
361  const struct xcb_button_press_event_t *event = extra;
362 
363  /* Reposition the client correctly while moving */
364  con->rect.x = old_rect->x + (new_x - event->root_x);
365  con->rect.y = old_rect->y + (new_y - event->root_y);
366 
367  render_con(con, false);
368  x_push_node(con);
369  xcb_flush(conn);
370 
371  /* Check if we cross workspace boundaries while moving */
372  if (!floating_maybe_reassign_ws(con))
373  return;
374  tree_render();
375 }
376 
377 /*
378  * Called when the user clicked on the titlebar of a floating window.
379  * Calls the drag_pointer function with the drag_window callback
380  *
381  */
382 void floating_drag_window(Con *con, const xcb_button_press_event_t *event) {
383  DLOG("floating_drag_window\n");
384 
385  /* Push changes before dragging, so that the window gets raised now and not
386  * after the user releases the mouse button */
387  tree_render();
388 
389  /* Drag the window */
390  drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, drag_window_callback, event);
391  tree_render();
392 }
393 
394 /*
395  * This is an ugly data structure which we need because there is no standard
396  * way of having nested functions (only available as a gcc extension at the
397  * moment, clang doesn’t support it) or blocks (only available as a clang
398  * extension and only on Mac OS X systems at the moment).
399  *
400  */
403  const bool proportional;
404  const xcb_button_press_event_t *event;
405 };
406 
407 DRAGGING_CB(resize_window_callback) {
408  const struct resize_window_callback_params *params = extra;
409  const xcb_button_press_event_t *event = params->event;
410  border_t corner = params->corner;
411 
412  int32_t dest_x = con->rect.x;
413  int32_t dest_y = con->rect.y;
414  uint32_t dest_width;
415  uint32_t dest_height;
416 
417  double ratio = (double) old_rect->width / old_rect->height;
418 
419  /* First guess: We resize by exactly the amount the mouse moved,
420  * taking into account in which corner the client was grabbed */
421  if (corner & BORDER_LEFT)
422  dest_width = old_rect->width - (new_x - event->root_x);
423  else dest_width = old_rect->width + (new_x - event->root_x);
424 
425  if (corner & BORDER_TOP)
426  dest_height = old_rect->height - (new_y - event->root_y);
427  else dest_height = old_rect->height + (new_y - event->root_y);
428 
429  /* Obey minimum window size */
430  Rect minimum = con_minimum_size(con);
431  dest_width = max(dest_width, minimum.width);
432  dest_height = max(dest_height, minimum.height);
433 
434  /* User wants to keep proportions, so we may have to adjust our values */
435  if (params->proportional) {
436  dest_width = max(dest_width, (int) (dest_height * ratio));
437  dest_height = max(dest_height, (int) (dest_width / ratio));
438  }
439 
440  /* If not the lower right corner is grabbed, we must also reposition
441  * the client by exactly the amount we resized it */
442  if (corner & BORDER_LEFT)
443  dest_x = old_rect->x + (old_rect->width - dest_width);
444 
445  if (corner & BORDER_TOP)
446  dest_y = old_rect->y + (old_rect->height - dest_height);
447 
448  con->rect = (Rect) { dest_x, dest_y, dest_width, dest_height };
449 
450  /* TODO: don’t re-render the whole tree just because we change
451  * coordinates of a floating window */
452  tree_render();
454 }
455 
456 /*
457  * Called when the user clicked on a floating window while holding the
458  * floating_modifier and the right mouse button.
459  * Calls the drag_pointer function with the resize_window callback
460  *
461  */
463  const xcb_button_press_event_t *event) {
464  DLOG("floating_resize_window\n");
465 
466  /* corner saves the nearest corner to the original click. It contains
467  * a bitmask of the nearest borders (BORDER_LEFT, BORDER_RIGHT, …) */
468  border_t corner = 0;
469 
470  if (event->event_x <= (con->rect.width / 2))
471  corner |= BORDER_LEFT;
472  else corner |= BORDER_RIGHT;
473 
474  if (event->event_y <= (con->rect.height / 2))
475  corner |= BORDER_TOP;
476  else corner |= BORDER_BOTTOM;
477 
478  struct resize_window_callback_params params = { corner, proportional, event };
479 
480  drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, resize_window_callback, &params);
481 }
482 
483 /*
484  * This function grabs your pointer and lets you drag stuff around (borders).
485  * Every time you move your mouse, an XCB_MOTION_NOTIFY event will be received
486  * and the given callback will be called with the parameters specified (client,
487  * border on which the click originally was), the original rect of the client,
488  * the event and the new coordinates (x, y).
489  *
490  */
491 void drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t
492  confine_to, border_t border, callback_t callback, const void *extra)
493 {
494  uint32_t new_x, new_y;
495  Rect old_rect;
496  if (con != NULL)
497  memcpy(&old_rect, &(con->rect), sizeof(Rect));
498 
499  /* Grab the pointer */
500  xcb_grab_pointer_cookie_t cookie;
501  xcb_grab_pointer_reply_t *reply;
502  cookie = xcb_grab_pointer(conn,
503  false, /* get all pointer events specified by the following mask */
504  root, /* grab the root window */
505  XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION, /* which events to let through */
506  XCB_GRAB_MODE_ASYNC, /* pointer events should continue as normal */
507  XCB_GRAB_MODE_ASYNC, /* keyboard mode */
508  confine_to, /* confine_to = in which window should the cursor stay */
509  XCB_NONE, /* don’t display a special cursor */
510  XCB_CURRENT_TIME);
511 
512  if ((reply = xcb_grab_pointer_reply(conn, cookie, NULL)) == NULL) {
513  ELOG("Could not grab pointer\n");
514  return;
515  }
516 
517  free(reply);
518 
519  /* Go into our own event loop */
520  xcb_flush(conn);
521 
522  xcb_generic_event_t *inside_event, *last_motion_notify = NULL;
523  bool loop_done = false;
524  /* I’ve always wanted to have my own eventhandler… */
525  while (!loop_done && (inside_event = xcb_wait_for_event(conn))) {
526  /* We now handle all events we can get using xcb_poll_for_event */
527  do {
528  /* skip x11 errors */
529  if (inside_event->response_type == 0) {
530  free(inside_event);
531  continue;
532  }
533  /* Strip off the highest bit (set if the event is generated) */
534  int type = (inside_event->response_type & 0x7F);
535 
536  switch (type) {
537  case XCB_BUTTON_RELEASE:
538  loop_done = true;
539  break;
540 
541  case XCB_MOTION_NOTIFY:
542  /* motion_notify events are saved for later */
543  FREE(last_motion_notify);
544  last_motion_notify = inside_event;
545  break;
546 
547  case XCB_UNMAP_NOTIFY:
548  case XCB_KEY_PRESS:
549  case XCB_KEY_RELEASE:
550  DLOG("Unmap-notify, aborting\n");
551  handle_event(type, inside_event);
552  loop_done = true;
553  break;
554 
555  default:
556  DLOG("Passing to original handler\n");
557  /* Use original handler */
558  handle_event(type, inside_event);
559  break;
560  }
561  if (last_motion_notify != inside_event)
562  free(inside_event);
563  } while ((inside_event = xcb_poll_for_event(conn)) != NULL);
564 
565  if (last_motion_notify == NULL)
566  continue;
567 
568  new_x = ((xcb_motion_notify_event_t*)last_motion_notify)->root_x;
569  new_y = ((xcb_motion_notify_event_t*)last_motion_notify)->root_y;
570 
571  callback(con, &old_rect, new_x, new_y, extra);
572  FREE(last_motion_notify);
573  }
574 
575  xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
576  xcb_flush(conn);
577 }
578 
579 /*
580  * Repositions the CT_FLOATING_CON to have the coordinates specified by
581  * newrect, but only if the coordinates are not out-of-bounds. Also reassigns
582  * the floating con to a different workspace if this move was across different
583  * outputs.
584  *
585  */
586 void floating_reposition(Con *con, Rect newrect) {
587  /* Sanity check: Are the new coordinates on any output? If not, we
588  * ignore that request. */
589  Output *output = get_output_containing(
590  newrect.x + (newrect.width / 2),
591  newrect.y + (newrect.height / 2));
592 
593  if (!output) {
594  ELOG("No output found at destination coordinates. Not repositioning.\n");
595  return;
596  }
597 
598  con->rect = newrect;
599 
601  tree_render();
602 }
603 
604 /*
605  * Fixes the coordinates of the floating window whenever the window gets
606  * reassigned to a different output (or when the output’s rect changes).
607  *
608  */
609 void floating_fix_coordinates(Con *con, Rect *old_rect, Rect *new_rect) {
610  DLOG("Fixing coordinates of floating window %p\n", con);
611  /* First we get the x/y coordinates relative to the x/y coordinates
612  * of the output on which the window is on */
613  uint32_t rel_x = (con->rect.x - old_rect->x);
614  uint32_t rel_y = (con->rect.y - old_rect->y);
615  /* Then we calculate a fraction, for example 0.63 for a window
616  * which is at y = 1212 of a 1920 px high output */
617  double fraction_x = ((double)rel_x / old_rect->width);
618  double fraction_y = ((double)rel_y / old_rect->height);
619  DLOG("rel_x = %d, rel_y = %d, fraction_x = %f, fraction_y = %f, output->w = %d, output->h = %d\n",
620  rel_x, rel_y, fraction_x, fraction_y, old_rect->width, old_rect->height);
621  con->rect.x = new_rect->x + (fraction_x * new_rect->width);
622  con->rect.y = new_rect->y + (fraction_y * new_rect->height);
623  DLOG("Resulting coordinates: x = %d, y = %d\n", con->rect.x, con->rect.y);
624 }
625 
626 #if 0
627 /*
628  * Moves the client 10px to the specified direction.
629  *
630  */
631 void floating_move(xcb_connection_t *conn, Client *currently_focused, direction_t direction) {
632  DLOG("floating move\n");
633 
634  Rect destination = currently_focused->rect;
635  Rect *screen = &(currently_focused->workspace->output->rect);
636 
637  switch (direction) {
638  case D_LEFT:
639  destination.x -= 10;
640  break;
641  case D_RIGHT:
642  destination.x += 10;
643  break;
644  case D_UP:
645  destination.y -= 10;
646  break;
647  case D_DOWN:
648  destination.y += 10;
649  break;
650  /* to make static analyzers happy */
651  default:
652  break;
653  }
654 
655  /* Prevent windows from vanishing completely */
656  if ((int32_t)(destination.x + destination.width - 5) <= (int32_t)screen->x ||
657  (int32_t)(destination.x + 5) >= (int32_t)(screen->x + screen->width) ||
658  (int32_t)(destination.y + destination.height - 5) <= (int32_t)screen->y ||
659  (int32_t)(destination.y + 5) >= (int32_t)(screen->y + screen->height)) {
660  DLOG("boundary check failed, not moving\n");
661  return;
662  }
663 
664  currently_focused->rect = destination;
665  reposition_client(conn, currently_focused);
666 
667  /* Because reposition_client does not send a faked configure event (only resize does),
668  * we need to initiate that on our own */
669  fake_absolute_configure_notify(conn, currently_focused);
670  /* fake_absolute_configure_notify flushes */
671 }
672 
673 /*
674  * Hides all floating clients (or show them if they are currently hidden) on
675  * the specified workspace.
676  *
677  */
678 void floating_toggle_hide(xcb_connection_t *conn, Workspace *workspace) {
679  Client *client;
680 
681  workspace->floating_hidden = !workspace->floating_hidden;
682  DLOG("floating_hidden is now: %d\n", workspace->floating_hidden);
683  TAILQ_FOREACH(client, &(workspace->floating_clients), floating_clients) {
684  if (workspace->floating_hidden)
685  client_unmap(conn, client);
686  else client_map(conn, client);
687  }
688 
689  /* If we just unmapped all floating windows we should ensure that the focus
690  * is set correctly, that ist, to the first non-floating client in stack */
691  if (workspace->floating_hidden)
692  SLIST_FOREACH(client, &(workspace->focus_stack), focus_clients) {
693  if (client_is_floating(client))
694  continue;
695  set_focus(conn, client, true);
696  return;
697  }
698 
699  xcb_flush(conn);
700 }
701 #endif