Merge pull request #1613 from Airblader/feature-1426

Added focus_on_window_activation directive
This commit is contained in:
Michael Stapelberg 2015-03-30 22:58:16 +02:00
commit 0ad097ee67
8 changed files with 276 additions and 8 deletions

View File

@ -981,6 +981,31 @@ force_display_urgency_hint <timeout> ms
force_display_urgency_hint 500 ms
---------------------------------
=== 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.
*Syntax*:
----------------------------------------------------
focus_on_window_activation <smart|urgent|focus|none>
----------------------------------------------------
The different modes will act as follows:
smart::
This is the default behavior. If the window requesting focus is on an active
workspace, it will receive the focus. Otherwise, the urgency hint will be set.
urgent::
The window will always be marked urgent, but the focus will not be stolen.
focus::
The window will always be focused and not be marked urgent.
none::
The window will neither be focused, nor be marked urgent.
== Configuring i3bar
The bar at the bottom of your monitor is drawn by a separate process called

View File

@ -167,6 +167,18 @@ struct Config {
* flag can be delayed using an urgency timer. */
float workspace_urgency_timer;
/** Behavior when a window sends a NET_ACTIVE_WINDOW message. */
enum {
/* Focus if the target workspace is visible, set urgency hint otherwise. */
FOWA_SMART,
/* Always set the urgency hint. */
FOWA_URGENT,
/* Always focus the window. */
FOWA_FOCUS,
/* Ignore the request (no focus, no urgency hint). */
FOWA_NONE
} focus_on_window_activation;
/** The default border style for new windows. */
border_style_t default_border;

View File

@ -51,6 +51,7 @@ CFGFUN(force_focus_wrapping, const char *value);
CFGFUN(force_xinerama, const char *value);
CFGFUN(fake_outputs, const char *outputs);
CFGFUN(force_display_urgency_hint, const long duration_ms);
CFGFUN(focus_on_window_activation, const char *mode);
CFGFUN(hide_edge_borders, const char *borders);
CFGFUN(assign, const char *workspace);
CFGFUN(ipc_socket, const char *path);

View File

@ -38,6 +38,7 @@ state INITIAL:
'workspace_auto_back_and_forth' -> WORKSPACE_BACK_AND_FORTH
'fake_outputs', 'fake-outputs' -> FAKE_OUTPUTS
'force_display_urgency_hint' -> FORCE_DISPLAY_URGENCY_HINT
'focus_on_window_activation' -> FOCUS_ON_WINDOW_ACTIVATION
'workspace' -> WORKSPACE
'ipc_socket', 'ipc-socket' -> IPC_SOCKET
'restart_state' -> RESTART_STATE
@ -210,6 +211,11 @@ state FORCE_DISPLAY_URGENCY_HINT_MS:
end
-> call cfg_force_display_urgency_hint(&duration_ms)
# focus_on_window_activation <smart|urgent|focus|none>
state FOCUS_ON_WINDOW_ACTIVATION:
mode = word
-> call cfg_focus_on_window_activation($mode)
# workspace <workspace> output <output>
state WORKSPACE:
workspace = word

View File

@ -329,6 +329,23 @@ CFGFUN(force_display_urgency_hint, const long duration_ms) {
config.workspace_urgency_timer = duration_ms / 1000.0;
}
CFGFUN(focus_on_window_activation, const char *mode) {
if (strcmp(mode, "smart") == 0)
config.focus_on_window_activation = FOWA_SMART;
else if (strcmp(mode, "urgent") == 0)
config.focus_on_window_activation = FOWA_URGENT;
else if (strcmp(mode, "focus") == 0)
config.focus_on_window_activation = FOWA_FOCUS;
else if (strcmp(mode, "none") == 0)
config.focus_on_window_activation = FOWA_NONE;
else {
ELOG("Unknown focus_on_window_activation mode \"%s\", ignoring it.\n", mode);
return;
}
DLOG("Set new focus_on_window_activation mode = %i", config.focus_on_window_activation);
}
CFGFUN(workspace, const char *workspace, const char *output) {
DLOG("Assigning workspace \"%s\" to output \"%s\"\n", workspace, output);
/* Check for earlier assignments of the same workspace so that we

View File

@ -743,16 +743,17 @@ static void handle_client_message(xcb_client_message_event_t *event) {
workspace_show(ws);
con_focus(con);
} else {
/* If the request is from an application, only focus if the
* workspace is visible. Otherwise set the urgency hint. */
if (workspace_is_visible(ws)) {
DLOG("Request to focus con on a visible workspace. Focusing con = %p\n", con);
/* Request is from an application. */
if (config.focus_on_window_activation == FOWA_FOCUS || (config.focus_on_window_activation == FOWA_SMART && workspace_is_visible(ws))) {
DLOG("Focusing con = %p\n", con);
workspace_show(ws);
con_focus(con);
} else {
DLOG("Request to focus con on a hidden workspace. Setting urgent con = %p\n", con);
} else if (config.focus_on_window_activation == FOWA_URGENT || (config.focus_on_window_activation == FOWA_SMART && !workspace_is_visible(ws))) {
DLOG("Marking con = %p urgent\n", con);
con_set_urgency(con, true);
}
} else
DLOG("Ignoring request for con = %p", con);
}
tree_render();

View File

@ -433,7 +433,7 @@ client.focused #4c7899 #285577 #ffffff #2e9ef4
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', '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', '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', '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';

View File

@ -0,0 +1,206 @@
#!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 for the focus_on_window_activation directive
# Ticket: #1426
use i3test i3_autostart => 0;
use List::Util qw(first);
sub send_net_active_window {
my ($id) = @_;
my $msg = pack "CCSLLLLLLL",
X11::XCB::CLIENT_MESSAGE, # response_type
32, # format
0, # sequence
$id, # destination window
$x->atom(name => '_NET_ACTIVE_WINDOW')->id,
0, # source
0, 0, 0, 0;
$x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
}
sub get_urgency_for_window_on_workspace {
my ($ws, $con) = @_;
my $current = first { $_->{window} == $con->{id} } @{get_ws_content($ws)};
return $current->{urgent};
}
#####################################################################
# 1: If mode is set to 'urgent' and the target workspace is visible,
# check that the urgent flag is set and focus is not lost.
#####################################################################
my $config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
focus_on_window_activation urgent
EOT
my $pid = launch_with_config($config);
my $ws = fresh_workspace;
my $first = open_window;
my $second = open_window;
send_net_active_window($first->id);
sync_with_i3;
is($x->input_focus, $second->id, 'second window is still focused');
is(get_urgency_for_window_on_workspace($ws, $first), 1, 'first window is marked urgent');
exit_gracefully($pid);
#####################################################################
# 2: If mode is set to 'urgent' and the target workspace is not
# visible, check that the urgent flag is set and focus is not lost.
#####################################################################
my $config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
focus_on_window_activation urgent
EOT
my $pid = launch_with_config($config);
my $ws1 = fresh_workspace;
my $first = open_window;
my $ws2 = fresh_workspace;
my $second = open_window;
send_net_active_window($first->id);
sync_with_i3;
is(focused_ws(), $ws2, 'second workspace is still focused');
is($x->input_focus, $second->id, 'second window is still focused');
is(get_urgency_for_window_on_workspace($ws1, $first), 1, 'first window is marked urgent');
exit_gracefully($pid);
#####################################################################
# 3: If mode is set to 'focus' and the target workspace is visible,
# check that the focus is switched.
#####################################################################
my $config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
focus_on_window_activation focus
EOT
my $pid = launch_with_config($config);
my $ws = fresh_workspace;
my $first = open_window;
my $second = open_window;
send_net_active_window($first->id);
sync_with_i3;
is($x->input_focus, $first->id, 'first window is now focused');
ok(!get_urgency_for_window_on_workspace($ws, $first), 'first window is not marked urgent');
exit_gracefully($pid);
#####################################################################
# 4: If mode is set to 'focus' and the target workspace is not
# visible, check that the focus switched.
#####################################################################
my $config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
focus_on_window_activation focus
EOT
my $pid = launch_with_config($config);
my $ws1 = fresh_workspace;
my $first = open_window;
my $ws2 = fresh_workspace;
my $second = open_window;
send_net_active_window($first->id);
sync_with_i3;
is(focused_ws(), $ws1, 'first workspace is now focused');
is($x->input_focus, $first->id, 'first window is now focused');
ok(!get_urgency_for_window_on_workspace($ws1, $first), 'first window is not marked urgent');
exit_gracefully($pid);
#####################################################################
# 5: If mode is set to 'none' and the target workspace is visible,
# check that nothing happens.
#####################################################################
my $config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
focus_on_window_activation none
EOT
my $pid = launch_with_config($config);
my $ws = fresh_workspace;
my $first = open_window;
my $second = open_window;
send_net_active_window($first->id);
sync_with_i3;
is($x->input_focus, $second->id, 'second window is still focused');
ok(!get_urgency_for_window_on_workspace($ws, $first), 'first window is not marked urgent');
exit_gracefully($pid);
#####################################################################
# 6: If mode is set to 'none' and the target workspace is not
# visible, check that nothing happens.
#####################################################################
my $config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
focus_on_window_activation none
EOT
my $pid = launch_with_config($config);
my $ws1 = fresh_workspace;
my $first = open_window;
my $ws2 = fresh_workspace;
my $second = open_window;
send_net_active_window($first->id);
sync_with_i3;
is(focused_ws(), $ws2, 'second workspace is still focused');
is($x->input_focus, $second->id, 'second window is still focused');
ok(!get_urgency_for_window_on_workspace($ws1, $first), 'first window is not marked urgent');
exit_gracefully($pid);
done_testing;