Merge pull request #1621 from Airblader/feature-1416

Added no_focus directive
This commit is contained in:
Michael Stapelberg 2015-04-01 23:35:03 -07:00
commit d6f1e0c568
8 changed files with 123 additions and 6 deletions

View File

@ -589,6 +589,27 @@ for_window [title="x200: ~/work"] floating enable
The valid criteria are the same as those for commands, see <<command_criteria>>. The valid criteria are the same as those for commands, see <<command_criteria>>.
=== Don't focus window upon opening
[[no_focus]]
When a new window appears, it will be focused. The +no_focus+ directive allows preventing
this from happening and can be used in combination with <<command_criteria>>.
Note that this does not apply to all cases, e.g., when feeding data into a running application
causing it to request being focused. To configure the behavior in such cases, refer to
<<focus_on_window_activation>>.
*Syntax*:
-------------------
no_focus <criteria>
-------------------
*Example*:
-------------------------------
no_focus [window_role="pop-up"]
-------------------------------
=== Variables === Variables
As you learned in the section about keyboard bindings, you will have As you learned in the section about keyboard bindings, you will have
@ -983,11 +1004,13 @@ force_display_urgency_hint 500 ms
=== Focus on window activation === Focus on window activation
[[focus_on_window_activation]]
If a window is activated, e.g., via +google-chrome www.google.com+, it may request If a window is activated, e.g., via +google-chrome www.google.com+, it may request
to take focus. Since this may not preferable, different reactions can be configured. to take focus. Since this may not preferable, different reactions can be configured.
Note that this does not apply to any window that is opened, i.e., typically opening Note that this may not affect windows that are being opened. To prevent new windows
an application will focus that window independent of this configuration. from being focused, see <<no_focus>>.
*Syntax*: *Syntax*:
---------------------------------------------------- ----------------------------------------------------

View File

@ -55,6 +55,7 @@ CFGFUN(focus_on_window_activation, const char *mode);
CFGFUN(show_marks, const char *value); CFGFUN(show_marks, const char *value);
CFGFUN(hide_edge_borders, const char *borders); CFGFUN(hide_edge_borders, const char *borders);
CFGFUN(assign, const char *workspace); CFGFUN(assign, const char *workspace);
CFGFUN(no_focus);
CFGFUN(ipc_socket, const char *path); CFGFUN(ipc_socket, const char *path);
CFGFUN(restart_state, const char *path); CFGFUN(restart_state, const char *path);
CFGFUN(popup_during_fullscreen, const char *value); CFGFUN(popup_during_fullscreen, const char *value);

View File

@ -460,6 +460,7 @@ struct Assignment {
* *
* A_COMMAND = run the specified command for the matching window * A_COMMAND = run the specified command for the matching window
* A_TO_WORKSPACE = assign the matching window to the specified workspace * A_TO_WORKSPACE = assign the matching window to the specified workspace
* A_NO_FOCUS = don't focus matched window when it is managed
* *
* While the type is a bitmask, only one value can be set at a time. It is * While the type is a bitmask, only one value can be set at a time. It is
* a bitmask to allow filtering for multiple types, for example in the * a bitmask to allow filtering for multiple types, for example in the
@ -469,7 +470,8 @@ struct Assignment {
enum { enum {
A_ANY = 0, A_ANY = 0,
A_COMMAND = (1 << 0), A_COMMAND = (1 << 0),
A_TO_WORKSPACE = (1 << 1) A_TO_WORKSPACE = (1 << 1),
A_NO_FOCUS = (1 << 2)
} type; } type;
/** the criteria to check if a window matches */ /** the criteria to check if a window matches */

View File

@ -31,6 +31,7 @@ state INITIAL:
'hide_edge_borders' -> HIDE_EDGE_BORDERS 'hide_edge_borders' -> HIDE_EDGE_BORDERS
'for_window' -> FOR_WINDOW 'for_window' -> FOR_WINDOW
'assign' -> ASSIGN 'assign' -> ASSIGN
'no_focus' -> NO_FOCUS
'focus_follows_mouse' -> FOCUS_FOLLOWS_MOUSE 'focus_follows_mouse' -> FOCUS_FOLLOWS_MOUSE
'mouse_warping' -> MOUSE_WARPING 'mouse_warping' -> MOUSE_WARPING
'force_focus_wrapping' -> FORCE_FOCUS_WRAPPING 'force_focus_wrapping' -> FORCE_FOCUS_WRAPPING
@ -150,6 +151,15 @@ state ASSIGN_WORKSPACE:
workspace = string workspace = string
-> call cfg_assign($workspace) -> call cfg_assign($workspace)
# no_focus <criteria>
state NO_FOCUS:
'['
-> call cfg_criteria_init(NO_FOCUS_END); CRITERIA
state NO_FOCUS_END:
end
-> call cfg_no_focus()
# Criteria: Used by for_window and assign. # Criteria: Used by for_window and assign.
state CRITERIA: state CRITERIA:
ctype = 'class' -> CRITERION ctype = 'class' -> CRITERION

View File

@ -431,6 +431,19 @@ CFGFUN(assign, const char *workspace) {
TAILQ_INSERT_TAIL(&assignments, assignment, assignments); TAILQ_INSERT_TAIL(&assignments, assignment, assignments);
} }
CFGFUN(no_focus) {
if (match_is_empty(current_match)) {
ELOG("Match is empty, ignoring this assignment\n");
return;
}
DLOG("new assignment, using above criteria, to ignore focus on manage");
Assignment *assignment = scalloc(sizeof(Assignment));
match_copy(&(assignment->match), current_match);
assignment->type = A_NO_FOCUS;
TAILQ_INSERT_TAIL(&assignments, assignment, assignments);
}
/******************************************************************************* /*******************************************************************************
* Bar configuration (i3bar) * Bar configuration (i3bar)
******************************************************************************/ ******************************************************************************/

View File

@ -512,8 +512,10 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
/* Defer setting focus after the 'new' event has been sent to ensure the /* Defer setting focus after the 'new' event has been sent to ensure the
* proper window event sequence. */ * proper window event sequence. */
if (set_focus && !nc->window->doesnt_accept_focus && nc->mapped) { if (set_focus && !nc->window->doesnt_accept_focus && nc->mapped) {
DLOG("Now setting focus.\n"); if (assignment_for(cwindow, A_NO_FOCUS) == NULL) {
con_focus(nc); DLOG("Now setting focus.\n");
con_focus(nc);
}
} }
tree_render(); tree_render();

View File

@ -433,7 +433,7 @@ client.focused #4c7899 #285577 #ffffff #2e9ef4
EOT EOT
my $expected_all_tokens = <<'EOT'; my $expected_all_tokens = <<'EOT';
ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'bindsym', 'bindcode', 'bind', 'bar', 'font', 'mode', 'floating_minimum_size', 'floating_maximum_size', 'floating_modifier', 'default_orientation', 'workspace_layout', 'new_window', 'new_float', 'hide_edge_borders', 'for_window', 'assign', 'focus_follows_mouse', 'mouse_warping', 'force_focus_wrapping', 'force_xinerama', 'force-xinerama', 'workspace_auto_back_and_forth', 'fake_outputs', 'fake-outputs', 'force_display_urgency_hint', 'focus_on_window_activation', 'show_marks', 'workspace', 'ipc_socket', 'ipc-socket', 'restart_state', 'popup_during_fullscreen', 'exec_always', 'exec', 'client.background', 'client.focused_inactive', 'client.focused', 'client.unfocused', 'client.urgent', 'client.placeholder' ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'bindsym', 'bindcode', 'bind', 'bar', 'font', 'mode', 'floating_minimum_size', 'floating_maximum_size', 'floating_modifier', 'default_orientation', 'workspace_layout', 'new_window', 'new_float', 'hide_edge_borders', 'for_window', 'assign', 'no_focus', 'focus_follows_mouse', 'mouse_warping', 'force_focus_wrapping', 'force_xinerama', 'force-xinerama', 'workspace_auto_back_and_forth', 'fake_outputs', 'fake-outputs', 'force_display_urgency_hint', 'focus_on_window_activation', 'show_marks', 'workspace', 'ipc_socket', 'ipc-socket', 'restart_state', 'popup_during_fullscreen', 'exec_always', 'exec', 'client.background', 'client.focused_inactive', 'client.focused', 'client.unfocused', 'client.urgent', 'client.placeholder'
EOT EOT
my $expected_end = <<'EOT'; my $expected_end = <<'EOT';

View File

@ -0,0 +1,66 @@
#!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)
#
# Test the 'no_focus' directive.
# Ticket: #1416
use i3test i3_autostart => 0;
#####################################################################
# 1: open a window and check that it takes focus
#####################################################################
my $config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
EOT
my $pid = launch_with_config($config);
my $ws = fresh_workspace;
my $first = open_window;
my $focused = get_focused($ws);
my $second = open_window;
isnt(get_focused($ws), $focused, 'focus has changed');
exit_gracefully($pid);
#####################################################################
# 2: open a window matched by a no_focus directive and check that
# it doesn't take focus
#####################################################################
my $config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
no_focus [instance=notme]
EOT
my $pid = launch_with_config($config);
my $ws = fresh_workspace;
my $first = open_window;
my $focused = get_focused($ws);
my $second = open_window(wm_class => 'notme');
is(get_focused($ws), $focused, 'focus has not changed');
exit_gracefully($pid);
#####################################################################
done_testing;