From 32be3af10966addeb8c5b84e5364d9f189e6a90c Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 2 Jun 2010 17:51:58 +0200 Subject: [PATCH] Re-implement support for the urgency hint, extend t/13-urgent.t The actual rendering will follow --- include/data.h | 4 +++ include/handlers.h | 2 ++ include/workspace.h | 4 ++- src/con.c | 4 +++ src/handlers.c | 78 ++++++++++++++++++++++------------------- src/ipc.c | 3 ++ src/nc.c | 4 +++ src/workspace.c | 38 +++++++++++--------- testcases/t/13-urgent.t | 75 ++++++++++++++++++++++++++++----------- 9 files changed, 137 insertions(+), 75 deletions(-) diff --git a/include/data.h b/include/data.h index def5d4b7..f74f0d52 100644 --- a/include/data.h +++ b/include/data.h @@ -272,6 +272,10 @@ struct Con { struct Window *window; + /* Should this container be marked urgent? This gets set when the window + * inside this container (if any) sets the urgency hint, for example. */ + bool urgent; + /* ids/gc for the frame window */ xcb_window_t frame; xcb_gcontext_t gc; diff --git a/include/handlers.h b/include/handlers.h index 12d64c48..73cafe4b 100644 --- a/include/handlers.h +++ b/include/handlers.h @@ -171,12 +171,14 @@ int handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, xcb_atom_t name, xcb_get_property_reply_t *reply); +#endif /** * Handles the WM_HINTS property for extracting the urgency state of the window. * */ int handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, xcb_atom_t name, xcb_get_property_reply_t *reply); +#if 0 /** * Handles the transient for hints set by a window, signalizing that this diff --git a/include/workspace.h b/include/workspace.h index 7a61daa8..28b7e571 100644 --- a/include/workspace.h +++ b/include/workspace.h @@ -90,14 +90,16 @@ void workspace_unmap_clients(xcb_connection_t *conn, Workspace *u_ws); * */ void workspace_map_clients(xcb_connection_t *conn, Workspace *ws); +#endif /** * Goes through all clients on the given workspace and updates the workspace’s * urgent flag accordingly. * */ -void workspace_update_urgent_flag(Workspace *ws); +void workspace_update_urgent_flag(Con *ws); +#if 0 /* * Returns the width of the workspace. * diff --git a/src/con.c b/src/con.c index 411c8df6..19227708 100644 --- a/src/con.c +++ b/src/con.c @@ -91,6 +91,10 @@ void con_focus(Con *con) { con_focus(con->parent); focused = con; + if (con->urgent) { + con->urgent = false; + workspace_update_urgent_flag(con_get_workspace(con)); + } } /* diff --git a/src/handlers.c b/src/handlers.c index 7613110c..990f7dd2 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -810,6 +810,7 @@ int handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_w return 1; } +#endif /* * Handles the WM_HINTS property for extracting the urgency state of the window. @@ -817,46 +818,49 @@ int handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_w */ int handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, xcb_atom_t name, xcb_get_property_reply_t *reply) { - Client *client = table_get(&by_child, window); - if (client == NULL) { - DLOG("Received WM_HINTS for unknown client\n"); - return 1; - } - xcb_wm_hints_t hints; - - if (reply != NULL) { - if (!xcb_get_wm_hints_from_reply(&hints, reply)) - return 1; - } else { - if (!xcb_get_wm_hints_reply(conn, xcb_get_wm_hints_unchecked(conn, client->child), &hints, NULL)) - return 1; - } - - Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack)); - if (!client->urgent && client == last_focused) { - DLOG("Ignoring urgency flag for current client\n"); - return 1; - } - - /* Update the flag on the client directly */ - client->urgent = (xcb_wm_hints_get_urgency(&hints) != 0); - CLIENT_LOG(client); - LOG("Urgency flag changed to %d\n", client->urgent); - - workspace_update_urgent_flag(client->workspace); - redecorate_window(conn, client); - - /* If the workspace this client is on is not visible, we need to redraw - * the workspace bar */ - if (!workspace_is_visible(client->workspace)) { - Output *output = client->workspace->output; - render_workspace(conn, output, output->current_workspace); - xcb_flush(conn); - } - + Con *con = con_by_window_id(window); + if (con == NULL) { + DLOG("Received WM_HINTS for unknown client\n"); return 1; + } + + xcb_wm_hints_t hints; + + if (reply != NULL) { + if (!xcb_get_wm_hints_from_reply(&hints, reply)) + return 1; + } else { + if (!xcb_get_wm_hints_reply(conn, xcb_get_wm_hints_unchecked(conn, con->window->id), &hints, NULL)) + return 1; + } + + if (!con->urgent && focused == con) { + DLOG("Ignoring urgency flag for current client\n"); + return 1; + } + + /* Update the flag on the client directly */ + con->urgent = (xcb_wm_hints_get_urgency(&hints) != 0); + //CLIENT_LOG(con); + LOG("Urgency flag changed to %d\n", con->urgent); + + workspace_update_urgent_flag(con_get_workspace(con)); + +#if 0 + /* If the workspace this client is on is not visible, we need to redraw + * the workspace bar */ + if (!workspace_is_visible(client->workspace)) { + Output *output = client->workspace->output; + render_workspace(conn, output, output->current_workspace); + xcb_flush(conn); + } +#endif + + return 1; } +#if 0 + /* * Handles the transient for hints set by a window, signalizing that this window is a popup window * for some other window. diff --git a/src/ipc.c b/src/ipc.c index 1ca0778b..bde24852 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -137,6 +137,9 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { ystr("orientation"); y(integer, con->orientation); + ystr("urgent"); + y(integer, con->urgent); + ystr("layout"); y(integer, con->layout); diff --git a/src/nc.c b/src/nc.c index ac8590af..a5769d17 100644 --- a/src/nc.c +++ b/src/nc.c @@ -2,6 +2,7 @@ * vim:ts=4:sw=4:expandtab */ #include +#include #include "all.h" static int xkb_event_base; @@ -254,6 +255,9 @@ int main(int argc, char *argv[]) { /* Watch _NET_WM_NAME (title of the window encoded in UTF-8) */ xcb_property_set_handler(&prophs, atoms[_NET_WM_NAME], 128, handle_windowname_change, NULL); + /* Watch WM_HINTS (contains the urgent property) */ + xcb_property_set_handler(&prophs, WM_HINTS, UINT_MAX, handle_hints, NULL); + /* Watch WM_NAME (title of the window encoded in COMPOUND_TEXT) */ xcb_watch_wm_name(&prophs, 128, handle_windowname_change_legacy, NULL); diff --git a/src/workspace.c b/src/workspace.c index 17986baf..6a81f30e 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -429,31 +429,37 @@ void workspace_unmap_clients(xcb_connection_t *conn, Workspace *u_ws) { ignore_enter_notify_forall(conn, u_ws, false); } +#endif + +static bool get_urgency_flag(Con *con) { + Con *child; + TAILQ_FOREACH(child, &(con->nodes_head), nodes) + if (child->urgent || get_urgency_flag(child)) + return true; + + TAILQ_FOREACH(child, &(con->floating_head), floating_windows) + if (child->urgent || get_urgency_flag(child)) + return true; + + return false; +} /* * Goes through all clients on the given workspace and updates the workspace’s * urgent flag accordingly. * */ -void workspace_update_urgent_flag(Workspace *ws) { - Client *current; - bool old_flag = ws->urgent; - bool urgent = false; +void workspace_update_urgent_flag(Con *ws) { + bool old_flag = ws->urgent; + ws->urgent = get_urgency_flag(ws); + DLOG("Workspace urgency flag changed from %d to %d\n", old_flag, ws->urgent); - SLIST_FOREACH(current, &(ws->focus_stack), focus_clients) { - if (!current->urgent) - continue; - - urgent = true; - break; - } - - ws->urgent = urgent; - - if (old_flag != urgent) - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"urgent\"}"); + if (old_flag != ws->urgent) + ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"urgent\"}"); } +#if 0 + /* * Returns the width of the workspace. * diff --git a/testcases/t/13-urgent.t b/testcases/t/13-urgent.t index cf847456..710d1892 100644 --- a/testcases/t/13-urgent.t +++ b/testcases/t/13-urgent.t @@ -1,12 +1,10 @@ #!perl # vim:ts=4:sw=4:expandtab -# Beware that this test uses workspace 9 to perform some tests (it expects -# the workspace to be empty). -# TODO: skip it by default? -use i3test tests => 7; +use i3test tests => 10; use X11::XCB qw(:all); use Time::HiRes qw(sleep); +use List::Util qw(first); BEGIN { use_ok('X11::XCB::Connection') or BAIL_OUT('Cannot load X11::XCB::Connection'); @@ -14,33 +12,68 @@ BEGIN { my $x = X11::XCB::Connection->new; -my $i3 = i3; - -# Switch to the nineth workspace -$i3->command('9')->recv; +my $i3 = i3("/tmp/nestedcons"); +my $tmp = get_unused_workspace(); +$i3->command("workspace $tmp")->recv; ##################################################################### # Create two windows and put them in stacking mode ##################################################################### -my $top = i3test::open_standard_window($x); -sleep 0.25; -my $bottom = i3test::open_standard_window($x); -sleep 0.25; +$i3->command('split v')->recv; -$i3->command('s')->recv; +my $top = i3test::open_standard_window($x); +my $bottom = i3test::open_standard_window($x); + +my @urgent = grep { $_->{urgent} == 1 } @{get_ws_content($tmp)}; +is(@urgent, 0, 'no window got the urgent flag'); + +#$i3->command('layout stacking')->recv; ##################################################################### # Add the urgency hint, switch to a different workspace and back again ##################################################################### $top->add_hint('urgency'); -sleep 1; +sleep 0.5; -$i3->command('1')->recv; -$i3->command('9')->recv; -$i3->command('1')->recv; +@content = @{get_ws_content($tmp)}; +@urgent = grep { $_->{urgent} == 1 } @content; +$top_info = first { $_->{window} == $top->id } @content; +$bottom_info = first { $_->{window} == $bottom->id } @content; -my $std = i3test::open_standard_window($x); -sleep 0.25; -$std->add_hint('urgency'); -sleep 1; +is($top_info->{urgent}, 1, 'top window is marked urgent'); +is($bottom_info->{urgent}, 0, 'bottom window is not marked urgent'); +is(@urgent, 1, 'exactly one window got the urgent flag'); + +$i3->command('[id="' . $top->id . '"] focus')->recv; + +@urgent = grep { $_->{urgent} == 1 } @{get_ws_content($tmp)}; +is(@urgent, 0, 'no window got the urgent flag after focusing'); + +$top->add_hint('urgency'); +sleep 0.5; + +@urgent = grep { $_->{urgent} == 1 } @{get_ws_content($tmp)}; +is(@urgent, 0, 'no window got the urgent flag after re-setting urgency hint'); + +##################################################################### +# Check if the workspace urgency hint gets set/cleared correctly +##################################################################### +my $ws = get_ws($tmp); +is($ws->{urgent}, 0, 'urgent flag not set on workspace'); + +my $otmp = get_unused_workspace(); +$i3->command("workspace $otmp")->recv; + +$top->add_hint('urgency'); +sleep 0.5; + +$ws = get_ws($tmp); +is($ws->{urgent}, 1, 'urgent flag set on workspace'); + +$i3->command("workspace $tmp")->recv; + +$ws = get_ws($tmp); +is($ws->{urgent}, 0, 'urgent flag not set on workspace after switching'); + +diag( "Testing i3, Perl $], $^X" );