]> git.sur5r.net Git - i3/i3/commitdiff
Merge branch 'master' into next
authorMichael Stapelberg <michael@stapelberg.de>
Sun, 10 Jun 2012 16:19:29 +0000 (18:19 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Sun, 10 Jun 2012 16:19:29 +0000 (18:19 +0200)
16 files changed:
docs/refcard.html
docs/userguide
include/con.h
include/tree.h
parser-specs/commands.spec
src/commands.c
src/con.c
src/floating.c
src/move.c
src/tree.c
src/workspace.c
testcases/lib/i3test.pm
testcases/t/132-move-workspace.t
testcases/t/141-resize.t
testcases/t/156-fullscreen-focus.t
testcases/t/157-regress-fullscreen-level-up.t [deleted file]

index a4427f4f261380b85377cd8e8e29a8de2c735733..7156da368e72721b443f424365dd6026765fd348 100644 (file)
                </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
index 4145279302eff8368eaf01d353a8add6840d8d49..37ddf4f8a115b6c7063923a67a393de83554846f 100644 (file)
@@ -1293,8 +1293,9 @@ You can also switch to the next and previous workspace with the commands
 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
@@ -1310,6 +1311,18 @@ you can use the +move workspace to output+ command followed by the name of the
 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
@@ -1325,6 +1338,9 @@ bindsym mod+b workspace back_and_forth
 
 # 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
index b14c477e53de6fb864b8d1dc06e29c1b36510fd3..95726147a74d56a053141c847fc0f5b3fb7d6843 100644 (file)
@@ -255,4 +255,27 @@ void con_set_layout(Con *con, int layout);
  */
 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
index b9159e3b5b134851ae759ebb0935aadcdcd47230..8816b19a640a55d0fa84300bdd59295c56c24943 100644 (file)
@@ -39,16 +39,16 @@ Con *tree_open_con(Con *con, i3Window *window);
 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
index b0fb9e0118a4e0f462a10c762d6891864da8a97b..b416d9680dfbfb2a48b248975fa376d17be6f752 100644 (file)
@@ -190,7 +190,7 @@ state RENAME_WORKSPACE_TO:
       -> 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>
@@ -231,7 +231,7 @@ state MOVE_DIRECTION_PX:
 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
index 1e1ee0ff243190aafa4d9f2a8e34256702618a4c..6f0d0c5e1ff4cad38a760e42f23ab3257ef259ac 100644 (file)
@@ -23,7 +23,7 @@
 } 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.
  */
@@ -351,7 +351,7 @@ void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue) {
 
 /*
  * 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) {
@@ -359,6 +359,15 @@ 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 */
@@ -371,6 +380,8 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) {
         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);
@@ -400,9 +411,11 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
 
     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;
     }
@@ -430,9 +443,11 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
 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;
     }
@@ -1213,23 +1228,26 @@ void cmd_focus_window_mode(I3_CMD, char *window_mode) {
  *
  */
 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);
 }
 
 /*
@@ -1262,13 +1280,9 @@ void cmd_focus(I3_CMD) {
         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;
         }
index c24a379f7abc186a1fd2dc5944da7a1e03992be0..f804a204a7979ed8f484570e95902701208dd7ef 100644 (file)
--- a/src/con.c
+++ b/src/con.c
@@ -576,11 +576,27 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
         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);
 
@@ -663,8 +679,12 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
         /* 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);
@@ -1132,3 +1152,63 @@ Rect con_minimum_size(Con *con) {
          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;
+}
index 3b69116988fc8470655e2f24b486486628b1a9c6..c0154acac47cf673810a4765459a48f9b8a69a6c 100644 (file)
@@ -614,12 +614,12 @@ void floating_fix_coordinates(Con *con, Rect *old_rect, Rect *new_rect) {
     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);
 }
 
index d3065c2490bf1faae92e6882231ece95b1a0fe16..d110312aa57ab9b916b9cfe3073b96943b73a6ea 100644 (file)
@@ -169,6 +169,12 @@ void tree_move(int direction) {
     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;
index f29369c607eba2d0e64b64d630a0d6ffe78753c2..3ed392ac125b9462699638978e5df83afa85ce4b 100644 (file)
@@ -375,38 +375,34 @@ 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) {
-    /* 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) {
@@ -560,6 +556,10 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
         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 */
index 928f0bd65e88b06e10a012b77a5fd178c344be87..3d08fa4c8db51031d1fcf005c59e44287a6069a5 100644 (file)
@@ -114,14 +114,15 @@ Con *create_workspace_on_output(Output *output, Con *content) {
         /* 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++;
index 4c41a7f2f27d4ccbc210f913417262dcabdd7992..32a17934d8e23d6e91cfb9645dcee68faeecf432 100644 (file)
@@ -33,6 +33,7 @@ our @EXPORT = qw(
     open_floating_window
     get_dock_clients
     cmd
+    cmp_float
     sync_with_i3
     does_i3_live
     exit_gracefully
@@ -563,6 +564,14 @@ sub launch_with_config {
     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';
 
index 3f00428c035739cabf9f6f21a04b68cb226bb592..79753cd7133241a7375b831e075a12ef373d212b 100644 (file)
@@ -99,6 +99,26 @@ cmd 'move workspace prev';
 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)
index 91aeca50314cc899283c2873254a274cf9d77270..86954f1371c1a7270e20ccddf911827ec819631f 100644 (file)
@@ -22,8 +22,8 @@ cmd 'resize grow up 10 px or 25 ppt';
 
 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%');
 
 
 ############################################################
@@ -34,8 +34,8 @@ cmd 'split h';
 
 ($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
@@ -52,14 +52,14 @@ cmd 'split h';
 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.
@@ -79,14 +79,14 @@ $top = open_window;
 $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.
@@ -101,8 +101,8 @@ $right = open_window;
 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;
@@ -112,19 +112,19 @@ open_window for (1..4);
 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%');
 
 
 ############################################################
index f9dc6dce1fe48e1fe158b10c629578fc5e6b780e..b779ce7d8490286a95088aacd2b4d2619b640c5b 100644 (file)
@@ -11,9 +11,9 @@ my $i3 = i3(get_socket_path());
 
 my $tmp = fresh_workspace;
 
-#####################################################################
-# open the left window
-#####################################################################
+################################################################################
+# Open the left window.
+################################################################################
 
 my $left = open_window({ background_color => '#ff0000' });
 
@@ -21,23 +21,26 @@ is($x->input_focus, $left->id, 'left window focused');
 
 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',
@@ -51,13 +54,15 @@ sync_with_i3;
 
 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');
 
 ################################################################################
@@ -87,20 +92,204 @@ is($nodes->[0]->{id}, $old_id, 'id unchanged');
 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;
diff --git a/testcases/t/157-regress-fullscreen-level-up.t b/testcases/t/157-regress-fullscreen-level-up.t
deleted file mode 100644 (file)
index 316dbca..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#!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;