]> git.sur5r.net Git - i3/i3/blobdiff - src/floating.c
Merge branch 'master' into next
[i3/i3] / src / floating.c
index 3d2c1d3191fb4f8b1367bf19a2f2aaf9cf64e691..643f204b64110a12aede45c4bf4b6ee9e4851f86 100644 (file)
@@ -28,6 +28,80 @@ static Rect total_outputs_dimensions(void) {
     return outputs_dimensions;
 }
 
+/**
+ * Called when a floating window is created or resized.
+ * This function resizes the window if its size is higher or lower than the
+ * configured maximum/minimum size, respectively.
+ *
+ */
+void floating_check_size(Con *floating_con) {
+    /* Define reasonable minimal and maximal sizes for floating windows */
+    const int floating_sane_min_height = 50;
+    const int floating_sane_min_width = 75;
+    Rect floating_sane_max_dimensions;
+    Con *focused_con = con_descend_focused(floating_con);
+
+    /* obey size increments */
+    if (focused_con->height_increment || focused_con->width_increment) {
+        Rect border_rect = con_border_style_rect(focused_con);
+
+        /* We have to do the opposite calculations that render_con() do
+         * to get the exact size we want. */
+        border_rect.width = -border_rect.width;
+        border_rect.width += 2 * focused_con->border_width;
+        border_rect.height = -border_rect.height;
+        border_rect.height += 2 * focused_con->border_width;
+        if (con_border_style(focused_con) == BS_NORMAL)
+            border_rect.height += render_deco_height();
+
+        if (focused_con->height_increment &&
+            floating_con->rect.height >= focused_con->base_height + border_rect.height) {
+            floating_con->rect.height -= focused_con->base_height + border_rect.height;
+            floating_con->rect.height -= floating_con->rect.height % focused_con->height_increment;
+            floating_con->rect.height += focused_con->base_height + border_rect.height;
+        }
+
+        if (focused_con->width_increment &&
+            floating_con->rect.width >= focused_con->base_width + border_rect.width) {
+            floating_con->rect.width -= focused_con->base_width + border_rect.width;
+            floating_con->rect.width -= floating_con->rect.width % focused_con->width_increment;
+            floating_con->rect.width += focused_con->base_width + border_rect.width;
+        }
+    }
+
+    /* Unless user requests otherwise (-1), ensure width/height do not exceed
+     * configured maxima or, if unconfigured, limit to combined width of all
+     * outputs */
+    if (config.floating_minimum_height != -1) {
+        if (config.floating_minimum_height == 0)
+            floating_con->rect.height = max(floating_con->rect.height, floating_sane_min_height);
+        else
+            floating_con->rect.height = max(floating_con->rect.height, config.floating_minimum_height);
+    }
+    if (config.floating_minimum_width != -1) {
+        if (config.floating_minimum_width == 0)
+            floating_con->rect.width = max(floating_con->rect.width, floating_sane_min_width);
+        else
+            floating_con->rect.width = max(floating_con->rect.width, config.floating_minimum_width);
+    }
+
+    /* Unless user requests otherwise (-1), raise the width/height to
+     * reasonable minimum dimensions */
+    floating_sane_max_dimensions = total_outputs_dimensions();
+    if (config.floating_maximum_height != -1) {
+        if (config.floating_maximum_height == 0)
+            floating_con->rect.height = min(floating_con->rect.height, floating_sane_max_dimensions.height);
+        else
+            floating_con->rect.height = min(floating_con->rect.height, config.floating_maximum_height);
+    }
+    if (config.floating_maximum_width != -1) {
+        if (config.floating_maximum_width == 0)
+            floating_con->rect.width = min(floating_con->rect.width, floating_sane_max_dimensions.width);
+        else
+            floating_con->rect.width = min(floating_con->rect.width, config.floating_maximum_width);
+    }
+}
+
 void floating_enable(Con *con, bool automatic) {
     bool set_focus = (con == focused);
 
@@ -99,7 +173,6 @@ void floating_enable(Con *con, bool automatic) {
      * otherwise. */
     Con *ws = con_get_workspace(con);
     nc->parent = ws;
-    nc->split = true;
     nc->type = CT_FLOATING_CON;
     nc->layout = L_SPLITH;
     /* We insert nc already, even though its rect is not yet calculated. This
@@ -121,7 +194,7 @@ void floating_enable(Con *con, bool automatic) {
     free(name);
 
     /* find the height for the decorations */
-    int deco_height = config.font.height + 5;
+    int deco_height = render_deco_height();
 
     DLOG("Original rect: (%d, %d) with %d x %d\n", con->rect.x, con->rect.y, con->rect.width, con->rect.height);
     DLOG("Geometry = (%d, %d) with %d x %d\n", con->geometry.x, con->geometry.y, con->geometry.width, con->geometry.height);
@@ -139,43 +212,7 @@ void floating_enable(Con *con, bool automatic) {
         }
     }
 
-    /* Define reasonable minimal and maximal sizes for floating windows */
-    const int floating_sane_min_height = 50;
-    const int floating_sane_min_width = 75;
-
-    Rect floating_sane_max_dimensions;
-    floating_sane_max_dimensions = total_outputs_dimensions();
-
-    /* Unless user requests otherwise (-1), ensure width/height do not exceed
-     * configured maxima or, if unconfigured, limit to combined width of all
-     * outputs */
-    if (config.floating_maximum_height != -1) {
-        if (config.floating_maximum_height == 0)
-            nc->rect.height = min(nc->rect.height, floating_sane_max_dimensions.height);
-        else
-            nc->rect.height = min(nc->rect.height, config.floating_maximum_height);
-    }
-    if (config.floating_maximum_width != -1) {
-        if (config.floating_maximum_width == 0)
-            nc->rect.width = min(nc->rect.width, floating_sane_max_dimensions.width);
-        else
-            nc->rect.width = min(nc->rect.width, config.floating_maximum_width);
-    }
-
-    /* Unless user requests otherwise (-1), raise the width/height to
-     * reasonable minimum dimensions */
-    if (config.floating_minimum_height != -1) {
-        if (config.floating_minimum_height == 0)
-            nc->rect.height = max(nc->rect.height, floating_sane_min_height);
-        else
-            nc->rect.height = max(nc->rect.height, config.floating_minimum_height);
-    }
-    if (config.floating_minimum_width != -1) {
-        if (config.floating_minimum_width == 0)
-            nc->rect.width = max(nc->rect.width, floating_sane_min_width);
-        else
-            nc->rect.width = max(nc->rect.width, config.floating_minimum_width);
-    }
+    floating_check_size(nc);
 
     /* 3: attach the child to the new parent container. We need to do this
      * because con_border_style_rect() needs to access con->parent. */
@@ -220,17 +257,22 @@ void floating_enable(Con *con, bool automatic) {
 
     /* Sanity check: Are the coordinates on the appropriate output? If not, we
      * need to change them */
-    Output *current_output = get_output_containing(nc->rect.x, nc->rect.y);
+    Output *current_output = get_output_containing(nc->rect.x +
+        (nc->rect.width / 2), nc->rect.y + (nc->rect.height / 2));
+
     Con *correct_output = con_get_output(ws);
     if (!current_output || current_output->con != correct_output) {
         DLOG("This floating window is on the wrong output, fixing coordinates (currently (%d, %d))\n",
              nc->rect.x, nc->rect.y);
-        /* Take the relative coordinates of the current output, then add them
-         * to the coordinate space of the correct output */
-        uint32_t rel_x = (nc->rect.x - (current_output ? current_output->con->rect.x : 0));
-        uint32_t rel_y = (nc->rect.y - (current_output ? current_output->con->rect.y : 0));
-        nc->rect.x = correct_output->rect.x + rel_x;
-        nc->rect.y = correct_output->rect.y + rel_y;
+
+        /* If moving from one output to another, keep the relative position
+         * consistent (e.g. a centered dialog will remain centered). */
+        if (current_output)
+            floating_fix_coordinates(nc, &current_output->con->rect, &correct_output->rect);
+        else {
+            nc->rect.x = correct_output->rect.x;
+            nc->rect.y = correct_output->rect.y;
+        }
     }
 
     DLOG("Floating rect: (%d, %d) with %d x %d\n", nc->rect.x, nc->rect.y, nc->rect.width, nc->rect.height);
@@ -238,7 +280,7 @@ void floating_enable(Con *con, bool automatic) {
     /* 5: Subtract the deco_height in order to make the floating window appear
      * at precisely the position it specified in its original geometry (which
      * is what applications might remember). */
-    deco_height = (con->border_style == BS_NORMAL ? config.font.height + 5 : 0);
+    deco_height = (con->border_style == BS_NORMAL ? render_deco_height() : 0);
     nc->rect.y -= deco_height;
 
     DLOG("Corrected y = %d (deco_height = %d)\n", nc->rect.y, deco_height);
@@ -273,6 +315,8 @@ void floating_disable(Con *con, bool automatic) {
         return;
     }
 
+    const bool set_focus = (con == focused);
+
     Con *ws = con_get_workspace(con);
 
     /* 1: detach from parent container */
@@ -302,8 +346,9 @@ void floating_disable(Con *con, bool automatic) {
     con_attach(con, con->parent, false);
 
     con_fix_percent(con->parent);
-    // TODO: don’t influence focus handling when Con was not focused before.
-    con_focus(con);
+
+    if (set_focus)
+        con_focus(con);
 }
 
 /*
@@ -379,6 +424,8 @@ DRAGGING_CB(drag_window_callback) {
     /* Check if we cross workspace boundaries while moving */
     if (!floating_maybe_reassign_ws(con))
         return;
+    /* Ensure not to warp the pointer while dragging */
+    x_set_warp_to(NULL);
     tree_render();
 }
 
@@ -395,7 +442,12 @@ void floating_drag_window(Con *con, const xcb_button_press_event_t *event) {
     tree_render();
 
     /* Drag the window */
-    drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, drag_window_callback, event);
+    drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, XCURSOR_CURSOR_MOVE, drag_window_callback, event);
+
+    /* If this is a scratchpad window, don't auto center it from now on. */
+    if (con->scratchpad_state == SCRATCHPAD_FRESH)
+        con->scratchpad_state = SCRATCHPAD_CHANGED;
+
     tree_render();
 }
 
@@ -434,26 +486,27 @@ DRAGGING_CB(resize_window_callback) {
         dest_height = old_rect->height - (new_y - event->root_y);
     else dest_height = old_rect->height + (new_y - event->root_y);
 
-    /* Obey minimum window size */
-    Rect minimum = con_minimum_size(con);
-    dest_width = max(dest_width, minimum.width);
-    dest_height = max(dest_height, minimum.height);
-
     /* User wants to keep proportions, so we may have to adjust our values */
     if (params->proportional) {
         dest_width = max(dest_width, (int) (dest_height * ratio));
         dest_height = max(dest_height, (int) (dest_width / ratio));
     }
 
+    con->rect = (Rect) { dest_x, dest_y, dest_width, dest_height };
+
+    /* Obey window size */
+    floating_check_size(con);
+
     /* If not the lower right corner is grabbed, we must also reposition
      * the client by exactly the amount we resized it */
     if (corner & BORDER_LEFT)
-        dest_x = old_rect->x + (old_rect->width - dest_width);
+        dest_x = old_rect->x + (old_rect->width - con->rect.width);
 
     if (corner & BORDER_TOP)
-        dest_y = old_rect->y + (old_rect->height - dest_height);
+        dest_y = old_rect->y + (old_rect->height - con->rect.height);
 
-    con->rect = (Rect) { dest_x, dest_y, dest_width, dest_height };
+    con->rect.x = dest_x;
+    con->rect.y = dest_y;
 
     /* TODO: don’t re-render the whole tree just because we change
      * coordinates of a floating window */
@@ -479,13 +532,25 @@ void floating_resize_window(Con *con, const bool proportional,
         corner |= BORDER_LEFT;
     else corner |= BORDER_RIGHT;
 
-    if (event->event_y <= (con->rect.height / 2))
+    int cursor = 0;
+    if (event->event_y <= (con->rect.height / 2)) {
         corner |= BORDER_TOP;
-    else corner |= BORDER_BOTTOM;
+        cursor = (corner & BORDER_LEFT) ?
+            XCURSOR_CURSOR_TOP_LEFT_CORNER : XCURSOR_CURSOR_TOP_RIGHT_CORNER;
+    }
+    else {
+        corner |= BORDER_BOTTOM;
+        cursor = (corner & BORDER_LEFT) ?
+            XCURSOR_CURSOR_BOTTOM_LEFT_CORNER : XCURSOR_CURSOR_BOTTOM_RIGHT_CORNER;
+    }
 
     struct resize_window_callback_params params = { corner, proportional, event };
 
-    drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, resize_window_callback, &params);
+    drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, cursor, resize_window_callback, &params);
+
+    /* If this is a scratchpad window, don't auto center it from now on. */
+    if (con->scratchpad_state == SCRATCHPAD_FRESH)
+        con->scratchpad_state = SCRATCHPAD_CHANGED;
 }
 
 /*
@@ -497,13 +562,16 @@ void floating_resize_window(Con *con, const bool proportional,
  *
  */
 void drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t
-                confine_to, border_t border, callback_t callback, const void *extra)
+                confine_to, border_t border, int cursor, callback_t callback, const void *extra)
 {
     uint32_t new_x, new_y;
     Rect old_rect = { 0, 0, 0, 0 };
     if (con != NULL)
         memcpy(&old_rect, &(con->rect), sizeof(Rect));
 
+    Cursor xcursor = (cursor && xcursor_supported) ?
+        xcursor_get_cursor(cursor) : XCB_NONE;
+
     /* Grab the pointer */
     xcb_grab_pointer_cookie_t cookie;
     xcb_grab_pointer_reply_t *reply;
@@ -514,7 +582,7 @@ void drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t
         XCB_GRAB_MODE_ASYNC, /* pointer events should continue as normal */
         XCB_GRAB_MODE_ASYNC, /* keyboard mode */
         confine_to,          /* confine_to = in which window should the cursor stay */
-        XCB_NONE,            /* don’t display a special cursor */
+        xcursor,             /* possibly display a special cursor */
         XCB_CURRENT_TIME);
 
     if ((reply = xcb_grab_pointer_reply(conn, cookie, NULL)) == NULL) {
@@ -570,7 +638,7 @@ void drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t
                 free(inside_event);
         } while ((inside_event = xcb_poll_for_event(conn)) != NULL);
 
-        if (last_motion_notify == NULL)
+        if (last_motion_notify == NULL || loop_done)
             continue;
 
         new_x = ((xcb_motion_notify_event_t*)last_motion_notify)->root_x;
@@ -594,11 +662,7 @@ void drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t
 void floating_reposition(Con *con, Rect newrect) {
     /* Sanity check: Are the new coordinates on any output? If not, we
      * ignore that request. */
-    Output *output = get_output_containing(
-        newrect.x + (newrect.width / 2),
-        newrect.y + (newrect.height / 2));
-
-    if (!output) {
+    if (!contained_by_output(newrect)) {
         ELOG("No output found at destination coordinates. Not repositioning.\n");
         return;
     }
@@ -606,6 +670,11 @@ void floating_reposition(Con *con, Rect newrect) {
     con->rect = newrect;
 
     floating_maybe_reassign_ws(con);
+
+    /* If this is a scratchpad window, don't auto center it from now on. */
+    if (con->scratchpad_state == SCRATCHPAD_FRESH)
+        con->scratchpad_state = SCRATCHPAD_CHANGED;
+
     tree_render();
 }
 
@@ -623,16 +692,18 @@ void floating_fix_coordinates(Con *con, Rect *old_rect, Rect *new_rect) {
          new_rect->x, new_rect->y, new_rect->width, new_rect->height);
     /* First we get the x/y coordinates relative to the x/y coordinates
      * of the output on which the window is on */
-    int32_t rel_x = (con->rect.x - old_rect->x);
-    int32_t rel_y = (con->rect.y - old_rect->y);
+    int32_t rel_x = con->rect.x - old_rect->x + (int32_t)(con->rect.width  / 2);
+    int32_t rel_y = con->rect.y - old_rect->y + (int32_t)(con->rect.height / 2);
     /* Then we calculate a fraction, for example 0.63 for a window
      * which is at y = 1212 of a 1920 px high output */
     DLOG("rel_x = %d, rel_y = %d, fraction_x = %f, fraction_y = %f, output->w = %d, output->h = %d\n",
           rel_x, rel_y, (double)rel_x / old_rect->width, (double)rel_y / old_rect->height,
           old_rect->width, old_rect->height);
     /* Here we have to multiply at first. Or we will lose precision when not compiled with -msse2 */
-    con->rect.x = (int32_t)new_rect->x + (double)(rel_x * (int32_t)new_rect->width)  / (int32_t)old_rect->width;
-    con->rect.y = (int32_t)new_rect->y + (double)(rel_y * (int32_t)new_rect->height) / (int32_t)old_rect->height;
+    con->rect.x = (int32_t)new_rect->x + (double)(rel_x * (int32_t)new_rect->width)
+        / (int32_t)old_rect->width - (int32_t)(con->rect.width / 2);
+    con->rect.y = (int32_t)new_rect->y + (double)(rel_y * (int32_t)new_rect->height)
+        / (int32_t)old_rect->height - (int32_t)(con->rect.height / 2);
     DLOG("Resulting coordinates: x = %d, y = %d\n", con->rect.x, con->rect.y);
 }