]> git.sur5r.net Git - i3/i3/commitdiff
Merge pull request #3102 from jolange/fix3071
authorIngo Bürk <admin@airblader.de>
Wed, 17 Jan 2018 20:24:52 +0000 (21:24 +0100)
committerGitHub <noreply@github.com>
Wed, 17 Jan 2018 20:24:52 +0000 (21:24 +0100)
tiling resize: remove minimum size (was 5%)

31 files changed:
docs/i3bar-protocol
docs/userguide
etc/config
etc/config.keycodes
include/commands.h
include/con.h
include/resize.h
src/click.c
src/commands.c
src/con.c
src/floating.c
src/handlers.c
src/load_layout.c
src/main.c
src/manage.c
src/move.c
src/randr.c
src/resize.c
src/scratchpad.c
src/tree.c
src/workspace.c
testcases/t/113-urgent.t
testcases/t/156-fullscreen-focus.t
testcases/t/185-scratchpad.t
testcases/t/189-floating-constraints.t
testcases/t/195-net-active-window.t
testcases/t/240-focus-on-window-activation.t
testcases/t/252-floating-size.t
testcases/t/293-focus-follows-mouse.t [new file with mode: 0644]
testcases/t/504-move-workspace-to-output.t
testcases/t/541-resize-set-tiling.t [new file with mode: 0644]

index 1a302587819347cff696c497725b3ad521647f79..cf86531cc5f399774e983770719245729693c17a 100644 (file)
@@ -177,7 +177,8 @@ separator_block_width::
 markup::
        A string that indicates how the text of the block should be parsed. Set to
        +"pango"+ to use https://developer.gnome.org/pango/stable/PangoMarkupFormat.html[Pango markup].
-       Set to +"none"+ to not use any markup (default).
+       Set to +"none"+ to not use any markup (default). Pango markup only works
+       if you use a pango font.
 
 If you want to put in your own entries into a block, prefix the key with an
 underscore (_). i3bar will ignore all keys it doesn’t understand, and prefixing
index 98242a9b0921af154266aaf65a79d1c92b2dc53b..974135acb794bb37a58769f0ba0ab90c66f4553b 100644 (file)
@@ -2320,8 +2320,11 @@ space from all the other containers. The optional pixel argument specifies by
 how many pixels a *floating container* should be grown or shrunk (the default
 is 10 pixels). The ppt argument means percentage points and specifies by how
 many percentage points a *tiling container* should be grown or shrunk (the
-default is 10 percentage points). Note that +resize set+ will only work for
-floating containers.
+default is 10 percentage points).
+
+Notes about +resize set+: a value of 0 for <width> or <height> means "do
+not resize in this direction", and resizing a tiling container by +px+ is not
+implemented.
 
 It is recommended to define bindings for resizing in a dedicated binding mode.
 See <<binding_modes>> and the example in the i3
index 5b751d00df55f7f6831e5845697d4ce98c9a8eff..3be9831dd9004e00e3469268dc2cb1e5d2f6678d 100644 (file)
@@ -104,29 +104,43 @@ bindsym Mod1+Shift+minus move scratchpad
 # If there are multiple scratchpad windows, this command cycles through them.
 bindsym Mod1+minus scratchpad show
 
+# Define names for default workspaces for which we configure key bindings later on.
+# We use variables to avoid repeating the names in multiple places.
+set $ws1 "1"
+set $ws2 "2"
+set $ws3 "3"
+set $ws4 "4"
+set $ws5 "5"
+set $ws6 "6"
+set $ws7 "7"
+set $ws8 "8"
+set $ws9 "9"
+set $ws10 "10"
+
+
 # switch to workspace
-bindsym Mod1+1 workspace 1
-bindsym Mod1+2 workspace 2
-bindsym Mod1+3 workspace 3
-bindsym Mod1+4 workspace 4
-bindsym Mod1+5 workspace 5
-bindsym Mod1+6 workspace 6
-bindsym Mod1+7 workspace 7
-bindsym Mod1+8 workspace 8
-bindsym Mod1+9 workspace 9
-bindsym Mod1+0 workspace 10
+bindsym Mod1+1 workspace $ws1
+bindsym Mod1+2 workspace $ws2
+bindsym Mod1+3 workspace $ws3
+bindsym Mod1+4 workspace $ws4
+bindsym Mod1+5 workspace $ws5
+bindsym Mod1+6 workspace $ws6
+bindsym Mod1+7 workspace $ws7
+bindsym Mod1+8 workspace $ws8
+bindsym Mod1+9 workspace $ws9
+bindsym Mod1+0 workspace $ws10
 
 # move focused container to workspace
-bindsym Mod1+Shift+1 move container to workspace 1
-bindsym Mod1+Shift+2 move container to workspace 2
-bindsym Mod1+Shift+3 move container to workspace 3
-bindsym Mod1+Shift+4 move container to workspace 4
-bindsym Mod1+Shift+5 move container to workspace 5
-bindsym Mod1+Shift+6 move container to workspace 6
-bindsym Mod1+Shift+7 move container to workspace 7
-bindsym Mod1+Shift+8 move container to workspace 8
-bindsym Mod1+Shift+9 move container to workspace 9
-bindsym Mod1+Shift+0 move container to workspace 10
+bindsym Mod1+Shift+1 move container to workspace $ws1
+bindsym Mod1+Shift+2 move container to workspace $ws2
+bindsym Mod1+Shift+3 move container to workspace $ws3
+bindsym Mod1+Shift+4 move container to workspace $ws4
+bindsym Mod1+Shift+5 move container to workspace $ws5
+bindsym Mod1+Shift+6 move container to workspace $ws6
+bindsym Mod1+Shift+7 move container to workspace $ws7
+bindsym Mod1+Shift+8 move container to workspace $ws8
+bindsym Mod1+Shift+9 move container to workspace $ws9
+bindsym Mod1+Shift+0 move container to workspace $ws10
 
 # reload the configuration file
 bindsym Mod1+Shift+c reload
@@ -154,7 +168,7 @@ mode "resize" {
         bindsym Up          resize shrink height 10 px or 10 ppt
         bindsym Right       resize grow width 10 px or 10 ppt
 
-        # back to normal: Enter or Escape
+        # back to normal: Enter or Escape or Mod1+r
         bindsym Return mode "default"
         bindsym Escape mode "default"
         bindsym Mod1+r mode "default"
index c07462b4e1cc144c0eef197bb1678398f42cbb09..2d56876c249b4d39d72fd19743b9d9b105731fe8 100644 (file)
@@ -91,29 +91,42 @@ bindcode $mod+38 focus parent
 # focus the child container
 #bindsym $mod+d focus child
 
+# Define names for default workspaces for which we configure key bindings later on.
+# We use variables to avoid repeating the names in multiple places.
+set $ws1 "1"
+set $ws2 "2"
+set $ws3 "3"
+set $ws4 "4"
+set $ws5 "5"
+set $ws6 "6"
+set $ws7 "7"
+set $ws8 "8"
+set $ws9 "9"
+set $ws10 "10"
+
 # switch to workspace
-bindcode $mod+10 workspace 1
-bindcode $mod+11 workspace 2
-bindcode $mod+12 workspace 3
-bindcode $mod+13 workspace 4
-bindcode $mod+14 workspace 5
-bindcode $mod+15 workspace 6
-bindcode $mod+16 workspace 7
-bindcode $mod+17 workspace 8
-bindcode $mod+18 workspace 9
-bindcode $mod+19 workspace 10
+bindcode $mod+10 workspace $ws1
+bindcode $mod+11 workspace $ws2
+bindcode $mod+12 workspace $ws3
+bindcode $mod+13 workspace $ws4
+bindcode $mod+14 workspace $ws5
+bindcode $mod+15 workspace $ws6
+bindcode $mod+16 workspace $ws7
+bindcode $mod+17 workspace $ws8
+bindcode $mod+18 workspace $ws9
+bindcode $mod+19 workspace $ws10
 
 # move focused container to workspace
-bindcode $mod+Shift+10 move container to workspace 1
-bindcode $mod+Shift+11 move container to workspace 2
-bindcode $mod+Shift+12 move container to workspace 3
-bindcode $mod+Shift+13 move container to workspace 4
-bindcode $mod+Shift+14 move container to workspace 5
-bindcode $mod+Shift+15 move container to workspace 6
-bindcode $mod+Shift+16 move container to workspace 7
-bindcode $mod+Shift+17 move container to workspace 8
-bindcode $mod+Shift+18 move container to workspace 9
-bindcode $mod+Shift+19 move container to workspace 10
+bindcode $mod+Shift+10 move container to workspace $ws1
+bindcode $mod+Shift+11 move container to workspace $ws2
+bindcode $mod+Shift+12 move container to workspace $ws3
+bindcode $mod+Shift+13 move container to workspace $ws4
+bindcode $mod+Shift+14 move container to workspace $ws5
+bindcode $mod+Shift+15 move container to workspace $ws6
+bindcode $mod+Shift+16 move container to workspace $ws7
+bindcode $mod+Shift+17 move container to workspace $ws8
+bindcode $mod+Shift+18 move container to workspace $ws9
+bindcode $mod+Shift+19 move container to workspace $ws10
 
 # reload the configuration file
 bindcode $mod+Shift+54 reload
@@ -141,7 +154,7 @@ mode "resize" {
         bindcode 111 resize shrink height 10 px or 10 ppt
         bindcode 114 resize grow width 10 px or 10 ppt
 
-        # back to normal: Enter or Escape
+        # back to normal: Enter or Escape or $mod+r
         bindcode 36 mode "default"
         bindcode 9 mode "default"
         bindcode $mod+27 mode "default"
index 85d5fe78a9f27056e565c048d52e4edc806e9bbc..1057f021db5a3d2aa45034b3557f51071cf03c07 100644 (file)
@@ -63,7 +63,7 @@ void cmd_move_con_to_workspace_name(I3_CMD, const char *name, const char *no_aut
 void cmd_move_con_to_workspace_number(I3_CMD, const char *which, const char *no_auto_back_and_forth);
 
 /**
- * Implementation of 'resize set <px> [px] <px> [px]'.
+ * Implementation of 'resize set <width> [px | ppt] <height> [px | ppt]'.
  *
  */
 void cmd_resize_set(I3_CMD, long cwidth, const char *mode_width, long cheight, const char *mode_height);
index b88ea354cd7e0b28e754ed0ac3e97167026d1916..726fec96d145e501898d2793367a3d1572eaa269 100644 (file)
@@ -38,6 +38,12 @@ void con_free(Con *con);
  */
 void con_focus(Con *con);
 
+/**
+ * Sets input focus to the given container and raises it to the top.
+ *
+ */
+void con_activate(Con *con);
+
 /**
  * Closes the given container.
  *
index 7b33de907fb397d8348bb70d1ee23558c9781271..386341561854fcaeefa240661bf10cc59cef6245 100644 (file)
@@ -11,6 +11,6 @@
 
 #include <config.h>
 
-bool resize_find_tiling_participants(Con **current, Con **other, direction_t direction);
+bool resize_find_tiling_participants(Con **current, Con **other, direction_t direction, bool both_sides);
 
 int resize_graphical_handler(Con *first, Con *second, orientation_t orientation, const xcb_button_press_event_t *event);
index 78af8a0350cd067179f5aaedcf263ad631e11b84..b036c5f8033c29e84b2d91b475b57daff8d64572 100644 (file)
@@ -49,7 +49,7 @@ static bool tiling_resize_for_border(Con *con, border_t border, xcb_button_press
             break;
     }
 
-    bool res = resize_find_tiling_participants(&first, &second, search_direction);
+    bool res = resize_find_tiling_participants(&first, &second, search_direction, false);
     if (!res) {
         LOG("No second container in this direction found.\n");
         return false;
@@ -241,7 +241,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
          * The splitv container will be focused. */
         Con *focused = con->parent;
         focused = TAILQ_FIRST(&(focused->focus_head));
-        con_focus(focused);
+        con_activate(focused);
         /* To prevent scrolling from going outside the container (see ticket
          * #557), we first check if scrolling is possible at all. */
         bool scroll_prev_possible = (TAILQ_PREV(focused, nodes_head, nodes) != NULL);
@@ -256,7 +256,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
     }
 
     /* 2: focus this con. */
-    con_focus(con);
+    con_activate(con);
 
     /* 3: For floating containers, we also want to raise them on click.
      * We will skip handling events on floating cons in fullscreen mode */
index 1938b250df63ec227b56a48e48782677e0e9ca91..73164f325a07a7f19bb50fd3028e5ae9c44e2730 100644 (file)
@@ -503,7 +503,7 @@ static bool cmd_resize_tiling_direction(I3_CMD, Con *current, const char *way, c
     else
         search_direction = D_DOWN;
 
-    bool res = resize_find_tiling_participants(&first, &second, search_direction);
+    bool res = resize_find_tiling_participants(&first, &second, search_direction, false);
     if (!res) {
         LOG("No second container in this direction found.\n");
         ysuccess(false);
@@ -542,19 +542,15 @@ static bool cmd_resize_tiling_direction(I3_CMD, Con *current, const char *way, c
 
 static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, const char *way, const char *direction, int ppt) {
     LOG("width/height resize\n");
-    /* get the appropriate current container (skip stacked/tabbed cons) */
-    while (current->parent->layout == L_STACKED ||
-           current->parent->layout == L_TABBED)
-        current = current->parent;
-
-    /* Then further go up until we find one with the matching orientation. */
-    orientation_t search_orientation =
-        (strcmp(direction, "width") == 0 ? HORIZ : VERT);
 
-    while (current->type != CT_WORKSPACE &&
-           current->type != CT_FLOATING_CON &&
-           (con_orientation(current->parent) != search_orientation || con_num_children(current->parent) == 1))
-        current = current->parent;
+    /* get the appropriate current container (skip stacked/tabbed cons) */
+    Con *dummy = NULL;
+    direction_t search_direction = (strcmp(direction, "width") == 0 ? D_LEFT : D_DOWN);
+    bool search_result = resize_find_tiling_participants(&current, &dummy, search_direction, true);
+    if (search_result == false) {
+        ysuccess(false);
+        return false;
+    }
 
     /* get the default percentage */
     int children = con_num_children(current->parent);
@@ -562,24 +558,6 @@ static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, const char *way
     double percentage = 1.0 / children;
     LOG("default percentage = %f\n", percentage);
 
-    orientation_t orientation = con_orientation(current->parent);
-
-    if ((orientation == HORIZ &&
-         strcmp(direction, "height") == 0) ||
-        (orientation == VERT &&
-         strcmp(direction, "width") == 0)) {
-        LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n",
-            (orientation == HORIZ ? "horizontal" : "vertical"));
-        ysuccess(false);
-        return false;
-    }
-
-    if (children == 1) {
-        LOG("This is the only container, cannot resize.\n");
-        ysuccess(false);
-        return false;
-    }
-
     /* Ensure all the other children have a percentage set. */
     Con *child;
     TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) {
@@ -670,7 +648,7 @@ void cmd_resize(I3_CMD, const char *way, const char *direction, long resize_px,
  */
 void cmd_resize_set(I3_CMD, long cwidth, const char *mode_width, long cheight, const char *mode_height) {
     DLOG("resizing to %ld %s x %ld %s\n", cwidth, mode_width, cheight, mode_height);
-    if (cwidth <= 0 || cheight <= 0) {
+    if (cwidth < 0 || cheight < 0) {
         ELOG("Resize failed: dimensions cannot be negative (was %ld %s x %ld %s)\n", cwidth, mode_width, cheight, mode_height);
         return;
     }
@@ -678,25 +656,84 @@ void cmd_resize_set(I3_CMD, long cwidth, const char *mode_width, long cheight, c
     HANDLE_EMPTY_MATCH;
 
     owindow *current;
+    bool success = true;
     TAILQ_FOREACH(current, &owindows, owindows) {
         Con *floating_con;
         if ((floating_con = con_inside_floating(current->con))) {
             Con *output = con_get_output(floating_con);
-            if (mode_width && strcmp(mode_width, "ppt") == 0) {
+            if (cwidth == 0) {
+                cwidth = output->rect.width;
+            } else if (mode_width && strcmp(mode_width, "ppt") == 0) {
                 cwidth = output->rect.width * ((double)cwidth / 100.0);
             }
-            if (mode_height && strcmp(mode_height, "ppt") == 0) {
+            if (cheight == 0) {
+                cheight = output->rect.height;
+            } else if (mode_height && strcmp(mode_height, "ppt") == 0) {
                 cheight = output->rect.height * ((double)cheight / 100.0);
             }
             floating_resize(floating_con, cwidth, cheight);
         } else {
-            ELOG("Resize failed: %p not a floating container\n", current->con);
+            if (current->con->window && current->con->window->dock) {
+                DLOG("This is a dock window. Not resizing (con = %p)\n)", current->con);
+                continue;
+            }
+
+            if (cwidth > 0 && mode_width && strcmp(mode_width, "ppt") == 0) {
+                /* get the appropriate current container (skip stacked/tabbed cons) */
+                Con *target = current->con;
+                Con *dummy;
+                resize_find_tiling_participants(&target, &dummy, D_LEFT, true);
+
+                /* Calculate new size for the target container */
+                double current_percent = target->percent;
+                char *action_string;
+                long adjustment;
+
+                if (current_percent > cwidth) {
+                    action_string = "shrink";
+                    adjustment = (int)(current_percent * 100) - cwidth;
+                } else {
+                    action_string = "grow";
+                    adjustment = cwidth - (int)(current_percent * 100);
+                }
+
+                /* perform resizing and report failure if not possible */
+                if (!cmd_resize_tiling_width_height(current_match, cmd_output,
+                                                    target, action_string, "width", adjustment)) {
+                    success = false;
+                }
+            }
+
+            if (cheight > 0 && mode_width && strcmp(mode_width, "ppt") == 0) {
+                /* get the appropriate current container (skip stacked/tabbed cons) */
+                Con *target = current->con;
+                Con *dummy;
+                resize_find_tiling_participants(&target, &dummy, D_DOWN, true);
+
+                /* Calculate new size for the target container */
+                double current_percent = target->percent;
+                char *action_string;
+                long adjustment;
+
+                if (current_percent > cheight) {
+                    action_string = "shrink";
+                    adjustment = (int)(current_percent * 100) - cheight;
+                } else {
+                    action_string = "grow";
+                    adjustment = cheight - (int)(current_percent * 100);
+                }
+
+                /* perform resizing and report failure if not possible */
+                if (!cmd_resize_tiling_width_height(current_match, cmd_output,
+                                                    target, action_string, "height", adjustment)) {
+                    success = false;
+                }
+            }
         }
     }
 
     cmd_output->needs_tree_render = true;
-    // XXX: default reply for now, make this a better reply
-    ysuccess(true);
+    ysuccess(success);
 }
 
 /*
@@ -756,6 +793,7 @@ void cmd_nop(I3_CMD, const char *comment) {
     LOG("-------------------------------------------------\n");
     LOG("  NOP: %s\n", comment);
     LOG("-------------------------------------------------\n");
+    ysuccess(true);
 }
 
 /*
@@ -1250,7 +1288,7 @@ static void cmd_focus_force_focus(Con *con) {
     if (fullscreen_on_ws && fullscreen_on_ws != con && !con_has_parent(con, fullscreen_on_ws)) {
         con_disable_fullscreen(fullscreen_on_ws);
     }
-    con_focus(con);
+    con_activate(con);
 }
 
 /*
@@ -1368,12 +1406,12 @@ void cmd_focus(I3_CMD) {
          * the target workspace, then revert focus. */
         Con *currently_focused = focused;
         cmd_focus_force_focus(current->con);
-        con_focus(currently_focused);
+        con_activate(currently_focused);
 
         /* Now switch to the workspace, then focus */
         workspace_show(ws);
         LOG("focusing %p / %s\n", current->con, current->con->name);
-        con_focus(current->con);
+        con_activate(current->con);
         count++;
     }
 
@@ -1485,7 +1523,7 @@ void cmd_move_direction(I3_CMD, const char *direction, long move_px) {
 
     /* the move command should not disturb focus */
     if (focused != initially_focused)
-        con_focus(initially_focused);
+        con_activate(initially_focused);
 
     // XXX: default reply for now, make this a better reply
     ysuccess(true);
@@ -1610,7 +1648,7 @@ void cmd_open(I3_CMD) {
     LOG("opening new container\n");
     Con *con = tree_open_con(NULL, NULL);
     con->layout = L_SPLITH;
-    con_focus(con);
+    con_activate(con);
 
     y(map_open);
     ystr("success");
@@ -1999,7 +2037,7 @@ void cmd_rename_workspace(I3_CMD, const char *old_name, const char *new_name) {
     }
 
     /* Restore the previous focus since con_attach messes with the focus. */
-    con_focus(previously_focused);
+    con_activate(previously_focused);
 
     cmd_output->needs_tree_render = true;
     ysuccess(true);
index c9e2b6cfa076f819e2d32e31a474d4927625f103..2d9b8691bd021ad67c5172579cdb1da24a88ca85 100644 (file)
--- a/src/con.c
+++ b/src/con.c
@@ -243,15 +243,29 @@ void con_focus(Con *con) {
         workspace_update_urgent_flag(con_get_workspace(con));
         ipc_send_window_event("urgent", con);
     }
+}
 
-    /* Focusing a container with a floating parent should raise it to the top. Since
-     * con_focus is called recursively for each parent we don't need to use
-     * con_inside_floating(). */
-    if (con->type == CT_FLOATING_CON) {
-        floating_raise_con(con);
+/*
+ * Raise container to the top if it is floating or inside some floating
+ * container.
+ *
+ */
+static void con_raise(Con *con) {
+    Con *floating = con_inside_floating(con);
+    if (floating) {
+        floating_raise_con(floating);
     }
 }
 
+/*
+ * Sets input focus to the given container and raises it to the top.
+ *
+ */
+void con_activate(Con *con) {
+    con_focus(con);
+    con_raise(con);
+}
+
 /*
  * Closes the given container.
  *
@@ -994,9 +1008,9 @@ void con_enable_fullscreen(Con *con, fullscreen_mode_t fullscreen_mode) {
     Con *old_focused = focused;
     if (fullscreen_mode == CF_GLOBAL && cur_ws != con_ws)
         workspace_show(con_ws);
-    con_focus(con);
+    con_activate(con);
     if (fullscreen_mode != CF_GLOBAL && cur_ws != con_ws)
-        con_focus(old_focused);
+        con_activate(old_focused);
 
     con_set_fullscreen_mode(con, fullscreen_mode);
 }
@@ -1148,11 +1162,11 @@ static bool _con_move_to_con(Con *con, Con *target, bool behind_focused, bool fi
          * new workspace is hidden and it's necessary to immediately switch
          * back to the originally-focused workspace. */
         Con *old_focus = TAILQ_FIRST(&(output_get_content(dest_output)->focus_head));
-        con_focus(con_descend_focused(con));
+        con_activate(con_descend_focused(con));
 
         /* Restore focus if the output's focused workspace has changed. */
         if (con_get_workspace(focused) != old_focus)
-            con_focus(old_focus);
+            con_activate(old_focus);
     }
 
     /* 7: when moving to another workspace, we leave the focus on the current
@@ -1172,7 +1186,7 @@ static bool _con_move_to_con(Con *con, Con *target, bool behind_focused, bool fi
     /* Set focus only if con was on current workspace before moving.
      * Otherwise we would give focus to some window on different workspace. */
     if (!ignore_focus && source_ws == current_ws)
-        con_focus(con_descend_focused(focus_next));
+        con_activate(con_descend_focused(focus_next));
 
     /* 8. If anything within the container is associated with a startup sequence,
      * delete it so child windows won't be created on the old workspace. */
@@ -1791,7 +1805,7 @@ void con_set_layout(Con *con, layout_t layout) {
             con_attach(new, con, false);
 
             if (old_focused)
-                con_focus(old_focused);
+                con_activate(old_focused);
 
             tree_flatten(croot);
         }
@@ -2358,7 +2372,7 @@ bool con_swap(Con *first, Con *second) {
      * We don't need to check this for the second container because we've only
      * moved the first one at this point.*/
     if (first_ws != second_ws && focused_within_first) {
-        con_focus(con_descend_focused(current_ws));
+        con_activate(con_descend_focused(current_ws));
     }
 
     /* Move second to where first has been originally. */
@@ -2401,15 +2415,15 @@ bool con_swap(Con *first, Con *second) {
      */
     if (focused_within_first) {
         if (first_ws == second_ws) {
-            con_focus(old_focus);
+            con_activate(old_focus);
         } else {
-            con_focus(con_descend_focused(second));
+            con_activate(con_descend_focused(second));
         }
     } else if (focused_within_second) {
         if (first_ws == second_ws) {
-            con_focus(old_focus);
+            con_activate(old_focus);
         } else {
-            con_focus(con_descend_focused(first));
+            con_activate(con_descend_focused(first));
         }
     }
 
index 85bd8cc051370dc0f3d95f3922df50212425b1cb..e958153d6a647ab5242188d932f28e1b3ae19e98 100644 (file)
@@ -318,7 +318,7 @@ void floating_enable(Con *con, bool automatic) {
     render_con(con, false);
 
     if (set_focus)
-        con_focus(con);
+        con_activate(con);
 
     /* Check if we need to re-assign it to a different workspace because of its
      * coordinates and exit if that was done successfully. */
@@ -382,7 +382,7 @@ void floating_disable(Con *con, bool automatic) {
     con_fix_percent(con->parent);
 
     if (set_focus)
-        con_focus(con);
+        con_activate(con);
 
     floating_set_hint_atom(con, false);
     ipc_send_window_event("floating", con);
@@ -450,7 +450,7 @@ bool floating_maybe_reassign_ws(Con *con) {
     DLOG("Moving con %p / %s to workspace %p / %s\n", con, con->name, ws, ws->name);
     con_move_to_workspace(con, ws, false, true, false);
     workspace_show(ws);
-    con_focus(con_descend_focused(con));
+    con_activate(con_descend_focused(con));
     return true;
 }
 
index 0f81afae19a095233e4c568e0b8dff7667edeb8b..e1671c3b4bec2df918fdf70d89d38a29ee77c0d1 100644 (file)
@@ -433,7 +433,7 @@ static void handle_configure_request(xcb_configure_request_event_t *event) {
         if (config.focus_on_window_activation == FOWA_FOCUS || (config.focus_on_window_activation == FOWA_SMART && workspace_is_visible(ws))) {
             DLOG("Focusing con = %p\n", con);
             workspace_show(ws);
-            con_focus(con);
+            con_activate(con);
             tree_render();
         } else if (config.focus_on_window_activation == FOWA_URGENT || (config.focus_on_window_activation == FOWA_SMART && !workspace_is_visible(ws))) {
             DLOG("Marking con = %p urgent\n", con);
@@ -776,7 +776,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
                 workspace_show(ws);
                 /* Re-set focus, even if unchanged from i3’s perspective. */
                 focused_id = XCB_NONE;
-                con_focus(con);
+                con_activate(con);
             }
         } else {
             /* Request is from an application. */
@@ -788,7 +788,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
             if (config.focus_on_window_activation == FOWA_FOCUS || (config.focus_on_window_activation == FOWA_SMART && workspace_is_visible(ws))) {
                 DLOG("Focusing con = %p\n", con);
                 workspace_show(ws);
-                con_focus(con);
+                con_activate(con);
             } else if (config.focus_on_window_activation == FOWA_URGENT || (config.focus_on_window_activation == FOWA_SMART && !workspace_is_visible(ws))) {
                 DLOG("Marking con = %p urgent\n", con);
                 con_set_urgency(con, true);
@@ -1245,7 +1245,7 @@ static void handle_focus_in(xcb_focus_in_event_t *event) {
     if (ws != con_get_workspace(focused))
         workspace_show(ws);
 
-    con_focus(con);
+    con_activate(con);
     /* We update focused_id because we don’t need to set focus again */
     focused_id = event->event;
     tree_render();
index 071b3ccd650a1833fbf4efdb4d7d91fe4649dff2..aa7ac03c452f20c66505215602be441cc61ae3bd 100644 (file)
@@ -654,6 +654,6 @@ void tree_append_json(Con *con, const char *buf, const size_t len, char **errorm
     yajl_free(hand);
 
     if (to_focus) {
-        con_focus(to_focus);
+        con_activate(to_focus);
     }
 }
index b634b139f90276db0e4273d28a70922f732a63f5..194ef05c337efc76898d780eee8148f5fc9c9ca0 100644 (file)
@@ -766,7 +766,7 @@ int main(int argc, char *argv[]) {
             output = get_first_output();
         }
 
-        con_focus(con_descend_focused(output_get_content(output->con)));
+        con_activate(con_descend_focused(output_get_content(output->con)));
         free(pointerreply);
     }
 
index d12ce8d6e11c8b475bc0068738df87994f2b032b..8b306052c7ea77243e457ccf97bccbd25ac0f23c 100644 (file)
@@ -646,7 +646,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
      * proper window event sequence. */
     if (set_focus && nc->mapped) {
         DLOG("Now setting focus.\n");
-        con_focus(nc);
+        con_activate(nc);
     }
 
     tree_render();
index 3ecc69e4c762c7c63e2e38ab6a9ceadaeb91edf0..97ca6d40b42423a07582d44f3ade0f9001d7ef7f 100644 (file)
@@ -118,7 +118,7 @@ static void move_to_output_directed(Con *con, direction_t direction) {
     attach_to_workspace(con, ws, direction);
 
     /* fix the focus stack */
-    con_focus(con);
+    con_activate(con);
 
     /* force re-painting the indicators */
     FREE(con->deco_render_params);
index 1d3330145dfb7fe62c1d3db041fa9514614214cd..85add08fa41ea2cb64a60fc194a3fe65d02a1550 100644 (file)
@@ -496,7 +496,7 @@ void init_ws_for_output(Output *output, Con *content) {
     Con *ws = create_workspace_on_output(output, content);
 
     /* TODO: Set focus in main.c */
-    con_focus(ws);
+    con_activate(ws);
 }
 
 /*
@@ -924,7 +924,7 @@ void randr_query_outputs(void) {
             continue;
 
         DLOG("Focusing primary output %s\n", output_primary_name(output));
-        con_focus(con_descend_focused(output->con));
+        con_activate(con_descend_focused(output->con));
     }
 
     /* render_layout flushes */
@@ -987,7 +987,7 @@ void randr_disable_output(Output *output) {
 
         if (next) {
             DLOG("now focusing next = %p\n", next);
-            con_focus(next);
+            con_activate(next);
             workspace_show(con_get_workspace(next));
         }
 
index f07fcec6a169fb9e478d48d8161a9d3ac3bc2ea0..ee50bfbc694514abe8b43c51a5cffed65365fa69 100644 (file)
@@ -47,7 +47,7 @@ DRAGGING_CB(resize_callback) {
     xcb_flush(conn);
 }
 
-bool resize_find_tiling_participants(Con **current, Con **other, direction_t direction) {
+bool resize_find_tiling_participants(Con **current, Con **other, direction_t direction, bool both_sides) {
     DLOG("Find two participants for resizing container=%p in direction=%i\n", other, direction);
     Con *first = *current;
     Con *second = NULL;
@@ -74,8 +74,14 @@ bool resize_find_tiling_participants(Con **current, Con **other, direction_t dir
         /* get the counterpart for this resizement */
         if (dir_backwards) {
             second = TAILQ_PREV(first, nodes_head, nodes);
+            if (second == NULL && both_sides == true) {
+                second = TAILQ_NEXT(first, nodes);
+            }
         } else {
             second = TAILQ_NEXT(first, nodes);
+            if (second == NULL && both_sides == true) {
+                second = TAILQ_PREV(first, nodes_head, nodes);
+            }
         }
 
         if (second == NULL) {
index 9018ad3ffc233c0037b000eb40837f58cf8a3f41..95154014c729a655a40b4ab9be49dd42f815eedf 100644 (file)
@@ -123,7 +123,7 @@ void scratchpad_show(Con *con) {
             /* use con_descend_tiling_focused to get the last focused
                  * window inside this scratch container in order to
                  * keep the focus the same within this container */
-            con_focus(con_descend_tiling_focused(walk_con));
+            con_activate(con_descend_tiling_focused(walk_con));
             return;
         }
     }
@@ -205,7 +205,7 @@ void scratchpad_show(Con *con) {
         workspace_show(active);
     }
 
-    con_focus(con_descend_focused(con));
+    con_activate(con_descend_focused(con));
 }
 
 /*
index d1c587d5b9d7de97a4a098b9cef4882428cfc505..b8bc732bca6625f8d27f2c9624cebfa2d73eebea 100644 (file)
@@ -344,12 +344,12 @@ bool tree_close_internal(Con *con, kill_window_t kill_window, bool dont_kill_par
             DLOG("focusing %p / %s\n", next, next->name);
             if (next->type == CT_DOCKAREA) {
                 /* Instead of focusing the dockarea, we need to restore focus to the workspace */
-                con_focus(con_descend_focused(output_get_content(next->parent)));
+                con_activate(con_descend_focused(output_get_content(next->parent)));
             } else {
                 if (!force_set_focus && con != focused)
                     DLOG("not changing focus, the container was not focused before\n");
                 else
-                    con_focus(next);
+                    con_activate(next);
             }
         } else {
             DLOG("not focusing because we're not killing anybody\n");
@@ -433,7 +433,7 @@ bool level_up(void) {
     /* Skip over floating containers and go directly to the grandparent
      * (which should always be a workspace) */
     if (focused->parent->type == CT_FLOATING_CON) {
-        con_focus(focused->parent->parent);
+        con_activate(focused->parent->parent);
         return true;
     }
 
@@ -444,7 +444,7 @@ bool level_up(void) {
         ELOG("'focus parent': Focus is already on the workspace, cannot go higher than that.\n");
         return false;
     }
-    con_focus(focused->parent);
+    con_activate(focused->parent);
     return true;
 }
 
@@ -469,7 +469,7 @@ bool level_down(void) {
             next = TAILQ_FIRST(&(next->focus_head));
     }
 
-    con_focus(next);
+    con_activate(next);
     return true;
 }
 
@@ -566,7 +566,7 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
         }
 
         workspace_show(workspace);
-        con_focus(focus);
+        con_activate(focus);
         x_set_warp_to(&(focus->rect));
         return true;
     }
@@ -604,7 +604,7 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
             TAILQ_INSERT_HEAD(&(parent->floating_head), last, floating_windows);
         }
 
-        con_focus(con_descend_focused(next));
+        con_activate(con_descend_focused(next));
         return true;
     }
 
@@ -653,7 +653,7 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
     /* 3: focus choice comes in here. at the moment we will go down
      * until we find a window */
     /* TODO: check for window, atm we only go down as far as possible */
-    con_focus(con_descend_focused(next));
+    con_activate(con_descend_focused(next));
     return true;
 }
 
index 1531335765dff19438ac1ba3857a3f4d84d13b65..d200b6e43fd391d0244ba2aff6d05af01b6fd911 100644 (file)
@@ -412,7 +412,7 @@ static void _workspace_show(Con *workspace) {
     if (next->urgent && (int)(config.workspace_urgency_timer * 1000) > 0) {
         /* focus for now… */
         next->urgent = false;
-        con_focus(next);
+        con_activate(next);
 
         /* … but immediately reset urgency flags; they will be set to false by
          * the timer callback in case the container is focused at the time of
@@ -435,7 +435,7 @@ static void _workspace_show(Con *workspace) {
             ev_timer_again(main_loop, focused->urgency_timer);
         }
     } else
-        con_focus(next);
+        con_activate(next);
 
     ipc_send_workspace_event("focus", workspace, current);
 
@@ -837,7 +837,7 @@ void ws_force_orientation(Con *ws, orientation_t orientation) {
     con_fix_percent(ws);
 
     if (old_focused)
-        con_focus(old_focused);
+        con_activate(old_focused);
 }
 
 /*
index 9c1507e679e21217e92abab9a864b2f3ce95289b..1e2644ad96ed334437042825293b50df54660753 100644 (file)
@@ -304,7 +304,7 @@ for ($type = 1; $type <= 2; $type++) {
     cmd 'move right';
     cmd '[id="' . $w3->id . '"] focus';
     sync_with_i3;
-    my $ws = get_ws($tmp);
+    $ws = get_ws($tmp);
     ok(!$ws->{urgent}, 'urgent flag not set on workspace');
 
 ##############################################################################
index cfa2405e4b3e124021fcba5f28550c5aba18d74e..9c396f40fadfc0a438e5a9c13f3978bb77d0bb8f 100644 (file)
@@ -340,8 +340,8 @@ is_num_fullscreen($tmp, 0, 'no fullscreen windows');
 kill_all_windows;
 
 $tmp = fresh_workspace;
-my $first = open_floating_window;
-my $second = open_window;
+$first = open_floating_window;
+$second = open_window;
 cmd 'fullscreen';
 is($x->input_focus, $second->id, 'fullscreen window focused');
 is_num_fullscreen($tmp, 1, '1 fullscreen window');
@@ -356,8 +356,8 @@ is_num_fullscreen($tmp, 0, 'no fullscreen windows');
 kill_all_windows;
 
 $tmp = fresh_workspace;
-my $first = open_window;
-my $second = open_floating_window;
+$first = open_window;
+$second = open_floating_window;
 cmd 'fullscreen';
 is($x->input_focus, $second->id, 'fullscreen window focused');
 is_num_fullscreen($tmp, 1, '1 fullscreen window');
@@ -376,10 +376,10 @@ is_num_fullscreen($tmp, 0, 'no fullscreen windows');
 kill_all_windows;
 
 $tmp = fresh_workspace;
-my $first = open_window;
+$first = open_window;
 
 $tmp2 = fresh_workspace;
-my $second = open_window;
+$second = open_window;
 cmd 'fullscreen';
 is($x->input_focus, $second->id, 'fullscreen window focused');
 is_num_fullscreen($tmp2, 1, '1 fullscreen window');
index f94bd75b61c6a85c658c8812fec0765652707c61..147890e1f9a20742842ebf5d16e4f37ca24315cc 100644 (file)
@@ -429,7 +429,7 @@ does_i3_live;
 ################################################################################
 
 clear_scratchpad;
-my $ws = fresh_workspace;
+$ws = fresh_workspace;
 
 open_window;
 my $scratch = get_focused($ws);
index ea4c08de0ddfdcaf33d865366a516cc961958e12..6b082bfdcb1bab61bbe2a108fd8bc9e47ccb3272 100644 (file)
@@ -190,7 +190,7 @@ exit_gracefully($pid);
 # 7: check floating_maximum_size with cmd_size
 ################################################################################
 
-my $config = <<EOT;
+$config = <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
@@ -201,12 +201,12 @@ EOT
 
 $pid = launch_with_config($config);
 
-my $window = open_floating_window(rect => [ 0, 0, 90, 80 ]);
+$window = open_floating_window(rect => [ 0, 0, 90, 80 ]);
 cmd 'border none';
 
 cmd 'resize set 101 91';
 sync_with_i3;
-my $rect = $window->rect;
+$rect = $window->rect;
 is($rect->{width}, 100, 'width did not exceed maximum width');
 is($rect->{height}, 90, 'height did not exceed maximum height');
 
index aee1e03a69d3288e7d2facaa0967923b6cfefb43..f9f883cbd7756a45bd48d85440a216afb8ebe222 100644 (file)
@@ -24,7 +24,7 @@ use i3test;
 sub send_net_active_window {
     my ($id, $source) = @_;
 
-    $source = ($source eq 'pager' ? 2 : 0);
+    $source = (((defined $source) && ($source eq 'pager')) ? 2 : 0);
 
     my $msg = pack "CCSLLLLLLL",
         X11::XCB::CLIENT_MESSAGE, # response_type
@@ -137,7 +137,7 @@ is($x->input_focus, $win3->id, 'window 3 still focused');
 # is received.
 ################################################################################
 
-my $scratch = open_window;
+$scratch = open_window;
 
 is($x->input_focus, $scratch->id, 'to-scratchpad window has focus');
 
index cd3989c4257a89fcf88491ba3cf183ef4ffc11ed..57060753978ac6f5c99085109e43b2c00a96df43 100644 (file)
@@ -111,7 +111,7 @@ EOT
 
 $pid = launch_with_config($config);
 
-my $ws = fresh_workspace;
+$ws = fresh_workspace;
 $first = open_window;
 $second = open_window;
 
@@ -165,7 +165,7 @@ EOT
 
 $pid = launch_with_config($config);
 
-my $ws = fresh_workspace;
+$ws = fresh_workspace;
 $first = open_window;
 $second = open_window;
 
index ac0c48d03d81a8ba31209197df47dd9c601426c0..2c8edf397084f2226a62ce56c4da6a8ffb17e3da 100644 (file)
@@ -79,8 +79,8 @@ cmp_ok($content[0]->{rect}->{height}, '==', $expected_height, "height changed to
 ################################################################################
 
 cmd 'resize set 44 ppt 111 px';
-my $expected_width = int(0.44 * 1333);
-my $expected_height = 111;
+$expected_width = int(0.44 * 1333);
+$expected_height = 111;
 
 @content = @{get_ws($tmp)->{floating_nodes}};
 cmp_ok($content[0]->{rect}->{x}, '==', $oldrect->{x}, 'x untouched');
@@ -89,8 +89,8 @@ cmp_ok($content[0]->{rect}->{width}, '==', $expected_width, "width changed to $e
 cmp_ok($content[0]->{rect}->{height}, '==', $expected_height, "height changed to $expected_height px");
 
 cmd 'resize set 222 px 100 ppt';
-my $expected_width = 222;
-my $expected_height = 999;
+$expected_width = 222;
+$expected_height = 999;
 
 @content = @{get_ws($tmp)->{floating_nodes}};
 cmp_ok($content[0]->{rect}->{x}, '==', $oldrect->{x}, 'x untouched');
diff --git a/testcases/t/293-focus-follows-mouse.t b/testcases/t/293-focus-follows-mouse.t
new file mode 100644 (file)
index 0000000..0cd6e5c
--- /dev/null
@@ -0,0 +1,88 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • https://build.i3wm.org/docs/testsuite.html
+#   (or docs/testsuite)
+#
+# • https://build.i3wm.org/docs/lib-i3test.html
+#   (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • https://build.i3wm.org/docs/ipc.html
+#   (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+#   (unless you are already familiar with Perl)
+#
+# Tests the focus_follows_mouse setting.
+use i3test i3_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+fake-outputs 1000x1000+0+0
+EOT
+
+my ($first, $second);
+
+sub synced_warp_pointer {
+    my ($x_px, $y_px) = @_;
+    sync_with_i3;
+    $x->root->warp_pointer($x_px, $y_px);
+    sync_with_i3;
+}
+
+###################################################################
+# Test a simple case with 2 windows.
+###################################################################
+
+synced_warp_pointer(600, 600);
+$first = open_window;
+$second = open_window;
+is($x->input_focus, $second->id, 'second window focused');
+
+synced_warp_pointer(0, 0);
+is($x->input_focus, $first->id, 'first window focused');
+
+###################################################################
+# Test that focus isn't changed with tabbed windows.
+###################################################################
+
+fresh_workspace;
+synced_warp_pointer(600, 600);
+$first = open_window;
+cmd 'layout tabbed';
+$second = open_window;
+is($x->input_focus, $second->id, 'second (tabbed) window focused');
+
+synced_warp_pointer(0, 0);
+is($x->input_focus, $second->id, 'second window still focused');
+
+###################################################################
+# Test that floating windows are focused but not raised to the top.
+# See issue #2990.
+###################################################################
+
+my $ws;
+my $tmp = fresh_workspace;
+my ($first_floating, $second_floating);
+
+synced_warp_pointer(0, 0);
+$first_floating = open_floating_window;
+$first_floating->rect(X11::XCB::Rect->new(x => 1, y => 1, width => 100, height => 100));
+$second_floating = open_floating_window;
+$second_floating->rect(X11::XCB::Rect->new(x => 50, y => 50, width => 100, height => 100));
+sync_with_i3;
+$first = open_window;
+
+is($x->input_focus, $first->id, 'first (tiling) window focused');
+$ws = get_ws($tmp);
+is($ws->{floating_nodes}->[1]->{nodes}->[0]->{window}, $second_floating->id, 'second floating on top');
+is($ws->{floating_nodes}->[0]->{nodes}->[0]->{window}, $first_floating->id, 'first floating behind');
+
+synced_warp_pointer(40, 40);
+is($x->input_focus, $first_floating->id, 'first floating window focused');
+$ws = get_ws($tmp);
+is($ws->{floating_nodes}->[1]->{nodes}->[0]->{window}, $second_floating->id, 'second floating still on top');
+is($ws->{floating_nodes}->[0]->{nodes}->[0]->{window}, $first_floating->id, 'first floating still behind');
+
+done_testing;
index 86bad5e505a1b2bb8c6f68834c2cd17f52d8310b..4ec33f6632e0de0bde9b3221a17cef30a1cc09a3 100644 (file)
@@ -187,10 +187,10 @@ ok($ws1 ~~ @$x0, 'ws1 on fake-0');
 my $__i3_scratch = get_ws('__i3_scratch');
 is(scalar @{$__i3_scratch->{floating_nodes}}, 0, 'scratchpad is empty');
 
-my $ws0 = fresh_workspace(output => 0);
+$ws0 = fresh_workspace(output => 0);
 open_window(wm_class => 'a');
 
-my $ws1 = fresh_workspace(output => 1);
+$ws1 = fresh_workspace(output => 1);
 open_window(wm_class => 'b');
 my $scratchpad_window = open_window(wm_class => 'c');
 cmd 'move to scratchpad';
diff --git a/testcases/t/541-resize-set-tiling.t b/testcases/t/541-resize-set-tiling.t
new file mode 100644 (file)
index 0000000..82267ba
--- /dev/null
@@ -0,0 +1,147 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • https://build.i3wm.org/docs/testsuite.html
+#   (or docs/testsuite)
+#
+# • https://build.i3wm.org/docs/lib-i3test.html
+#   (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • https://build.i3wm.org/docs/ipc.html
+#   (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+#   (unless you are already familiar with Perl)
+#
+# Tests resizing tiling containers
+use i3test;
+
+############################################################
+# resize horizontally
+############################################################
+
+my $tmp = fresh_workspace;
+
+cmd 'split h';
+
+my $left = open_window;
+my $right = open_window;
+
+diag("left = " . $left->id . ", right = " . $right->id);
+
+is($x->input_focus, $right->id, 'Right window focused');
+
+cmd 'resize set 75 ppt 0 ppt';
+
+my ($nodes, $focus) = get_ws_content($tmp);
+
+cmp_float($nodes->[0]->{percent}, 0.25, 'left window got only 25%');
+cmp_float($nodes->[1]->{percent}, 0.75, 'right window got 75%');
+
+############################################################
+# resize vertically
+############################################################
+
+my $tmp = fresh_workspace;
+
+cmd 'split v';
+
+my $top = open_window;
+my $bottom = open_window;
+
+diag("top = " . $top->id . ", bottom = " . $bottom->id);
+
+is($x->input_focus, $bottom->id, 'Bottom window focused');
+
+cmd 'resize set 0 ppt 75 ppt';
+
+my ($nodes, $focus) = get_ws_content($tmp);
+
+cmp_float($nodes->[0]->{percent}, 0.25, 'top window got only 25%');
+cmp_float($nodes->[1]->{percent}, 0.75, 'bottom window got 75%');
+
+
+############################################################
+# resize horizontally and vertically
+############################################################
+
+my $tmp = fresh_workspace;
+
+cmd 'split h';
+my $left = open_window;
+my $top_right = open_window;
+cmd 'split v';
+my $bottom_right = open_window;
+
+diag("left = " . $left->id . ", top-right = " . $top_right->id . ", bottom-right = " . $bottom_right->id);
+
+is($x->input_focus, $bottom_right->id, 'Bottom-right window focused');
+
+cmd 'resize set 75 ppt 75 ppt';
+
+my ($nodes, $focus) = get_ws_content($tmp);
+
+cmp_float($nodes->[0]->{percent}, 0.25, 'left container got 25%');
+cmp_float($nodes->[1]->{percent}, 0.75, 'right container got 75%');
+cmp_float($nodes->[1]->{nodes}->[0]->{percent}, 0.25, 'top-right window got 25%');
+cmp_float($nodes->[1]->{nodes}->[1]->{percent}, 0.75, 'bottom-right window got 75%');
+
+
+############################################################
+# resize from inside a tabbed container
+############################################################
+
+my $tmp = fresh_workspace;
+
+cmd 'split h';
+
+my $left = open_window;
+my $right1 = open_window;
+
+cmd 'split h';
+cmd 'layout tabbed';
+
+my $right2 = open_window;
+
+diag("left = " . $left->id . ", right1 = " . $right1->id . ", right2 = " . $right2->id);
+
+is($x->input_focus, $right2->id, '2nd right window focused');
+
+cmd 'resize set 75 ppt 0 ppt';
+
+my ($nodes, $focus) = get_ws_content($tmp);
+
+cmp_float($nodes->[0]->{percent}, 0.25, 'left container got 25%');
+cmp_float($nodes->[1]->{percent}, 0.75, 'right container got 75%');
+
+
+############################################################
+# resize from inside a stacked container
+############################################################
+
+my $tmp = fresh_workspace;
+
+cmd 'split h';
+
+my $left = open_window;
+my $right1 = open_window;
+
+cmd 'split h';
+cmd 'layout stacked';
+
+my $right2 = open_window;
+
+diag("left = " . $left->id . ", right1 = " . $right1->id . ", right2 = " . $right2->id);
+
+is($x->input_focus, $right2->id, '2nd right window focused');
+
+cmd 'resize set 75 ppt 0 ppt';
+
+my ($nodes, $focus) = get_ws_content($tmp);
+
+cmp_float($nodes->[0]->{percent}, 0.25, 'left container got 25%');
+cmp_float($nodes->[1]->{percent}, 0.75, 'right container got 75%');
+
+
+done_testing;