From 536f60e230713fa131cdeeebf80bc5518c62eeee Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Fri, 15 Sep 2017 01:59:50 +0000 Subject: [PATCH 1/3] docs/userguide: Update description of focus wrapping Focus wrapping applies to all kinds of containers, not just tabbed/stacked ones. --- docs/userguide | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/userguide b/docs/userguide index cc9b5a01..7064dcef 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1039,11 +1039,12 @@ 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+. +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 however there is another window or container in that direction, focus +will 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 From 28f7e14650882d89fae2eee78291eeec8dd4e8fd Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Fri, 15 Sep 2017 02:57:55 +0000 Subject: [PATCH 2/3] Add "focus_wrapping" option Fixes #2352. --- docs/userguide | 28 ++++++++++--- include/config_directives.h | 1 + include/configuration.h | 8 ++++ parser-specs/config.spec | 6 +++ src/config.c | 2 + src/config_directives.c | 4 ++ src/tree.c | 2 +- testcases/t/201-config-parser.t | 1 + testcases/t/539-disable_focus_wrapping.t | 51 ++++++++++++++++++++++++ 9 files changed, 96 insertions(+), 7 deletions(-) create mode 100644 testcases/t/539-disable_focus_wrapping.t diff --git a/docs/userguide b/docs/userguide index 7064dcef..fdfb121b 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1039,12 +1039,28 @@ popup_during_fullscreen smart === Focus wrapping -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 however there is another window or container in that direction, focus -will 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+. +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 using the +focus_wrapping+ +configuration directive: + +*Syntax*: +--------------------- +focus_wrapping yes|no +--------------------- + +*Example*: +----------------- +focus_wrapping no +----------------- + +By default, focus wrapping does not occur 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 diff --git a/include/config_directives.h b/include/config_directives.h index b729e728..66defa8f 100644 --- a/include/config_directives.h +++ b/include/config_directives.h @@ -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); diff --git a/include/configuration.h b/include/configuration.h index 4f6e5ce8..33df2c2d 100644 --- a/include/configuration.h +++ b/include/configuration.h @@ -137,6 +137,14 @@ struct Config { * comes with i3. Thus, you can turn it off entirely. */ bool disable_workspace_bar; + /** 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. */ + bool focus_wrapping; + /** 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 diff --git a/parser-specs/config.spec b/parser-specs/config.spec index 665b046a..3a10bbc1 100644 --- a/parser-specs/config.spec +++ b/parser-specs/config.spec @@ -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 @@ -203,6 +204,11 @@ state MOUSE_WARPING: value = 'none', 'output' -> call cfg_mouse_warping($value) +# focus_wrapping +state FOCUS_WRAPPING: + value = word + -> call cfg_focus_wrapping($value) + # force_focus_wrapping state FORCE_FOCUS_WRAPPING: value = word diff --git a/src/config.c b/src/config.c index 7e08b520..c8e9bd6b 100644 --- a/src/config.c +++ b/src/config.c @@ -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 = true; + parse_configuration(override_configpath, true); if (reload) { diff --git a/src/config_directives.c b/src/config_directives.c index 376397e8..41d21dec 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -264,6 +264,10 @@ CFGFUN(disable_randr15, const char *value) { config.disable_randr15 = eval_boolstr(value); } +CFGFUN(focus_wrapping, const char *value) { + config.focus_wrapping = eval_boolstr(value); +} + CFGFUN(force_focus_wrapping, const char *value) { config.force_focus_wrapping = eval_boolstr(value); } diff --git a/src/tree.c b/src/tree.c index 7f466583..97c02798 100644 --- a/src/tree.c +++ b/src/tree.c @@ -675,7 +675,7 @@ 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); } /* diff --git a/testcases/t/201-config-parser.t b/testcases/t/201-config-parser.t index e8080a73..3e2c4297 100644 --- a/testcases/t/201-config-parser.t +++ b/testcases/t/201-config-parser.t @@ -470,6 +470,7 @@ my $expected_all_tokens = "ERROR: CONFIG: Expected one of these tokens: , ' 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 index 00000000..8d2e8472 --- /dev/null +++ b/testcases/t/539-disable_focus_wrapping.t @@ -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 => <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; From 50edf495aa3971bfb67471c3aaf2eb72e7abd443 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Fri, 22 Sep 2017 23:41:38 +0000 Subject: [PATCH 3/3] Merge "force_focus_wrapping" option into "focus_wrapping force" Allow enabling forced focus wrapping by specifying "focus_wrapping force" in i3's configuration. This syntax supersedes the previous "force_focus_wrapping yes" one, which remains available for backwards compatibility. --- docs/userguide | 44 ++++++++++++++++++---------------------- include/configuration.h | 25 ++++++++++++----------- include/data.h | 9 ++++++++ parser-specs/config.spec | 2 +- src/config.c | 2 +- src/config_directives.c | 19 +++++++++++++++-- src/tree.c | 5 +++-- 7 files changed, 64 insertions(+), 42 deletions(-) diff --git a/docs/userguide b/docs/userguide index fdfb121b..bfb3da66 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1044,38 +1044,34 @@ 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 using the +focus_wrapping+ -configuration directive: +If desired, you can disable this behavior by setting the +focus_wrapping+ +configuration directive to the value +no+. -*Syntax*: ---------------------- -focus_wrapping yes|no ---------------------- - -*Example*: ------------------ -focus_wrapping no ------------------ - -By default, focus wrapping does not occur 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+. +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 + +# Legacy syntax, equivalent to "focus_wrapping force" +force_focus_wrapping yes --------------------------- -*Example*: ------------------------- -force_focus_wrapping yes ------------------------- +*Examples*: +----------------- +# Disable focus wrapping +focus_wrapping no + +# Force focus wrapping +focus_wrapping force +----------------- === Forcing Xinerama diff --git a/include/configuration.h b/include/configuration.h index 33df2c2d..8f1ce332 100644 --- a/include/configuration.h +++ b/include/configuration.h @@ -142,18 +142,19 @@ struct Config { * 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. */ - bool focus_wrapping; - - /** 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; + * 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 diff --git a/include/data.h b/include/data.h index 7411ac20..258ea94f 100644 --- a/include/data.h +++ b/include/data.h @@ -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. diff --git a/parser-specs/config.spec b/parser-specs/config.spec index 3a10bbc1..6b826543 100644 --- a/parser-specs/config.spec +++ b/parser-specs/config.spec @@ -206,7 +206,7 @@ state MOUSE_WARPING: # focus_wrapping state FOCUS_WRAPPING: - value = word + value = '1', 'yes', 'true', 'on', 'enable', 'active', '0', 'no', 'false', 'off', 'disable', 'inactive', 'force' -> call cfg_focus_wrapping($value) # force_focus_wrapping diff --git a/src/config.c b/src/config.c index c8e9bd6b..24c7b541 100644 --- a/src/config.c +++ b/src/config.c @@ -227,7 +227,7 @@ 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 = true; + config.focus_wrapping = FOCUS_WRAPPING_ON; parse_configuration(override_configpath, true); diff --git a/src/config_directives.c b/src/config_directives.c index 41d21dec..8d5cf1f0 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -265,11 +265,26 @@ CFGFUN(disable_randr15, const char *value) { } CFGFUN(focus_wrapping, const char *value) { - config.focus_wrapping = eval_boolstr(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) { diff --git a/src/tree.c b/src/tree.c index 97c02798..fc9526e6 100644 --- a/src/tree.c +++ b/src/tree.c @@ -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, config.focus_wrapping); + _tree_next(focused, way, orientation, + config.focus_wrapping != FOCUS_WRAPPING_OFF); } /*