Add a timeout: delay_exit_on_zero_displays

Outputs may disappear momentarily and come back later.
To prevent i3 from exit when no output is available momentarily, add a timeout delay_exit_on_zero_displays.
next
hwangcc 2015-04-03 16:16:38 +08:00
parent 1546891e6a
commit 2c77d7ceed
9 changed files with 124 additions and 9 deletions

View File

@ -1003,6 +1003,31 @@ force_display_urgency_hint <timeout> ms
force_display_urgency_hint 500 ms
---------------------------------
=== Delaying exiting on zero displays
Outputs may disappear momentarily and come back later. For example,
using a docking station that does not announce the undock (e.g. ACPI Undock
event triggered through manually pushing a button before actually ejecting
the notebook). During the removal of the notebook from the docking station,
all outputs disappear momentarily.
To prevent i3 from exiting when no output is available momentarily, you can
tell i3 to delay a certain time first and check available outputs again using
the +delay_exit_on_zero_displays+ directive. Setting the value to 0 disables
this feature.
The default is 500ms.
*Syntax*:
----------------------------------------
delay_exit_on_zero_displays <timeout> ms
----------------------------------------
*Example*:
----------------------------------
delay_exit_on_zero_displays 500 ms
----------------------------------
=== Focus on window activation
[[focus_on_window_activation]]

View File

@ -167,6 +167,10 @@ struct Config {
* flag can be delayed using an urgency timer. */
float workspace_urgency_timer;
/** Use a timer to delay exiting when no output is available.
* This can prevent i3 from exiting when all outputs disappear momentarily. */
float zero_disp_exit_timer_ms;
/** Behavior when a window sends a NET_ACTIVE_WINDOW message. */
enum {
/* Focus if the target workspace is visible, set urgency hint otherwise. */

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(delay_exit_on_zero_displays, const long duration_ms);
CFGFUN(focus_on_window_activation, const char *mode);
CFGFUN(show_marks, const char *value);
CFGFUN(hide_edge_borders, const char *borders);

View File

@ -39,6 +39,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
'delay_exit_on_zero_displays' -> DELAY_EXIT_ON_ZERO_DISPLAYS
'focus_on_window_activation' -> FOCUS_ON_WINDOW_ACTIVATION
'show_marks' -> SHOW_MARKS
'workspace' -> WORKSPACE
@ -227,6 +228,17 @@ state FORCE_DISPLAY_URGENCY_HINT_MS:
end
-> call cfg_force_display_urgency_hint(&duration_ms)
# delay_exit_on_zero_displays <delay> ms
state DELAY_EXIT_ON_ZERO_DISPLAYS:
duration_ms = number
-> DELAY_EXIT_ON_ZERO_DISPLAYS_MS
state DELAY_EXIT_ON_ZERO_DISPLAYS_MS:
'ms'
->
end
-> call cfg_delay_exit_on_zero_displays(&duration_ms)
# focus_on_window_activation <smart|urgent|focus|none>
state FOCUS_ON_WINDOW_ACTIVATION:
mode = word

View File

@ -203,6 +203,10 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
if (config.workspace_urgency_timer == 0)
config.workspace_urgency_timer = 0.5;
/* Set default zero displays exit delay to 500ms */
if (config.zero_disp_exit_timer_ms == 0)
config.zero_disp_exit_timer_ms = 500;
parse_configuration(override_configpath, true);
if (reload) {

View File

@ -329,6 +329,10 @@ CFGFUN(force_display_urgency_hint, const long duration_ms) {
config.workspace_urgency_timer = duration_ms / 1000.0;
}
CFGFUN(delay_exit_on_zero_displays, const long duration_ms) {
config.zero_disp_exit_timer_ms = duration_ms;
}
CFGFUN(focus_on_window_activation, const char *mode) {
if (strcmp(mode, "smart") == 0)
config.focus_on_window_activation = FOWA_SMART;

View File

@ -621,6 +621,8 @@ int main(int argc, char *argv[]) {
ELOG("ERROR: No screen at (%d, %d), starting on the first screen\n",
pointerreply->root_x, pointerreply->root_y);
output = get_first_output();
if (!output)
die("No usable outputs available.\n");
}
con_focus(con_descend_focused(output_get_content(output->con)));

View File

@ -69,7 +69,7 @@ Output *get_first_output(void) {
if (output->active)
return output;
die("No usable outputs available.\n");
return NULL;
}
/*
@ -564,6 +564,8 @@ static void handle_output(xcb_connection_t *conn, xcb_randr_output_t id,
if (!new->active) {
DLOG("width/height 0/0, disabling output\n");
return;
} else {
new->to_be_disabled = false;
}
DLOG("mode: %dx%d+%d+%d\n", new->rect.width, new->rect.height,
@ -585,11 +587,7 @@ static void handle_output(xcb_connection_t *conn, xcb_randr_output_t id,
new->changed = true;
}
/*
* (Re-)queries the outputs via RandR and stores them in the list of outputs.
*
*/
void randr_query_outputs(void) {
static bool __randr_query_outputs(void) {
Output *output, *other, *first;
xcb_randr_get_output_primary_cookie_t pcookie;
xcb_randr_get_screen_resources_current_cookie_t rcookie;
@ -603,7 +601,7 @@ void randr_query_outputs(void) {
xcb_randr_output_t *randr_outputs;
if (randr_disabled)
return;
return true;
/* Get screen resources (primary output, crtcs, outputs, modes) */
rcookie = xcb_randr_get_screen_resources_current(conn, root);
@ -615,7 +613,7 @@ void randr_query_outputs(void) {
DLOG("primary output is %08x\n", primary->output);
if ((res = xcb_randr_get_screen_resources_current_reply(conn, rcookie, NULL)) == NULL) {
disable_randr(conn);
return;
return true;
}
cts = res->config_timestamp;
@ -697,6 +695,11 @@ void randr_query_outputs(void) {
DLOG("Output %s disabled, re-assigning workspaces/docks\n", output->name);
first = get_first_output();
if (!first) {
FREE(res);
FREE(primary);
return false;
}
/* TODO: refactor the following code into a nice function. maybe
* use an on_destroy callback which is implement differently for
@ -812,6 +815,32 @@ void randr_query_outputs(void) {
FREE(res);
FREE(primary);
return true;
}
/*
* (Re-)queries the outputs via RandR and stores them in the list of outputs.
*
*/
void randr_query_outputs(void) {
static bool first_query = true;
if (first_query) {
/* find monitors at least once via RandR */
if (!__randr_query_outputs())
die("No usable outputs available.\n");
first_query = false;
} else {
/* requery */
if (!__randr_query_outputs()) {
DLOG("sleep %f ms due to zero displays\n", config.zero_disp_exit_timer_ms);
usleep(config.zero_disp_exit_timer_ms * 1000);
if (!__randr_query_outputs())
die("No usable outputs available.\n");
}
}
}
/*

View File

@ -365,6 +365,40 @@ is(parser_calls($config),
$expected,
'force_display_urgency_hint ok');
################################################################################
# delay_exit_on_zero_displays
################################################################################
is(parser_calls('delay_exit_on_zero_displays 300'),
"cfg_delay_exit_on_zero_displays(300)\n",
'delay_exit_on_zero_displays ok');
is(parser_calls('delay_exit_on_zero_displays 500 ms'),
"cfg_delay_exit_on_zero_displays(500)\n",
'delay_exit_on_zero_displays ok');
is(parser_calls('delay_exit_on_zero_displays 700ms'),
"cfg_delay_exit_on_zero_displays(700)\n",
'delay_exit_on_zero_displays ok');
$config = <<'EOT';
delay_exit_on_zero_displays 300
delay_exit_on_zero_displays 500 ms
delay_exit_on_zero_displays 700ms
delay_exit_on_zero_displays 700
EOT
$expected = <<'EOT';
cfg_delay_exit_on_zero_displays(300)
cfg_delay_exit_on_zero_displays(500)
cfg_delay_exit_on_zero_displays(700)
cfg_delay_exit_on_zero_displays(700)
EOT
is(parser_calls($config),
$expected,
'delay_exit_on_zero_displays ok');
################################################################################
# workspace
################################################################################
@ -437,7 +471,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', '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'
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', 'delay_exit_on_zero_displays', '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';