=== 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
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);
* 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
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.
'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
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
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) {
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) {
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 */
*
*/
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);
}
/*
no_focus
focus_follows_mouse
mouse_warping
+ focus_wrapping
force_focus_wrapping
force_xinerama
force-xinerama
--- /dev/null
+#!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;