</p>
</header>
+
<section>
<h2>Basics</h2>
<table>
<tr>
- <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd></kbd>
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd>
<td>open new terminal
-
<tr>
- <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>j</kbd>
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>j</kbd>
<td>focus left
<tr>
- <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>k</kbd>
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>k</kbd>
<td>focus down
<tr>
- <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>l</kbd>
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>l</kbd>
<td>focus up
<tr>
- <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>;</kbd>
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>;</kbd>
<td>focus right
+ <tr>
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd>
+ <td>toggle focus mode
</table>
</section>
-
<section>
- <h2>Changing the container layout</h2>
+ <h2>Moving windows</h2>
<table>
<tr>
- <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>e</kbd>
- <td>default
-
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>j</kbd>
+ <td>move window left
<tr>
- <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>s</kbd>
- <td>stacking
-
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>k</kbd>
+ <td>move window down
<tr>
- <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>w</kbd>
- <td>tabbed
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>l</kbd>
+ <td>move window up
+ <tr>
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>;</kbd>
+ <td>move window right
</table>
</section>
</div><div>
<section>
- <h2>Fullscreen mode</h2>
+ <h2>Modifying windows</h2>
<table>
<tr>
- <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>f</kbd>
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>f</kbd>
<td>toggle fullscreen
+ <tr>
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>v</kbd>
+ <td>split a window vertically
+ <tr>
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>h</kbd>
+ <td>split a window horizontally
+ <tr>
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>r</kbd>
+ <td>resize mode
</table>
+ <p class="ref">Look at the “Resizing containers / windows” section of the user guide.</p>
</section>
-
<section>
- <h2>Opening other applications</h2>
+ <h2>Changing the container layout</h2>
<table>
<tr>
- <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>d</kbd>
- <td>open application (with dmenu)
- </table>
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>e</kbd>
+ <td>default
+ <tr>
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>s</kbd>
+ <td>stacking
- <section>
- <h2>Closing windows</h2>
- <table>
<tr>
- <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd> </kbd>+ <kbd>q</kbd>
- <td>kill a window
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>w</kbd>
+ <td>tabbed
</table>
</section>
-
<section>
- <h2>Using workspaces</h2>
+ <h2>Floating</h2>
<table>
<tr>
- <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>1</kbd>–<kbd>9</kbd>
- <td>switch to another workspace
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd></kbd>
+ <td>toggle floating
+ <tr>
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd>
+ <td>drag floating
</table>
</section>
<section>
- <h2>Moving windows to workspaces</h2>
+ <h2>Using workspaces</h2>
<table>
<tr>
- <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd></kbd> + <kbd>1</kbd>–<kbd>9</kbd>
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>0</kbd>-<kbd>9</kbd>
+ <td>switch to another workspace
+ <tr>
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>0</kbd>-<kbd>9</kbd>
<td>move a window to another workspace
</table>
</section>
</div><div>
<section>
- <h2>Resizing</h2>
- <p class="ref">Look at “Resizing containers / windows” section of the user guide.</p>
- </section>
-
-
- <section>
- <h2>Restart / Exit</h2>
+ <h2>Opening applications / Closing windows</h2>
<table>
<tr>
- <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd></kbd> + <kbd>r</kbd>
- <td>restart i3 inplace
-
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>d</kbd>
+ <td>open application launcher (dmenu)
<tr>
- <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd></kbd> + <kbd>e</kbd>
-
- </section><td>exit i3
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>q</kbd>
+ <td>kill a window
</table>
-
+ </section>
<section>
- <h2>Floating</h2>
+ <h2>Restart / Exit</h2>
<table>
<tr>
- <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd></kbd> + <kbd></kbd>
- <td>toggle floating
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>c</kbd>
+ <td>reload the configuration file
+ <tr>
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>r</kbd>
+ <td>restart i3 inplace
<tr>
- <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd></kbd>
- <td>drag floating
- </table>
+ <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>e</kbd>
+ <td>exit i3
</section>
+ </table>
+
<!-- footer -->
<p id="copyright">
<br />
All rights reserved
<br />
- Designed by Zeus Panchenko
+ Designed by Zeus Panchenko, updated by Moritz Bandemer
</p>
<p id="licence">
Permission is granted to copy, distribute and/or modify this document provided
workspace 1, 3, 4 and 9 and you want to cycle through them with a single key
combination. To restrict those to the current output, use +workspace
next_on_output+ and +workspace prev_on_output+. Similarly, you can use +move
-container to workspace next+ and +move container to workspace prev+ to move a
-container to the next/previous workspace.
+container to workspace next+, +move container to workspace prev+ to move a
+container to the next/previous workspace and +move container to workspace current+
+(the last one makes sense only when used with criteria).
[[back_and_forth]]
To switch back to the previously focused workspace, use +workspace
target output. You may also use +left+, +right+, +up+, +down+ instead of the
xrandr output name to move to the next output in the specified direction.
+*Syntax*:
+-----------------------------------
+workspace <next|prev|next_on_output|prev_on_output>
+workspace back_and_forth
+workspace <name>
+workspace number <number>
+
+move [window|container] [to] workspace <name>
+move [window|container] [to] workspace number <number>
+move [window|container] [to] workspace <prev|next|current>
+-----------------------------------
+
*Examples*:
-------------------------
bindsym mod+1 workspace 1
# move the whole workspace to the next output
bindsym mod+x move workspace to output right
+
+# move firefox to current workspace
+bindsym mod+F1 [class="Firefox"] move workspace current
-------------------------
==== Named workspaces
*/
Rect con_minimum_size(Con *con);
+/**
+ * Returns true if changing the focus to con would be allowed considering
+ * the fullscreen focus constraints. Specifically, if a fullscreen container or
+ * any of its descendants is focused, this function returns true if and only if
+ * focusing con would mean that focus would still be visible on screen, i.e.,
+ * the newly focused container would not be obscured by a fullscreen container.
+ *
+ * In the simplest case, if a fullscreen container or any of its descendants is
+ * fullscreen, this functions returns true if con is the fullscreen container
+ * itself or any of its descendants, as this means focus wouldn't escape the
+ * boundaries of the fullscreen container.
+ *
+ * In case the fullscreen container is of type CF_OUTPUT, this function returns
+ * true if con is on a different workspace, as focus wouldn't be obscured by
+ * the fullscreen container that is constrained to a different workspace.
+ *
+ * Note that this same logic can be applied to moving containers. If a
+ * container can be focused under the fullscreen focus constraints, it can also
+ * become a parent or sibling to the currently focused container.
+ *
+ */
+bool con_fullscreen_permits_focusing(Con *con);
+
#endif
void tree_split(Con *con, orientation_t orientation);
/**
- * Moves focus one level up.
+ * Moves focus one level up. Returns true if focus changed.
*
*/
-void level_up(void);
+bool level_up(void);
/**
- * Moves focus one level down.
+ * Moves focus one level down. Returns true if focus changed.
*
*/
-void level_down(void);
+bool level_down(void);
/**
* Renders the tree, that is rendering all outputs using render_con() and
-> call cmd_rename_workspace($old_name, $new_name)
# move <direction> [<pixels> [px]]
-# move [window|container] [to] workspace <str>
+# move [window|container] [to] workspace [<str>|next|prev|current]
# move [window|container] [to] output <str>
# move [window|container] [to] scratchpad
# move workspace to [output] <str>
state MOVE_WORKSPACE:
'to'
-> MOVE_WORKSPACE_TO_OUTPUT
- workspace = 'next', 'prev', 'next_on_output', 'prev_on_output'
+ workspace = 'next', 'prev', 'next_on_output', 'prev_on_output', 'current'
-> call cmd_move_con_to_workspace($workspace)
'number'
-> MOVE_WORKSPACE_NUMBER
} while (0)
/** When the command did not include match criteria (!), we use the currently
- * focused command. Do not confuse this case with a command which included
+ * focused container. Do not confuse this case with a command which included
* criteria but which did not match any windows. This macro has to be called in
* every command.
*/
/*
* Implementation of 'move [window|container] [to] workspace
- * next|prev|next_on_output|prev_on_output'.
+ * next|prev|next_on_output|prev_on_output|current'.
*
*/
void cmd_move_con_to_workspace(I3_CMD, char *which) {
DLOG("which=%s\n", which);
+ /* We have nothing to move:
+ * when criteria was specified but didn't match any window or
+ * when criteria wasn't specified and we don't have any window focused. */
+ if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
+ (match_is_empty(current_match) && focused->type == CT_WORKSPACE)) {
+ ysuccess(false);
+ return;
+ }
+
HANDLE_EMPTY_MATCH;
/* get the workspace */
ws = workspace_next_on_output();
else if (strcmp(which, "prev_on_output") == 0)
ws = workspace_prev_on_output();
+ else if (strcmp(which, "current") == 0)
+ ws = con_get_workspace(focused);
else {
ELOG("BUG: called with which=%s\n", which);
ysuccess(false);
owindow *current;
- /* Error out early to not create a non-existing workspace (in
- * workspace_get()) if we are not actually able to move anything. */
- if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) {
+ /* We have nothing to move:
+ * when criteria was specified but didn't match any window or
+ * when criteria wasn't specified and we don't have any window focused. */
+ if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
+ (match_is_empty(current_match) && focused->type == CT_WORKSPACE)) {
ysuccess(false);
return;
}
void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
owindow *current;
- /* Error out early to not create a non-existing workspace (in
- * workspace_get()) if we are not actually able to move anything. */
- if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) {
+ /* We have nothing to move:
+ * when criteria was specified but didn't match any window or
+ * when criteria wasn't specified and we don't have any window focused. */
+ if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
+ (match_is_empty(current_match) && focused->type == CT_WORKSPACE)) {
ysuccess(false);
return;
}
*
*/
void cmd_focus_level(I3_CMD, char *level) {
- if (focused &&
- focused->type != CT_WORKSPACE &&
- focused->fullscreen_mode != CF_NONE) {
- LOG("Cannot change focus while in fullscreen mode.\n");
- ysuccess(false);
- return;
- }
-
DLOG("level = %s\n", level);
+ bool success = false;
+
+ /* Focusing the parent can only be allowed if the newly
+ * focused container won't escape the fullscreen container. */
+ if (strcmp(level, "parent") == 0) {
+ if (focused && focused->parent) {
+ if (con_fullscreen_permits_focusing(focused->parent))
+ success = level_up();
+ else
+ LOG("Currently in fullscreen, not going up\n");
+ }
+ }
- if (strcmp(level, "parent") == 0)
- level_up();
- else level_down();
+ /* Focusing a child should always be allowed. */
+ else success = level_down();
- cmd_output->needs_tree_render = true;
+ cmd_output->needs_tree_render = success;
// XXX: default reply for now, make this a better reply
- ysuccess(true);
+ ysuccess(success);
}
/*
if (!ws)
continue;
- /* Don't allow the focus switch if the focused and current
- * containers are in the same workspace. */
- if (focused &&
- focused->type != CT_WORKSPACE &&
- focused->fullscreen_mode != CF_NONE &&
- con_get_workspace(focused) == ws) {
- LOG("Cannot change focus while in fullscreen mode (same workspace).\n");
+ /* Check the fullscreen focus constraints. */
+ if (!con_fullscreen_permits_focusing(current->con)) {
+ LOG("Cannot change focus while in fullscreen mode (fullscreen rules).\n");
ysuccess(false);
return;
}
return;
}
+ /* Prevent moving if this would violate the fullscreen focus restrictions. */
+ if (!con_fullscreen_permits_focusing(workspace)) {
+ LOG("Cannot move out of a fullscreen container");
+ return;
+ }
+
if (con_is_floating(con)) {
DLOG("Using FLOATINGCON instead\n");
con = con->parent;
}
+ Con *source_ws = con_get_workspace(con);
+ if (workspace == source_ws) {
+ DLOG("Not moving, already there\n");
+ return;
+ }
+
+ /* Save the current workspace. So we can call workspace_show() by the end
+ * of this function. */
+ Con *current_ws = con_get_workspace(focused);
+
Con *source_output = con_get_output(con),
*dest_output = con_get_output(workspace);
/* 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));
+ workspace_show(current_ws);
+
+ /* Set focus only if con was on current workspace before moving.
+ * Otherwise we would give focus to some window on different workspace. */
+ if (source_ws == current_ws)
+ con_focus(con_descend_focused(focus_next));
}
CALL(parent, on_remove_child);
con->type, con->layout, con->orientation);
assert(false);
}
+
+/*
+ * Returns true if changing the focus to con would be allowed considering
+ * the fullscreen focus constraints. Specifically, if a fullscreen container or
+ * any of its descendants is focused, this function returns true if and only if
+ * focusing con would mean that focus would still be visible on screen, i.e.,
+ * the newly focused container would not be obscured by a fullscreen container.
+ *
+ * In the simplest case, if a fullscreen container or any of its descendants is
+ * fullscreen, this functions returns true if con is the fullscreen container
+ * itself or any of its descendants, as this means focus wouldn't escape the
+ * boundaries of the fullscreen container.
+ *
+ * In case the fullscreen container is of type CF_OUTPUT, this function returns
+ * true if con is on a different workspace, as focus wouldn't be obscured by
+ * the fullscreen container that is constrained to a different workspace.
+ *
+ * Note that this same logic can be applied to moving containers. If a
+ * container can be focused under the fullscreen focus constraints, it can also
+ * become a parent or sibling to the currently focused container.
+ *
+ */
+bool con_fullscreen_permits_focusing(Con *con) {
+ /* No focus, no problem. */
+ if (!focused)
+ return true;
+
+ /* Find the first fullscreen ascendent. */
+ Con *fs = focused;
+ while (fs && fs->fullscreen_mode == CF_NONE)
+ fs = fs->parent;
+
+ /* The most common case is we hit the workspace level. In this
+ * situation, changing focus is also harmless. */
+ assert(fs->fullscreen_mode != CF_NONE);
+ if (fs->type == CT_WORKSPACE)
+ return true;
+
+ /* Allow it if the container itself is the fullscreen container. */
+ if (con == fs)
+ return true;
+
+ /* If fullscreen is per-output, the focus being in a different workspace is
+ * sufficient to guarantee that change won't leave fullscreen in bad shape. */
+ if (fs->fullscreen_mode == CF_OUTPUT &&
+ con_get_workspace(con) != con_get_workspace(fs)) {
+ return true;
+ }
+
+ /* Allow it only if the container to be focused is contained within the
+ * current fullscreen container. */
+ do {
+ if (con->parent == fs)
+ return true;
+ con = con->parent;
+ } while (con);
+
+ /* Focusing con would hide it behind a fullscreen window, disallow it. */
+ return false;
+}
uint32_t rel_y = (con->rect.y - old_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 / old_rect->width);
- double fraction_y = ((double)rel_y / old_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, old_rect->width, old_rect->height);
- con->rect.x = new_rect->x + (fraction_x * new_rect->width);
- con->rect.y = new_rect->y + (fraction_y * new_rect->height);
+ 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 = new_rect->x + (double)(rel_x * new_rect->width) / old_rect->width;
+ con->rect.y = new_rect->y + (double)(rel_y * new_rect->height) / old_rect->height;
DLOG("Resulting coordinates: x = %d, y = %d\n", con->rect.x, con->rect.y);
}
while (above->parent != same_orientation)
above = above->parent;
+ /* Enforce the fullscreen focus restrictions. */
+ if (!con_fullscreen_permits_focusing(above->parent)) {
+ LOG("Cannot move out of fullscreen container\n");
+ return;
+ }
+
DLOG("above = %p\n", above);
Con *next;
position_t position;
}
/*
- * Moves focus one level up.
+ * Moves focus one level up. Returns true if focus changed.
*
*/
-void level_up(void) {
- /* We cannot go up when we are in fullscreen mode at the moment, that would
- * be totally not intuitive */
- if (focused->fullscreen_mode != CF_NONE) {
- LOG("Currently in fullscreen, not going up\n");
- return;
- }
+bool level_up(void) {
/* We can focus up to the workspace, but not any higher in the tree */
if ((focused->parent->type != CT_CON &&
focused->parent->type != CT_WORKSPACE) ||
focused->type == CT_WORKSPACE) {
LOG("Cannot go up any further\n");
- return;
+ return false;
}
con_focus(focused->parent);
+ return true;
}
/*
- * Moves focus one level down.
+ * Moves focus one level down. Returns true if focus changed.
*
*/
-void level_down(void) {
+bool level_down(void) {
/* Go down the focus stack of the current node */
Con *next = TAILQ_FIRST(&(focused->focus_head));
if (next == TAILQ_END(&(focused->focus_head))) {
printf("cannot go down\n");
- return;
+ return false;
}
con_focus(next);
+ return true;
}
static void mark_unmapped(Con *con) {
else next = TAILQ_LAST(&(parent->nodes_head), nodes_head);
}
+ /* Don't violate fullscreen focus restrictions. */
+ if (!con_fullscreen_permits_focusing(next))
+ return false;
+
/* 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 */
/* We check if this is the workspace
* next/prev/next_on_output/prev_on_output/back_and_forth/number command.
* Beware: The workspace names "next", "prev", "next_on_output",
- * "prev_on_output", "number" and "back_and_forth" are OK, so we check
- * before stripping the double quotes */
+ * "prev_on_output", "number", "back_and_forth" and "current" are OK,
+ * so we check before stripping the double quotes */
if (strncasecmp(target, "next", strlen("next")) == 0 ||
strncasecmp(target, "prev", strlen("prev")) == 0 ||
strncasecmp(target, "next_on_output", strlen("next_on_output")) == 0 ||
strncasecmp(target, "prev_on_output", strlen("prev_on_output")) == 0 ||
strncasecmp(target, "number", strlen("number")) == 0 ||
- strncasecmp(target, "back_and_forth", strlen("back_and_forth")) == 0)
+ strncasecmp(target, "back_and_forth", strlen("back_and_forth")) == 0 ||
+ strncasecmp(target, "current", strlen("current")) == 0)
continue;
if (*target == '"')
target++;
open_floating_window
get_dock_clients
cmd
+ cmp_float
sync_with_i3
does_i3_live
exit_gracefully
return $i3_pid;
}
+# compares two floats and return true if they differ less
+# then 1e-6
+sub cmp_float {
+ my ($a, $b) = @_;
+
+ return abs($a - $b) < 1e-6;
+}
+
package i3test::X11;
use parent 'X11::XCB::Connection';
ok(@{get_ws_content($tmp)} == 3, 'three containers on first ws');
ok(@{get_ws_content($tmp2)} == 0, 'no containers on second ws');
+###################################################################
+# check if 'move workspace current' works
+###################################################################
+
+$tmp = get_unused_workspace();
+$tmp2 = get_unused_workspace();
+
+cmd "workspace $tmp";
+$first = open_window(name => 'win-name');
+ok(@{get_ws_content($tmp)} == 1, 'one container on first ws');
+
+cmd "workspace $tmp2";
+ok(@{get_ws_content($tmp2)} == 0, 'no containers yet');
+
+cmd qq|[title="win-name"] move workspace $tmp2|;
+ok(@{get_ws_content($tmp2)} == 1, 'one container on second ws');
+
+cmd qq|[title="win-name"] move workspace $tmp|;
+ok(@{get_ws_content($tmp2)} == 0, 'no containers on second ws');
+
###################################################################
# check if floating cons are moved to new workspaces properly
# (that is, if they are floating on the target ws, too)
my ($nodes, $focus) = get_ws_content($tmp);
-is($nodes->[0]->{percent}, 0.25, 'top window got only 25%');
-is($nodes->[1]->{percent}, 0.75, 'bottom window got 75%');
+ok(cmp_float($nodes->[0]->{percent}, 0.25), 'top window got only 25%');
+ok(cmp_float($nodes->[1]->{percent}, 0.75), 'bottom window got 75%');
############################################################
($nodes, $focus) = get_ws_content($tmp);
-is($nodes->[0]->{percent}, 0.25, 'top window got only 25%');
-is($nodes->[1]->{percent}, 0.75, 'bottom window got 75%');
+ok(cmp_float($nodes->[0]->{percent}, 0.25), 'top window got only 25%');
+ok(cmp_float($nodes->[1]->{percent}, 0.75), 'bottom window got 75%');
############################################################
# checks that resizing within stacked/tabbed cons works
cmd 'layout stacked';
($nodes, $focus) = get_ws_content($tmp);
-is($nodes->[0]->{percent}, 0.5, 'top window got 50%');
-is($nodes->[1]->{percent}, 0.5, 'bottom window got 50%');
+ok(cmp_float($nodes->[0]->{percent}, 0.5), 'top window got 50%');
+ok(cmp_float($nodes->[1]->{percent}, 0.5), 'bottom window got 50%');
cmd 'resize grow up 10 px or 25 ppt';
($nodes, $focus) = get_ws_content($tmp);
-is($nodes->[0]->{percent}, 0.25, 'top window got 25%');
-is($nodes->[1]->{percent}, 0.75, 'bottom window got 75%');
+ok(cmp_float($nodes->[0]->{percent}, 0.25), 'top window got 25%');
+ok(cmp_float($nodes->[1]->{percent}, 0.75), 'bottom window got 75%');
############################################################
# Checks that resizing in the parent's parent's orientation works.
$bottom = open_window;
($nodes, $focus) = get_ws_content($tmp);
-is($nodes->[0]->{percent}, 0.5, 'left window got 50%');
-is($nodes->[1]->{percent}, 0.5, 'right window got 50%');
+ok(cmp_float($nodes->[0]->{percent}, 0.5), 'left window got 50%');
+ok(cmp_float($nodes->[1]->{percent}, 0.5), 'right window got 50%');
cmd 'resize grow left 10 px or 25 ppt';
($nodes, $focus) = get_ws_content($tmp);
-is($nodes->[0]->{percent}, 0.25, 'left window got 25%');
-is($nodes->[1]->{percent}, 0.75, 'right window got 75%');
+ok(cmp_float($nodes->[0]->{percent}, 0.25), 'left window got 25%');
+ok(cmp_float($nodes->[1]->{percent}, 0.75), 'right window got 75%');
################################################################################
# Check that the resize grow/shrink width/height syntax works.
cmd 'resize grow width 10 px or 25 ppt';
($nodes, $focus) = get_ws_content($tmp);
-is($nodes->[0]->{percent}, 0.25, 'left window got 25%');
-is($nodes->[1]->{percent}, 0.75, 'right window got 75%');
+ok(cmp_float($nodes->[0]->{percent}, 0.25), 'left window got 25%');
+ok(cmp_float($nodes->[1]->{percent}, 0.75), 'right window got 75%');
# Now test it with four windows
$tmp = fresh_workspace;
cmd 'resize grow width 10 px or 25 ppt';
($nodes, $focus) = get_ws_content($tmp);
-is($nodes->[0]->{percent}, 0.166666666666667, 'first window got 16%');
-is($nodes->[1]->{percent}, 0.166666666666667, 'second window got 16%');
-is($nodes->[2]->{percent}, 0.166666666666667, 'third window got 16%');
-is($nodes->[3]->{percent}, 0.50, 'fourth window got 50%');
+ok(cmp_float($nodes->[0]->{percent}, 0.166666666666667), 'first window got 16%');
+ok(cmp_float($nodes->[1]->{percent}, 0.166666666666667), 'second window got 16%');
+ok(cmp_float($nodes->[2]->{percent}, 0.166666666666667), 'third window got 16%');
+ok(cmp_float($nodes->[3]->{percent}, 0.50), 'fourth window got 50%');
# height should be a no-op in this situation
cmd 'resize grow height 10 px or 25 ppt';
($nodes, $focus) = get_ws_content($tmp);
-is($nodes->[0]->{percent}, 0.166666666666667, 'first window got 16%');
-is($nodes->[1]->{percent}, 0.166666666666667, 'second window got 16%');
-is($nodes->[2]->{percent}, 0.166666666666667, 'third window got 16%');
-is($nodes->[3]->{percent}, 0.50, 'fourth window got 50%');
+ok(cmp_float($nodes->[0]->{percent}, 0.166666666666667), 'first window got 16%');
+ok(cmp_float($nodes->[1]->{percent}, 0.166666666666667), 'second window got 16%');
+ok(cmp_float($nodes->[2]->{percent}, 0.166666666666667), 'third window got 16%');
+ok(cmp_float($nodes->[3]->{percent}, 0.50), 'fourth window got 50%');
############################################################
my $tmp = fresh_workspace;
-#####################################################################
-# open the left window
-#####################################################################
+################################################################################
+# Open the left window.
+################################################################################
my $left = open_window({ background_color => '#ff0000' });
diag("left = " . $left->id);
-#####################################################################
-# Open the right window
-#####################################################################
+################################################################################
+# Open the right window.
+################################################################################
my $right = open_window({ background_color => '#00ff00' });
diag("right = " . $right->id);
-#####################################################################
-# Set the right window to fullscreen
-#####################################################################
+################################################################################
+# Set the right window to fullscreen.
+################################################################################
+
cmd 'nop setting fullscreen';
cmd 'fullscreen';
-#####################################################################
-# Open a third window
-#####################################################################
+################################################################################
+# Open a third window. Since we're fullscreen, the window won't be # mapped, so
+# don't wait for it to be mapped. Instead, just send the map request and sync
+# with i3 to make sure i3 recognizes it.
+################################################################################
my $third = open_window({
background_color => '#0000ff',
diag("third = " . $third->id);
-# move the fullscreen window to a different ws
+################################################################################
+# Move the window to a different workspace, and verify that the third window now
+# gets focused in the current workspace.
+################################################################################
my $tmp2 = get_unused_workspace;
cmd "move workspace $tmp2";
-# verify that the third window has the focus
is($x->input_focus, $third->id, 'third window focused');
################################################################################
is($nodes->[0]->{focused}, 1, 'fullscreen window focused');
################################################################################
-# Make sure it's possible to focus a container in a different workspace even if
-# we are currently focusing a fullscreen container.
+# Ensure it's possible to change focus if it doesn't escape the fullscreen
+# container with fullscreen global. We can't even focus a container in a
+# different workspace.
################################################################################
+cmd 'fullscreen';
+
+$tmp = fresh_workspace;
+cmd "workspace $tmp";
+my $diff_ws = open_window;
+
$tmp2 = fresh_workspace;
-my $focusable_window = open_window;
+cmd "workspace $tmp2";
+cmd 'split h';
+
+$left = open_window;
+my $right1 = open_window;
+cmd 'split v';
+my $right2 = open_window;
+
+cmd 'focus parent';
+cmd 'fullscreen global';
+
+cmd '[id="' . $right1->id . '"] focus';
+is($x->input_focus, $right1->id, 'upper right window focused');
+
+cmd '[id="' . $right2->id . '"] focus';
+is($x->input_focus, $right2->id, 'bottom right window focused');
+
+cmd 'focus parent';
+isnt($x->input_focus, $right2->id, 'bottom right window no longer focused');
+
+cmd 'focus child';
+is($x->input_focus, $right2->id, 'bottom right window focused again');
+
+cmd '[id="' . $left->id . '"] focus';
+is($x->input_focus, $right2->id, 'prevented focus change to left window');
+
+cmd 'focus up';
+is($x->input_focus, $right1->id, 'allowed focus up');
+
+cmd 'focus down';
+is($x->input_focus, $right2->id, 'allowed focus down');
+
+cmd 'focus left';
+is($x->input_focus, $right2->id, 'prevented focus left');
+
+cmd 'focus right';
+is($x->input_focus, $right2->id, 'prevented focus right');
+
+cmd 'focus down';
+is($x->input_focus, $right1->id, 'allowed focus wrap (down)');
+
+cmd 'focus up';
+is($x->input_focus, $right2->id, 'allowed focus wrap (up)');
+
+cmd '[id="' . $diff_ws->id . '"] focus';
+is($x->input_focus, $right2->id, 'prevented focus change to different ws');
+
+################################################################################
+# Same tests when we're in non-global fullscreen mode. It should now be possible
+# to focus a container in a different workspace.
+################################################################################
+
+cmd 'focus parent';
+cmd 'fullscreen global';
+cmd 'fullscreen';
+
+cmd '[id="' . $right1->id . '"] focus';
+is($x->input_focus, $right1->id, 'upper right window focused');
+
+cmd '[id="' . $right2->id . '"] focus';
+is($x->input_focus, $right2->id, 'bottom right window focused');
+
+cmd 'focus parent';
+isnt($x->input_focus, $right2->id, 'bottom right window no longer focused');
+
+cmd 'focus child';
+is($x->input_focus, $right2->id, 'bottom right window focused again');
+
+cmd '[id="' . $left->id . '"] focus';
+is($x->input_focus, $right2->id, 'prevented focus change to left window');
+
+cmd 'focus up';
+is($x->input_focus, $right1->id, 'allowed focus up');
+
+cmd 'focus down';
+is($x->input_focus, $right2->id, 'allowed focus down');
+
+cmd 'focus left';
+is($x->input_focus, $right2->id, 'prevented focus left');
+
+cmd 'focus right';
+is($x->input_focus, $right2->id, 'prevented focus right');
+
+cmd 'focus down';
+is($x->input_focus, $right1->id, 'allowed focus wrap (down)');
+
+cmd 'focus up';
+is($x->input_focus, $right2->id, 'allowed focus wrap (up)');
+
+cmd '[id="' . $diff_ws->id . '"] focus';
+is($x->input_focus, $diff_ws->id, 'allowed focus change to different ws');
+
+################################################################################
+# More testing of the interaction between wrapping and the fullscreen focus
+# restrictions.
+################################################################################
+
+cmd '[id="' . $right1->id . '"] focus';
+is($x->input_focus, $right1->id, 'upper right window focused');
+
+cmd 'focus parent';
+cmd 'fullscreen';
+cmd 'focus child';
+
+cmd 'split v';
+my $right12 = open_window;
+
+cmd 'focus down';
+is($x->input_focus, $right2->id, 'bottom right window focused');
+
+cmd 'split v';
+my $right22 = open_window;
+
+cmd 'focus parent';
+cmd 'fullscreen';
+cmd 'focus child';
+
+cmd 'focus down';
+is($x->input_focus, $right2->id, 'focus did not leave parent container (1)');
+
+cmd 'focus down';
+is($x->input_focus, $right22->id, 'focus did not leave parent container (2)');
+
+cmd 'focus up';
+is($x->input_focus, $right2->id, 'focus did not leave parent container (3)');
+
+cmd 'focus up';
+is($x->input_focus, $right22->id, 'focus did not leave parent container (4)');
+
+################################################################################
+# Ensure that moving in a direction doesn't violate the focus restrictions.
+################################################################################
+
+sub verify_move {
+ my $num = shift;
+ my $msg = shift;
+ my $nodes = get_ws_content($tmp2);
+ my $split = $nodes->[1];
+ my $fs = $split->{nodes}->[1];
+ is(scalar @{$fs->{nodes}}, $num, $msg);
+}
+
+cmd 'move left';
+verify_move(2, 'prevented move left');
+cmd 'move right';
+verify_move(2, 'prevented move right');
+cmd 'move down';
+verify_move(2, 'prevented move down');
+cmd 'move up';
+cmd 'move up';
+verify_move(2, 'prevented move up');
+
+################################################################################
+# Moving to a different workspace is allowed with per-output fullscreen
+# containers.
+################################################################################
+
+cmd "move to workspace $tmp";
+verify_move(1, 'did not prevent move to workspace by name');
cmd "workspace $tmp";
-cmd '[id="' . $focusable_window->id . '"] focus';
+cmd "move to workspace $tmp2";
+cmd "workspace $tmp2";
-is(focused_ws(), $tmp2, 'focus went to a different workspace');
+cmd "move to workspace prev";
+verify_move(1, 'did not prevent move to workspace by position');
-$nodes = get_ws_content($tmp2);
-is(scalar @$nodes, 1, 'precisely one window');
-is($nodes->[0]->{focused}, 1, 'focusable window focused');
+################################################################################
+# Ensure that is not allowed with global fullscreen containers.
+################################################################################
+
+cmd "workspace $tmp";
+cmd "move to workspace $tmp2";
+cmd "workspace $tmp2";
+
+cmd 'focus parent';
+cmd 'fullscreen';
+cmd 'fullscreen global';
+cmd 'focus child';
+
+cmd "move to workspace $tmp";
+verify_move(2, 'prevented move to workspace by name');
+
+cmd "move to workspace prev";
+verify_move(2, 'prevented move to workspace by position');
+
+# TODO: Tests for "move to output" and "move workspace to output".
done_testing;
+++ /dev/null
-#!perl
-# vim:ts=4:sw=4:expandtab
-#
-# Regression test: level up should be a noop during fullscreen mode
-#
-use i3test;
-
-my $tmp = fresh_workspace;
-
-#####################################################################
-# open a window, verify it’s not in fullscreen mode
-#####################################################################
-
-my $win = open_window;
-
-my $nodes = get_ws_content $tmp;
-is(@$nodes, 1, 'exactly one client');
-is($nodes->[0]->{fullscreen_mode}, 0, 'client not fullscreen');
-
-#####################################################################
-# make it fullscreen
-#####################################################################
-
-cmd 'nop making fullscreen';
-cmd 'fullscreen';
-
-$nodes = get_ws_content $tmp;
-is($nodes->[0]->{fullscreen_mode}, 1, 'client fullscreen now');
-
-#####################################################################
-# send level up, try to un-fullscreen
-#####################################################################
-cmd 'focus parent';
-cmd 'fullscreen';
-
-$nodes = get_ws_content $tmp;
-is($nodes->[0]->{fullscreen_mode}, 0, 'client not fullscreen any longer');
-
-does_i3_live;
-
-done_testing;