Merge pull request #2953 from CyberShadow/focus_wrapping
Add "focus_wrapping" option
This commit is contained in:
commit
54d61b510d
|
@ -1055,26 +1055,39 @@ popup_during_fullscreen smart
|
||||||
|
|
||||||
=== Focus wrapping
|
=== Focus wrapping
|
||||||
|
|
||||||
When being in a tabbed or stacked container, the first container will be
|
By default, when in a container with several windows or child containers, the
|
||||||
focused when you use +focus down+ on the last container -- the focus wraps. If
|
opposite window will be focused when trying to move the focus over the edge of
|
||||||
however there is another stacked/tabbed container in that direction, focus will
|
a container (and there are no other containers in that direction) -- the focus
|
||||||
be set on that container. This is the default behavior so you can navigate to
|
wraps.
|
||||||
all your windows without having to use +focus parent+.
|
|
||||||
|
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
|
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
|
parent+ to switch to different containers, you can instead set +focus_wrapping+
|
||||||
+force_focus_wrapping+ configuration directive. After enabling it, the focus
|
to the value +force+.
|
||||||
will always wrap.
|
|
||||||
|
|
||||||
*Syntax*:
|
*Syntax*:
|
||||||
---------------------------
|
---------------------------
|
||||||
force_focus_wrapping yes|no
|
focus_wrapping yes|no|force
|
||||||
|
|
||||||
|
# Legacy syntax, equivalent to "focus_wrapping force"
|
||||||
|
force_focus_wrapping yes
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
*Example*:
|
*Examples*:
|
||||||
------------------------
|
-----------------
|
||||||
force_focus_wrapping yes
|
# Disable focus wrapping
|
||||||
------------------------
|
focus_wrapping no
|
||||||
|
|
||||||
|
# Force focus wrapping
|
||||||
|
focus_wrapping force
|
||||||
|
-----------------
|
||||||
|
|
||||||
=== Forcing Xinerama
|
=== Forcing Xinerama
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ CFGFUN(workspace_layout, const char *layout);
|
||||||
CFGFUN(workspace_back_and_forth, const char *value);
|
CFGFUN(workspace_back_and_forth, const char *value);
|
||||||
CFGFUN(focus_follows_mouse, const char *value);
|
CFGFUN(focus_follows_mouse, const char *value);
|
||||||
CFGFUN(mouse_warping, const char *value);
|
CFGFUN(mouse_warping, const char *value);
|
||||||
|
CFGFUN(focus_wrapping, const char *value);
|
||||||
CFGFUN(force_focus_wrapping, const char *value);
|
CFGFUN(force_focus_wrapping, const char *value);
|
||||||
CFGFUN(force_xinerama, const char *value);
|
CFGFUN(force_xinerama, const char *value);
|
||||||
CFGFUN(disable_randr15, const char *value);
|
CFGFUN(disable_randr15, const char *value);
|
||||||
|
|
|
@ -137,15 +137,24 @@ struct Config {
|
||||||
* comes with i3. Thus, you can turn it off entirely. */
|
* comes with i3. Thus, you can turn it off entirely. */
|
||||||
bool disable_workspace_bar;
|
bool disable_workspace_bar;
|
||||||
|
|
||||||
/** Think of the following layout: Horizontal workspace with a tabbed
|
/** When focus wrapping is enabled (the default), attempting to
|
||||||
* con on the left of the screen and a terminal on the right of the
|
* move focus past the edge of the screen (in other words, in a
|
||||||
* screen. You are in the second container in the tabbed container and
|
* direction in which there are no more containers to focus) will
|
||||||
* focus to the right. By default, i3 will set focus to the terminal on
|
* cause the focus to wrap to the opposite edge of the current
|
||||||
* the right. If you are in the first container in the tabbed container
|
* container. When it is disabled, nothing happens; the current
|
||||||
* however, focusing to the left will wrap. This option forces i3 to
|
* focus is preserved.
|
||||||
* always wrap, which will result in you having to use "focus parent"
|
*
|
||||||
* more often. */
|
* Additionally, focus wrapping may be forced. Think of the
|
||||||
bool force_focus_wrapping;
|
* 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.
|
/** By default, use the RandR API for multi-monitor setups.
|
||||||
* Unfortunately, the nVidia binary graphics driver doesn't support
|
* Unfortunately, the nVidia binary graphics driver doesn't support
|
||||||
|
|
|
@ -133,6 +133,15 @@ typedef enum {
|
||||||
POINTER_WARPING_NONE = 1
|
POINTER_WARPING_NONE = 1
|
||||||
} warping_t;
|
} 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.
|
* 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.
|
* It needs to be packed so that the compiler will not add any padding bytes.
|
||||||
|
|
|
@ -36,6 +36,7 @@ state INITIAL:
|
||||||
'no_focus' -> NO_FOCUS
|
'no_focus' -> NO_FOCUS
|
||||||
'focus_follows_mouse' -> FOCUS_FOLLOWS_MOUSE
|
'focus_follows_mouse' -> FOCUS_FOLLOWS_MOUSE
|
||||||
'mouse_warping' -> MOUSE_WARPING
|
'mouse_warping' -> MOUSE_WARPING
|
||||||
|
'focus_wrapping' -> FOCUS_WRAPPING
|
||||||
'force_focus_wrapping' -> FORCE_FOCUS_WRAPPING
|
'force_focus_wrapping' -> FORCE_FOCUS_WRAPPING
|
||||||
'force_xinerama', 'force-xinerama' -> FORCE_XINERAMA
|
'force_xinerama', 'force-xinerama' -> FORCE_XINERAMA
|
||||||
'disable_randr15', 'disable-randr15' -> DISABLE_RANDR15
|
'disable_randr15', 'disable-randr15' -> DISABLE_RANDR15
|
||||||
|
@ -209,6 +210,11 @@ state MOUSE_WARPING:
|
||||||
value = 'none', 'output'
|
value = 'none', 'output'
|
||||||
-> call cfg_mouse_warping($value)
|
-> 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
|
# force_focus_wrapping
|
||||||
state FORCE_FOCUS_WRAPPING:
|
state FORCE_FOCUS_WRAPPING:
|
||||||
value = word
|
value = word
|
||||||
|
|
|
@ -227,6 +227,8 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
||||||
if (config.workspace_urgency_timer == 0)
|
if (config.workspace_urgency_timer == 0)
|
||||||
config.workspace_urgency_timer = 0.5;
|
config.workspace_urgency_timer = 0.5;
|
||||||
|
|
||||||
|
config.focus_wrapping = FOCUS_WRAPPING_ON;
|
||||||
|
|
||||||
parse_configuration(override_configpath, true);
|
parse_configuration(override_configpath, true);
|
||||||
|
|
||||||
if (reload) {
|
if (reload) {
|
||||||
|
|
|
@ -264,8 +264,27 @@ CFGFUN(disable_randr15, const char *value) {
|
||||||
config.disable_randr15 = eval_boolstr(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) {
|
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) {
|
CFGFUN(workspace_back_and_forth, const char *value) {
|
||||||
|
|
|
@ -641,7 +641,7 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
|
||||||
next = TAILQ_PREV(current, nodes_head, nodes);
|
next = TAILQ_PREV(current, nodes_head, nodes);
|
||||||
|
|
||||||
if (!next) {
|
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
|
/* 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
|
* when going higher (without wrapping, though). If so, we are done, if
|
||||||
* not, we wrap */
|
* 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) {
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -470,6 +470,7 @@ my $expected_all_tokens = "ERROR: CONFIG: Expected one of these tokens: <end>, '
|
||||||
no_focus
|
no_focus
|
||||||
focus_follows_mouse
|
focus_follows_mouse
|
||||||
mouse_warping
|
mouse_warping
|
||||||
|
focus_wrapping
|
||||||
force_focus_wrapping
|
force_focus_wrapping
|
||||||
force_xinerama
|
force_xinerama
|
||||||
force-xinerama
|
force-xinerama
|
||||||
|
|
|
@ -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;
|
Loading…
Reference in New Issue