]> git.sur5r.net Git - i3/i3/blob - src/floating.c
Merge branch 'fix-coords'
[i3/i3] / src / floating.c
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  */
18 static Rect total_outputs_dimensions(void) {
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;
100     nc->orientation = NO_ORIENTATION;
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) {
150         if (config.floating_maximum_height == 0)
151             nc->rect.height = min(nc->rect.height, floating_sane_max_dimensions.height);
152         else
153             nc->rect.height = min(nc->rect.height, config.floating_maximum_height);
154     }
155     if (config.floating_maximum_width != -1) {
156         if (config.floating_maximum_width == 0)
157             nc->rect.width = min(nc->rect.width, floating_sane_max_dimensions.width);
158         else
159             nc->rect.width = min(nc->rect.width, config.floating_maximum_width);
160     }
161
162     /* Unless user requests otherwise (-1), raise the width/height to
163      * reasonable minimum dimensions */
164     if (config.floating_minimum_height != -1) {
165         if (config.floating_minimum_height == 0)
166             nc->rect.height = max(nc->rect.height, floating_sane_min_height);
167         else
168             nc->rect.height = max(nc->rect.height, config.floating_minimum_height);
169     }
170     if (config.floating_minimum_width != -1) {
171         if (config.floating_minimum_width == 0)
172             nc->rect.width = max(nc->rect.width, floating_sane_min_width);
173         else
174             nc->rect.width = max(nc->rect.width, config.floating_minimum_width);
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)
228         con->border_style = config.default_floating_border;
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. */
250     if (floating_maybe_reassign_ws(nc))
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 */
281     Con *focused = con_descend_tiling_focused(ws);
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  */
324 void floating_raise_con(Con *con) {
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  */
335 bool floating_maybe_reassign_ws(Con *con) {
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);
356     con_focus(con_descend_focused(con));
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  */
401 struct resize_window_callback_params {
402     const border_t corner;
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();
453     x_push_changes(croot);
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  */
462 void floating_resize_window(Con *con, const bool proportional,
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
600     floating_maybe_reassign_ws(con);
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 (rect (%d, %d), %d x %d)\n",
611          con, con->rect.x, con->rect.y, con->rect.width, con->rect.height);
612     DLOG("old_rect = (%d, %d), %d x %d\n",
613          old_rect->x, old_rect->y, old_rect->width, old_rect->height);
614     DLOG("new_rect = (%d, %d), %d x %d\n",
615          new_rect->x, new_rect->y, new_rect->width, new_rect->height);
616     /* First we get the x/y coordinates relative to the x/y coordinates
617      * of the output on which the window is on */
618     int32_t rel_x = (con->rect.x - old_rect->x);
619     int32_t rel_y = (con->rect.y - old_rect->y);
620     /* Then we calculate a fraction, for example 0.63 for a window
621      * which is at y = 1212 of a 1920 px high output */
622     double fraction_x = ((double)rel_x / (int32_t)old_rect->width);
623     double fraction_y = ((double)rel_y / (int32_t)old_rect->height);
624     DLOG("rel_x = %d, rel_y = %d, fraction_x = %f, fraction_y = %f, output->w = %d, output->h = %d\n",
625          rel_x, rel_y, fraction_x, fraction_y, old_rect->width, old_rect->height);
626     con->rect.x = (int32_t)new_rect->x + (fraction_x * (int32_t)new_rect->width);
627     con->rect.y = (int32_t)new_rect->y + (fraction_y * (int32_t)new_rect->height);
628     DLOG("Resulting coordinates: x = %d, y = %d\n", con->rect.x, con->rect.y);
629 }
630
631 #if 0
632 /*
633  * Moves the client 10px to the specified direction.
634  *
635  */
636 void floating_move(xcb_connection_t *conn, Client *currently_focused, direction_t direction) {
637         DLOG("floating move\n");
638
639         Rect destination = currently_focused->rect;
640         Rect *screen = &(currently_focused->workspace->output->rect);
641
642         switch (direction) {
643                 case D_LEFT:
644                         destination.x -= 10;
645                         break;
646                 case D_RIGHT:
647                         destination.x += 10;
648                         break;
649                 case D_UP:
650                         destination.y -= 10;
651                         break;
652                 case D_DOWN:
653                         destination.y += 10;
654                         break;
655                 /* to make static analyzers happy */
656                 default:
657                         break;
658         }
659
660         /* Prevent windows from vanishing completely */
661         if ((int32_t)(destination.x + destination.width - 5) <= (int32_t)screen->x ||
662             (int32_t)(destination.x + 5) >= (int32_t)(screen->x + screen->width) ||
663             (int32_t)(destination.y + destination.height - 5) <= (int32_t)screen->y ||
664             (int32_t)(destination.y + 5) >= (int32_t)(screen->y + screen->height)) {
665                 DLOG("boundary check failed, not moving\n");
666                 return;
667         }
668
669         currently_focused->rect = destination;
670         reposition_client(conn, currently_focused);
671
672         /* Because reposition_client does not send a faked configure event (only resize does),
673          * we need to initiate that on our own */
674         fake_absolute_configure_notify(conn, currently_focused);
675         /* fake_absolute_configure_notify flushes */
676 }
677
678 /*
679  * Hides all floating clients (or show them if they are currently hidden) on
680  * the specified workspace.
681  *
682  */
683 void floating_toggle_hide(xcb_connection_t *conn, Workspace *workspace) {
684         Client *client;
685
686         workspace->floating_hidden = !workspace->floating_hidden;
687         DLOG("floating_hidden is now: %d\n", workspace->floating_hidden);
688         TAILQ_FOREACH(client, &(workspace->floating_clients), floating_clients) {
689                 if (workspace->floating_hidden)
690                         client_unmap(conn, client);
691                 else client_map(conn, client);
692         }
693
694         /* If we just unmapped all floating windows we should ensure that the focus
695          * is set correctly, that ist, to the first non-floating client in stack */
696         if (workspace->floating_hidden)
697                 SLIST_FOREACH(client, &(workspace->focus_stack), focus_clients) {
698                         if (client_is_floating(client))
699                                 continue;
700                         set_focus(conn, client, true);
701                         return;
702                 }
703
704         xcb_flush(conn);
705 }
706 #endif