]> git.sur5r.net Git - i3/i3/commitdiff
Merge pull request #2953 from CyberShadow/focus_wrapping
authorMichael Stapelberg <stapelberg@users.noreply.github.com>
Wed, 27 Sep 2017 16:31:39 +0000 (09:31 -0700)
committerGitHub <noreply@github.com>
Wed, 27 Sep 2017 16:31:39 +0000 (09:31 -0700)
Add "focus_wrapping" option

docs/userguide
include/config_directives.h
include/configuration.h
include/data.h
parser-specs/config.spec
src/config.c
src/config_directives.c
src/tree.c
testcases/t/201-config-parser.t
testcases/t/539-disable_focus_wrapping.t [new file with mode: 0644]

index d682c1384de457f17db21f9825739929c6dd51f4..5225c39149539342158c0774b93f54922419bc1d 100644 (file)
@@ -1055,26 +1055,39 @@ popup_during_fullscreen smart
 
 === Focus wrapping
 
-When being in a tabbed or stacked container, the first container will be
-focused when you use +focus down+ on the last container -- the focus wraps. If
-however there is another stacked/tabbed container in that direction, focus will
-be set on that container. This is the default behavior so you can navigate to
-all your windows without having to use +focus parent+.
+By default, when in a container with several windows or child containers, the
+opposite window will be focused when trying to move the focus over the edge of
+a container (and there are no other containers in that direction) -- the focus
+wraps.
+
+If desired, you can disable this behavior by setting the +focus_wrapping+
+configuration directive to the value +no+.
+
+When enabled, focus wrapping does not occur by default if there is another
+window or container in the specified direction, and focus will instead be set
+on that window or container. This is the default behavior so you can navigate
+to all your windows without having to use +focus parent+.
 
 If you want the focus to *always* wrap and you are aware of using +focus
-parent+ to switch to different containers, you can use the
-+force_focus_wrapping+ configuration directive. After enabling it, the focus
-will always wrap.
+parent+ to switch to different containers, you can instead set +focus_wrapping+
+to the value +force+.
 
 *Syntax*:
 ---------------------------
-force_focus_wrapping yes|no
----------------------------
+focus_wrapping yes|no|force
 
-*Example*:
-------------------------
+# Legacy syntax, equivalent to "focus_wrapping force"
 force_focus_wrapping yes
-------------------------
+---------------------------
+
+*Examples*:
+-----------------
+# Disable focus wrapping
+focus_wrapping no
+
+# Force focus wrapping
+focus_wrapping force
+-----------------
 
 === Forcing Xinerama
 
index 5318bae809cbc45eba158f93b4902a4ae92b6ea9..1191a7c6e34a476da70e286e16f0f39f4b4b1cac 100644 (file)
@@ -49,6 +49,7 @@ CFGFUN(workspace_layout, const char *layout);
 CFGFUN(workspace_back_and_forth, const char *value);
 CFGFUN(focus_follows_mouse, const char *value);
 CFGFUN(mouse_warping, const char *value);
+CFGFUN(focus_wrapping, const char *value);
 CFGFUN(force_focus_wrapping, const char *value);
 CFGFUN(force_xinerama, const char *value);
 CFGFUN(disable_randr15, const char *value);
index 4f6e5ce833be7e80ac36422efc8537e4e8ba68d5..8f1ce3320353664296fa49a410f717e2049e9016 100644 (file)
@@ -137,15 +137,24 @@ struct Config {
      * comes with i3. Thus, you can turn it off entirely. */
     bool disable_workspace_bar;
 
-    /** Think of the following layout: Horizontal workspace with a tabbed
-     * con on the left of the screen and a terminal on the right of the
-     * screen. You are in the second container in the tabbed container and
-     * focus to the right. By default, i3 will set focus to the terminal on
-     * the right. If you are in the first container in the tabbed container
-     * however, focusing to the left will wrap. This option forces i3 to
-     * always wrap, which will result in you having to use "focus parent"
-     * more often. */
-    bool force_focus_wrapping;
+    /** When focus wrapping is enabled (the default), attempting to
+     * move focus past the edge of the screen (in other words, in a
+     * direction in which there are no more containers to focus) will
+     * cause the focus to wrap to the opposite edge of the current
+     * container. When it is disabled, nothing happens; the current
+     * focus is preserved.
+     *
+     * Additionally, focus wrapping may be forced. Think of the
+     * following layout: Horizontal workspace with a tabbed con on the
+     * left of the screen and a terminal on the right of the
+     * screen. You are in the second container in the tabbed container
+     * and focus to the right. By default, i3 will set focus to the
+     * terminal on the right. If you are in the first container in the
+     * tabbed container however, focusing to the left will
+     * wrap. Setting focus_wrapping to FOCUS_WRAPPING_FORCE forces i3
+     * to always wrap, which will result in you having to use "focus
+     * parent" more often. */
+    focus_wrapping_t focus_wrapping;
 
     /** By default, use the RandR API for multi-monitor setups.
      * Unfortunately, the nVidia binary graphics driver doesn't support
index 632dfb4f04c98191a0b9c11405e7a0cff0c074a4..69a46e464331172172873a70940af6d348cb09bd 100644 (file)
@@ -133,6 +133,15 @@ typedef enum {
     POINTER_WARPING_NONE = 1
 } warping_t;
 
+/**
+ * Focus wrapping modes.
+ */
+typedef enum {
+    FOCUS_WRAPPING_OFF = 0,
+    FOCUS_WRAPPING_ON = 1,
+    FOCUS_WRAPPING_FORCE = 2
+} focus_wrapping_t;
+
 /**
  * Stores a rectangle, for example the size of a window, the child window etc.
  * It needs to be packed so that the compiler will not add any padding bytes.
index 8c89b3f912024a6695d2a7f13687cf8bcbf1d6c3..c31567a64b87d29dc340cd821621f15c2636fb45 100644 (file)
@@ -36,6 +36,7 @@ state INITIAL:
   'no_focus'                               -> NO_FOCUS
   'focus_follows_mouse'                    -> FOCUS_FOLLOWS_MOUSE
   'mouse_warping'                          -> MOUSE_WARPING
+  'focus_wrapping'                         -> FOCUS_WRAPPING
   'force_focus_wrapping'                   -> FORCE_FOCUS_WRAPPING
   'force_xinerama', 'force-xinerama'       -> FORCE_XINERAMA
   'disable_randr15', 'disable-randr15'     -> DISABLE_RANDR15
@@ -209,6 +210,11 @@ state MOUSE_WARPING:
   value = 'none', 'output'
       -> call cfg_mouse_warping($value)
 
+# focus_wrapping
+state FOCUS_WRAPPING:
+  value = '1', 'yes', 'true', 'on', 'enable', 'active', '0', 'no', 'false', 'off', 'disable', 'inactive', 'force'
+      -> call cfg_focus_wrapping($value)
+
 # force_focus_wrapping
 state FORCE_FOCUS_WRAPPING:
   value = word
index 7e08b5208702ef64d06af2e047e302f3b0714aee..24c7b541e447e4f80ab2b676c4d3f0b140bdff6c 100644 (file)
@@ -227,6 +227,8 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
     if (config.workspace_urgency_timer == 0)
         config.workspace_urgency_timer = 0.5;
 
+    config.focus_wrapping = FOCUS_WRAPPING_ON;
+
     parse_configuration(override_configpath, true);
 
     if (reload) {
index c981ff60910729c57aec228d289e18e7ea37362c..1cf875ef00aeaf5d30c52f9944f84217a99fc19b 100644 (file)
@@ -264,8 +264,27 @@ CFGFUN(disable_randr15, const char *value) {
     config.disable_randr15 = eval_boolstr(value);
 }
 
+CFGFUN(focus_wrapping, const char *value) {
+    if (strcmp(value, "force") == 0) {
+        config.focus_wrapping = FOCUS_WRAPPING_FORCE;
+    } else if (eval_boolstr(value)) {
+        config.focus_wrapping = FOCUS_WRAPPING_ON;
+    } else {
+        config.focus_wrapping = FOCUS_WRAPPING_OFF;
+    }
+}
+
 CFGFUN(force_focus_wrapping, const char *value) {
-    config.force_focus_wrapping = eval_boolstr(value);
+    /* Legacy syntax. */
+    if (eval_boolstr(value)) {
+        config.focus_wrapping = FOCUS_WRAPPING_FORCE;
+    } else {
+        /* For "force_focus_wrapping off", don't enable or disable
+         * focus wrapping, just ensure it's not forced. */
+        if (config.focus_wrapping == FOCUS_WRAPPING_FORCE) {
+            config.focus_wrapping = FOCUS_WRAPPING_ON;
+        }
+    }
 }
 
 CFGFUN(workspace_back_and_forth, const char *value) {
index d5f457dd24dc268921ff89acf8c2dd76499035cb..a6cc59fbb8faf8260e21719edba5bb85e164fc85 100644 (file)
@@ -641,7 +641,7 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
         next = TAILQ_PREV(current, nodes_head, nodes);
 
     if (!next) {
-        if (!config.force_focus_wrapping) {
+        if (config.focus_wrapping != FOCUS_WRAPPING_FORCE) {
             /* If there is no next/previous container, we check if we can focus one
              * when going higher (without wrapping, though). If so, we are done, if
              * not, we wrap */
@@ -675,7 +675,8 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
  *
  */
 void tree_next(char way, orientation_t orientation) {
-    _tree_next(focused, way, orientation, true);
+    _tree_next(focused, way, orientation,
+               config.focus_wrapping != FOCUS_WRAPPING_OFF);
 }
 
 /*
index 861f8a35b3ffc9cc0074bc5cb8bb69e422690779..ece7e9b9a35012e68cb0f41b98e1c4ee05c421d9 100644 (file)
@@ -470,6 +470,7 @@ my $expected_all_tokens = "ERROR: CONFIG: Expected one of these tokens: <end>, '
         no_focus
         focus_follows_mouse
         mouse_warping
+        focus_wrapping
         force_focus_wrapping
         force_xinerama
         force-xinerama
diff --git a/testcases/t/539-disable_focus_wrapping.t b/testcases/t/539-disable_focus_wrapping.t
new file mode 100644 (file)
index 0000000..8d2e847
--- /dev/null
@@ -0,0 +1,51 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+#   (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+#   (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://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 that focus does not wrap when focus_wrapping is disabled in
+# the configuration.
+# Ticket: #2352
+# Bug still in: 4.14-72-g6411130c
+use i3test i3_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+focus_wrapping no
+EOT
+
+sub test_orientation {
+    my ($orientation, $prev, $next) = @_;
+    my $tmp = fresh_workspace;
+
+    cmd "split $orientation";
+
+    my $win1 = open_window;
+    my $win2 = open_window;
+
+    is($x->input_focus, $win2->id, "Second window focused initially");
+    cmd "focus $prev";
+    is($x->input_focus, $win1->id, "First window focused");
+    cmd "focus $prev";
+    is($x->input_focus, $win1->id, "First window still focused");
+    cmd "focus $next";
+    is($x->input_focus, $win2->id, "Second window focused");
+    cmd "focus $next";
+    is($x->input_focus, $win2->id, "Second window still focused");
+}
+
+test_orientation('v', 'up', 'down');
+test_orientation('h', 'left', 'right');
+
+done_testing;