implement delayed urgency hint reset

If there is a client with an urgency hint on another workspace and
switching to this workspace would cause the urgency to be reset (by
moving the focusing to the client), delay the reset by some time. This
gives the user the chance to see it.

This commit adds the possibility to configure the urgency delay timer
duration using the 'force_display_urgency_hint' directive. Also,
documentation and a testcase was added to allow for automated checks of
the intended behavior.

fixes #482
This commit is contained in:
Simon Elsbrock
2012-09-22 13:48:22 +02:00
committed by Michael Stapelberg
parent e15e37f922
commit 28104a480c
10 changed files with 221 additions and 5 deletions

View File

@ -212,6 +212,8 @@ force-xinerama { return TOK_FORCE_XINERAMA; }
fake_outputs { WS_STRING; return TOK_FAKE_OUTPUTS; }
fake-outputs { WS_STRING; return TOK_FAKE_OUTPUTS; }
workspace_auto_back_and_forth { return TOK_WORKSPACE_AUTO_BAF; }
force_display_urgency_hint { return TOK_WORKSPACE_URGENCY_TIMER; }
ms { return TOK_TIME_MS; }
workspace_bar { return TOKWORKSPACEBAR; }
popup_during_fullscreen { return TOK_POPUP_DURING_FULLSCREEN; }
ignore { return TOK_IGNORE; }

View File

@ -728,6 +728,7 @@ void parse_file(const char *f) {
%token <color> TOKCOLOR
%token TOKARROW "→"
%token TOKMODE "mode"
%token TOK_TIME_MS "ms"
%token TOK_BAR "bar"
%token TOK_ORIENTATION "default_orientation"
%token TOK_HORIZ "horizontal"
@ -746,6 +747,7 @@ void parse_file(const char *f) {
%token TOK_FORCE_XINERAMA "force_xinerama"
%token TOK_FAKE_OUTPUTS "fake_outputs"
%token TOK_WORKSPACE_AUTO_BAF "workspace_auto_back_and_forth"
%token TOK_WORKSPACE_URGENCY_TIMER "force_display_urgency_hint"
%token TOKWORKSPACEBAR "workspace_bar"
%token TOK_DEFAULT "default"
%token TOK_STACKING "stacking"
@ -819,6 +821,7 @@ void parse_file(const char *f) {
%type <number> optional_release
%type <string> command
%type <string> word_or_number
%type <string> duration
%type <string> qstring_or_number
%type <string> optional_workspace_name
%type <string> workspace_name
@ -848,6 +851,7 @@ line:
| force_focus_wrapping
| force_xinerama
| fake_outputs
| force_display_urgency_hint
| workspace_back_and_forth
| workspace_bar
| workspace
@ -1052,6 +1056,10 @@ word_or_number:
}
;
duration:
NUMBER TOK_TIME_MS { sasprintf(&$$, "%d", $1); }
;
mode:
TOKMODE QUOTEDSTRING '{' modelines '}'
{
@ -1548,6 +1556,14 @@ workspace_back_and_forth:
}
;
force_display_urgency_hint:
TOK_WORKSPACE_URGENCY_TIMER duration
{
DLOG("workspace urgency_timer = %f\n", atoi($2) / 1000.0);
config.workspace_urgency_timer = atoi($2) / 1000.0;
}
;
workspace_bar:
TOKWORKSPACEBAR bool
{

View File

@ -420,6 +420,10 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
/* Set default_orientation to NO_ORIENTATION for auto orientation. */
config.default_orientation = NO_ORIENTATION;
/* Set default urgency reset delay to 500ms */
if (config.workspace_urgency_timer == 0)
config.workspace_urgency_timer = 0.5;
parse_configuration(override_configpath);
if (reload) {

View File

@ -836,7 +836,13 @@ static bool handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_
}
/* Update the flag on the client directly */
con->urgent = (xcb_icccm_wm_hints_get_urgency(&hints) != 0);
bool hint_urgent = (xcb_icccm_wm_hints_get_urgency(&hints) != 0);
if (con->urgency_timer == NULL) {
con->urgent = hint_urgent;
} else
DLOG("Discarding urgency WM_HINT because timer is running\n");
//CLIENT_LOG(con);
if (con->window) {
if (con->urgent) {

View File

@ -255,6 +255,15 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool
x_con_kill(con);
con_detach(con);
/* disable urgency timer, if needed */
if (con->urgency_timer != NULL) {
DLOG("Removing urgency timer of con %p\n", con);
workspace_update_urgent_flag(con_get_workspace(con));
ev_timer_stop(main_loop, con->urgency_timer);
FREE(con->urgency_timer);
}
if (con->type != CT_FLOATING_CON) {
/* If the container is *not* floating, we might need to re-distribute
* percentage values for the resized containers. */

View File

@ -311,6 +311,23 @@ static void workspace_reassign_sticky(Con *con) {
workspace_reassign_sticky(current);
}
/*
* Callback to reset the urgent flag of the given con to false. May be started by
* _workspace_show to avoid urgency hints being lost by switching to a workspace
* focusing the con.
*
*/
static void workspace_defer_update_urgent_hint_cb(EV_P_ ev_timer *w, int revents) {
Con *con = w->data;
DLOG("Resetting urgency flag of con %p by timer\n", con);
con->urgent = false;
workspace_update_urgent_flag(con_get_workspace(con));
tree_render();
ev_timer_stop(main_loop, con->urgency_timer);
FREE(con->urgency_timer);
}
static void _workspace_show(Con *workspace) {
Con *current, *old = NULL;
@ -350,6 +367,43 @@ static void _workspace_show(Con *workspace) {
LOG("switching to %p\n", workspace);
Con *next = con_descend_focused(workspace);
/* Memorize current output */
Con *old_output = con_get_output(focused);
/* Display urgency hint for a while if the newly visible workspace would
* focus and thereby immediately destroy it */
if (next->urgent && (int)(config.workspace_urgency_timer * 1000) > 0) {
/* focus for now… */
con_focus(next);
/* … but immediately reset urgency flags; they will be set to false by
* the timer callback in case the container is focused at the time of
* its expiration */
focused->urgent = true;
workspace->urgent = true;
if (focused->urgency_timer == NULL) {
DLOG("Deferring reset of urgency flag of con %p on newly shown workspace %p\n",
focused, workspace);
focused->urgency_timer = scalloc(sizeof(struct ev_timer));
/* use a repeating timer to allow for easy resets */
ev_timer_init(focused->urgency_timer, workspace_defer_update_urgent_hint_cb,
config.workspace_urgency_timer, config.workspace_urgency_timer);
focused->urgency_timer->data = focused;
ev_timer_start(main_loop, focused->urgency_timer);
} else {
DLOG("Resetting urgency timer of con %p on workspace %p\n",
focused, workspace);
ev_timer_again(main_loop, focused->urgency_timer);
}
} else
con_focus(next);
/* Close old workspace if necessary. This must be done *after* doing
* urgency handling, because tree_close() will do a con_focus() on the next
* client, which will clear the urgency flag too early. Also, there is no
* way for con_focus() to know about when to clear urgency immediately and
* when to defer it. */
if (old && TAILQ_EMPTY(&(old->nodes_head)) && TAILQ_EMPTY(&(old->floating_head))) {
/* check if this workspace is currently visible */
if (!workspace_is_visible(old)) {
@ -359,10 +413,6 @@ static void _workspace_show(Con *workspace) {
}
}
/* Memorize current output */
Con *old_output = con_get_output(focused);
con_focus(next);
workspace->fullscreen_mode = CF_OUTPUT;
LOG("focused now = %p / %s\n", focused, focused->name);