diff --git a/docs/userguide b/docs/userguide index 2e42493c..98629761 100644 --- a/docs/userguide +++ b/docs/userguide @@ -589,6 +589,27 @@ for_window [title="x200: ~/work"] floating enable The valid criteria are the same as those for commands, see <>. +=== 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 <>. + +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 +<>. + +*Syntax*: +------------------- +no_focus +------------------- + +*Example*: +------------------------------- +no_focus [window_role="pop-up"] +------------------------------- + === Variables 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]] + 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. -Note that this does not apply to any window that is opened, i.e., typically opening -an application will focus that window independent of this configuration. +Note that this may not affect windows that are being opened. To prevent new windows +from being focused, see <>. *Syntax*: ---------------------------------------------------- diff --git a/include/config_directives.h b/include/config_directives.h index b9189b2c..f5039624 100644 --- a/include/config_directives.h +++ b/include/config_directives.h @@ -55,6 +55,7 @@ CFGFUN(focus_on_window_activation, const char *mode); CFGFUN(show_marks, const char *value); CFGFUN(hide_edge_borders, const char *borders); CFGFUN(assign, const char *workspace); +CFGFUN(no_focus); CFGFUN(ipc_socket, const char *path); CFGFUN(restart_state, const char *path); CFGFUN(popup_during_fullscreen, const char *value); diff --git a/include/data.h b/include/data.h index d40ed292..50e1f180 100644 --- a/include/data.h +++ b/include/data.h @@ -460,6 +460,7 @@ struct Assignment { * * A_COMMAND = run the specified command for the matching window * 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 * a bitmask to allow filtering for multiple types, for example in the @@ -469,7 +470,8 @@ struct Assignment { enum { A_ANY = 0, A_COMMAND = (1 << 0), - A_TO_WORKSPACE = (1 << 1) + A_TO_WORKSPACE = (1 << 1), + A_NO_FOCUS = (1 << 2) } type; /** the criteria to check if a window matches */ diff --git a/parser-specs/config.spec b/parser-specs/config.spec index 81357206..433e1d11 100644 --- a/parser-specs/config.spec +++ b/parser-specs/config.spec @@ -31,6 +31,7 @@ state INITIAL: 'hide_edge_borders' -> HIDE_EDGE_BORDERS 'for_window' -> FOR_WINDOW 'assign' -> ASSIGN + 'no_focus' -> NO_FOCUS 'focus_follows_mouse' -> FOCUS_FOLLOWS_MOUSE 'mouse_warping' -> MOUSE_WARPING 'force_focus_wrapping' -> FORCE_FOCUS_WRAPPING @@ -150,6 +151,15 @@ state ASSIGN_WORKSPACE: workspace = string -> call cfg_assign($workspace) +# no_focus +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. state CRITERIA: ctype = 'class' -> CRITERION diff --git a/src/config_directives.c b/src/config_directives.c index eddfaa3d..398e53bb 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -431,6 +431,19 @@ CFGFUN(assign, const char *workspace) { 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) ******************************************************************************/ diff --git a/src/manage.c b/src/manage.c index 2b3c6743..3499963b 100644 --- a/src/manage.c +++ b/src/manage.c @@ -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 * proper window event sequence. */ if (set_focus && !nc->window->doesnt_accept_focus && nc->mapped) { - DLOG("Now setting focus.\n"); - con_focus(nc); + if (assignment_for(cwindow, A_NO_FOCUS) == NULL) { + DLOG("Now setting focus.\n"); + con_focus(nc); + } } tree_render(); diff --git a/testcases/t/201-config-parser.t b/testcases/t/201-config-parser.t index 3314aaf7..0f22878d 100644 --- a/testcases/t/201-config-parser.t +++ b/testcases/t/201-config-parser.t @@ -433,7 +433,7 @@ client.focused #4c7899 #285577 #ffffff #2e9ef4 EOT my $expected_all_tokens = <<'EOT'; -ERROR: CONFIG: Expected one of these tokens: , '#', '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: , '#', '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 my $expected_end = <<'EOT'; diff --git a/testcases/t/242-no-focus.t b/testcases/t/242-no-focus.t new file mode 100644 index 00000000..b3983436 --- /dev/null +++ b/testcases/t/242-no-focus.t @@ -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 = < 'notme'); + +is(get_focused($ws), $focused, 'focus has not changed'); + +exit_gracefully($pid); + +##################################################################### + +done_testing;