]> git.sur5r.net Git - i3/i3/blobdiff - src/con.c
Merge branch 'master' into next
[i3/i3] / src / con.c
index 5b5acc1b1b4598007a32f67cded10669e638f5e0..7b5cc499468e5594f463a726b3a4864ee832e281 100644 (file)
--- a/src/con.c
+++ b/src/con.c
@@ -2,11 +2,11 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2010 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
  *
- * con.c contains all functions which deal with containers directly (creating
- * containers, searching containers, getting specific properties from
- * containers, …).
+ * con.c: Functions which deal with containers directly (creating containers,
+ *        searching containers, getting specific properties from containers,
+ *        …).
  *
  */
 #include "all.h"
@@ -129,7 +129,9 @@ void con_attach(Con *con, Con *parent, bool ignore_focus) {
          * workspace or a new split container with the configured
          * workspace_layout).
          */
-        if (con->window != NULL && parent->type == CT_WORKSPACE) {
+        if (con->window != NULL &&
+            parent->type == CT_WORKSPACE &&
+            config.default_layout != L_DEFAULT) {
             DLOG("Parent is a workspace. Applying default layout...\n");
             Con *target = workspace_attach_to(parent);
 
@@ -193,7 +195,6 @@ void con_focus(Con *con) {
         con->urgent = false;
         workspace_update_urgent_flag(con_get_workspace(con));
     }
-    DLOG("con_focus done = %p\n", con);
 }
 
 /*
@@ -358,6 +359,18 @@ Con *con_inside_floating(Con *con) {
     return con_inside_floating(con->parent);
 }
 
+/*
+ * Checks if the given container is inside a focused container.
+ *
+ */
+bool con_inside_focused(Con *con) {
+    if (con == focused)
+        return true;
+    if (!con->parent)
+        return false;
+    return con_inside_focused(con->parent);
+}
+
 /*
  * Returns the container with the given client window ID or NULL if no such
  * container exists.
@@ -392,8 +405,8 @@ Con *con_by_frame_id(xcb_window_t frame) {
 Con *con_for_window(Con *con, i3Window *window, Match **store_match) {
     Con *child;
     Match *match;
-    DLOG("searching con for window %p starting at con %p\n", window, con);
-    DLOG("class == %s\n", window->class_class);
+    //DLOG("searching con for window %p starting at con %p\n", window, con);
+    //DLOG("class == %s\n", window->class_class);
 
     TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
         TAILQ_FOREACH(match, &(child->swallow_head), matches) {
@@ -534,16 +547,27 @@ update_netwm_state:
         values[num++] = A__NET_WM_STATE_FULLSCREEN;
 
     xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id,
-                        A__NET_WM_STATE, A_ATOM, 32, num, values);
+                        A__NET_WM_STATE, XCB_ATOM_ATOM, 32, num, values);
 }
 
 /*
  * Moves the given container to the currently focused container on the given
  * workspace.
+ *
+ * The fix_coordinates flag will translate the current coordinates (offset from
+ * the monitor position basically) to appropriate coordinates on the
+ * destination workspace.
+ * Not enabling this behaviour comes in handy when this function gets called by
+ * floating_maybe_reassign_ws, which will only "move" a floating window when it
+ * *already* changed its coordinates to a different output.
+ *
+ * The dont_warp flag disables pointer warping and will be set when this
+ * function is called while dragging a floating window.
+ *
  * TODO: is there a better place for this function?
  *
  */
-void con_move_to_workspace(Con *con, Con *workspace) {
+void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool dont_warp) {
     if (con->type == CT_WORKSPACE) {
         DLOG("Moving workspaces is not yet implemented.\n");
         return;
@@ -585,6 +609,41 @@ void con_move_to_workspace(Con *con, Con *workspace) {
         next = ws;
     }
 
+    if (source_output != dest_output) {
+        /* Take the relative coordinates of the current output, then add them
+         * to the coordinate space of the correct output */
+        if (fix_coordinates && con->type == CT_FLOATING_CON) {
+            DLOG("Floating window, fixing coordinates\n");
+            /* First we get the x/y coordinates relative to the x/y coordinates
+             * of the output on which the window is on */
+            uint32_t rel_x = (con->rect.x - source_output->rect.x);
+            uint32_t rel_y = (con->rect.y - source_output->rect.y);
+            /* Then we calculate a fraction, for example 0.63 for a window
+             * which is at y = 1212 of a 1920 px high output */
+            double fraction_x = ((double)rel_x / source_output->rect.width);
+            double fraction_y = ((double)rel_y / source_output->rect.height);
+            DLOG("rel_x = %d, rel_y = %d, fraction_x = %f, fraction_y = %f, output->w = %d, output->h = %d\n",
+                 rel_x, rel_y, fraction_x, fraction_y, source_output->rect.width, source_output->rect.height);
+            con->rect.x = dest_output->rect.x + (fraction_x * dest_output->rect.width);
+            con->rect.y = dest_output->rect.y + (fraction_y * dest_output->rect.height);
+            DLOG("Resulting coordinates: x = %d, y = %d\n", con->rect.x, con->rect.y);
+        } else DLOG("Not fixing coordinates, fix_coordinates flag = %d\n", fix_coordinates);
+
+        /* If moving to a visible workspace, call show so it can be considered
+         * focused. Must do before attaching because workspace_show checks to see
+         * if focused container is in its area. */
+        if (workspace_is_visible(workspace)) {
+            workspace_show(workspace);
+
+            /* Don’t warp if told so (when dragging floating windows with the
+             * mouse for example) */
+            if (dont_warp)
+                x_set_warp_to(NULL);
+            else
+                x_set_warp_to(&(con->rect));
+        }
+    }
+
     DLOG("Re-attaching container to %p / %s\n", next, next->name);
     /* 5: re-attach the con to the parent of this focused container */
     Con *parent = con->parent;
@@ -597,8 +656,8 @@ void con_move_to_workspace(Con *con, Con *workspace) {
     con_fix_percent(next);
 
     /* 7: focus the con on the target workspace (the X focus is only updated by
-     * calling tree_render(), so for the "real" focus this is a no-op) */
-    con_focus(con);
+     * calling tree_render(), so for the "real" focus this is a no-op). */
+    con_focus(con_descend_focused(con));
 
     /* 8: when moving to a visible workspace on a different output, we keep the
      * con focused. Otherwise, we leave the focus on the current workspace as we
@@ -607,7 +666,11 @@ void con_move_to_workspace(Con *con, Con *workspace) {
         workspace_is_visible(workspace)) {
         DLOG("Moved to a different output, focusing target\n");
     } else {
-        con_focus(focus_next);
+        /* Descend focus stack in case focus_next is a workspace which can
+         * occur if we move to the same workspace.  Also show current workspace
+         * to ensure it is focused. */
+        workspace_show(con_get_workspace(focus_next));
+        con_focus(con_descend_focused(focus_next));
     }
 
     CALL(parent, on_remove_child);
@@ -781,16 +844,17 @@ Con *con_descend_tiling_focused(Con *con) {
  *
  */
 Con *con_descend_direction(Con *con, direction_t direction) {
-    Con *most;
-    DLOG("con_descend_direction(%p, %d)\n", con, direction);
+    Con *most = NULL;
+    int orientation = con_orientation(con);
+    DLOG("con_descend_direction(%p, orientation %d, direction %d)\n", con, orientation, direction);
     if (direction == D_LEFT || direction == D_RIGHT) {
-        if (con->orientation == HORIZ) {
+        if (orientation == HORIZ) {
             /* If the direction is horizontal, we can use either the first
              * (D_RIGHT) or the last con (D_LEFT) */
             if (direction == D_RIGHT)
                 most = TAILQ_FIRST(&(con->nodes_head));
             else most = TAILQ_LAST(&(con->nodes_head), nodes_head);
-        } else if (con->orientation == VERT) {
+        } else if (orientation == VERT) {
             /* Wrong orientation. We use the last focused con. Within that con,
              * we recurse to chose the left/right con or at least the last
              * focused one. */
@@ -803,13 +867,13 @@ Con *con_descend_direction(Con *con, direction_t direction) {
     }
 
     if (direction == D_UP || direction == D_DOWN) {
-        if (con->orientation == VERT) {
+        if (orientation == VERT) {
             /* If the direction is vertical, we can use either the first
              * (D_DOWN) or the last con (D_UP) */
             if (direction == D_UP)
                 most = TAILQ_LAST(&(con->nodes_head), nodes_head);
             else most = TAILQ_FIRST(&(con->nodes_head));
-        } else if (con->orientation == HORIZ) {
+        } else if (orientation == HORIZ) {
             /* Wrong orientation. We use the last focused con. Within that con,
              * we recurse to chose the top/bottom con or at least the last
              * focused one. */
@@ -877,6 +941,49 @@ int con_border_style(Con *con) {
     return con->border_style;
 }
 
+/*
+ * Sets the given border style on con, correctly keeping the position/size of a
+ * floating window.
+ *
+ */
+void con_set_border_style(Con *con, int border_style) {
+    /* Handle the simple case: non-floating containerns */
+    if (!con_is_floating(con)) {
+        con->border_style = border_style;
+        return;
+    }
+
+    /* For floating containers, we want to keep the position/size of the
+     * *window* itself. We first add the border pixels to con->rect to make
+     * con->rect represent the absolute position of the window. Then, we change
+     * the border and subtract the new border pixels. Afterwards, we update
+     * parent->rect to contain con. */
+    DLOG("This is a floating container\n");
+
+    Rect bsr = con_border_style_rect(con);
+    con->rect.x += bsr.x;
+    con->rect.y += bsr.y;
+    con->rect.width += bsr.width;
+    con->rect.height += bsr.height;
+
+    /* Change the border style, get new border/decoration values. */
+    con->border_style = border_style;
+    bsr = con_border_style_rect(con);
+    int deco_height =
+        (con->border_style == BS_NORMAL ? config.font.height + 5 : 0);
+
+    con->rect.x -= bsr.x;
+    con->rect.y -= bsr.y;
+    con->rect.width -= bsr.width;
+    con->rect.height -= bsr.height;
+
+    Con *parent = con->parent;
+    parent->rect.x = con->rect.x;
+    parent->rect.y = con->rect.y - deco_height;
+    parent->rect.width = con->rect.width;
+    parent->rect.height = con->rect.height + deco_height;
+}
+
 /*
  * This function changes the layout of a given container. Use it to handle
  * special cases like changing a whole workspace to stacked/tabbed (creates a
@@ -945,20 +1052,29 @@ static void con_on_remove_child(Con *con) {
 
     /* Every container 'above' (in the hierarchy) the workspace content should
      * not be closed when the last child was removed */
-    if (con->type == CT_WORKSPACE ||
-        con->type == CT_OUTPUT ||
+    if (con->type == CT_OUTPUT ||
         con->type == CT_ROOT ||
         con->type == CT_DOCKAREA) {
         DLOG("not handling, type = %d\n", con->type);
         return;
     }
 
+    /* For workspaces, close them only if they're not visible anymore */
+    if (con->type == CT_WORKSPACE) {
+        if (TAILQ_EMPTY(&(con->focus_head)) && !workspace_is_visible(con)) {
+            LOG("Closing old workspace (%p / %s), it is empty\n", con, con->name);
+            tree_close(con, DONT_KILL_WINDOW, false, false);
+            ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}");
+        }
+        return;
+    }
+
     /* TODO: check if this container would swallow any other client and
      * don’t close it automatically. */
     int children = con_num_children(con);
     if (children == 0) {
         DLOG("Container empty, closing\n");
-        tree_close(con, DONT_KILL_WINDOW, false);
+        tree_close(con, DONT_KILL_WINDOW, false, false);
         return;
     }
 }