From d12482e5fd08403ce0954318e2993b2fb7214c8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Thu, 16 Apr 2015 19:42:31 +0200 Subject: [PATCH 1/3] Added 'con_is_hidden' to check whether a given container is visible to the user assuming its workspace is visible. This is useful for determining whether we want to set the _NET_WM_STATE_HIDDEN atom on the window. --- include/con.h | 9 ++++++++- src/con.c | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/include/con.h b/include/con.h index 498fcbaa..4813b776 100644 --- a/include/con.h +++ b/include/con.h @@ -42,12 +42,19 @@ bool con_is_leaf(Con *con); */ bool con_has_managed_window(Con *con); -/* +/** * Returns true if a container should be considered split. * */ bool con_is_split(Con *con); +/** + * This will only return true for containers which have some parent with + * a tabbed / stacked parent of which they are not the currently focused child. + * + */ +bool con_is_hidden(Con *con); + /** * Returns true if this node has regular or floating children. * diff --git a/src/con.c b/src/con.c index aefe756c..05f608bd 100644 --- a/src/con.c +++ b/src/con.c @@ -260,6 +260,29 @@ bool con_is_split(Con *con) { } } +/* + * This will only return true for containers which have some parent with + * a tabbed / stacked parent of which they are not the currently focused child. + * + */ +bool con_is_hidden(Con *con) { + Con *current = con; + + /* ascend to the workspace level and memorize the highest-up container + * which is stacked or tabbed. */ + while (current != NULL && current->type != CT_WORKSPACE) { + Con *parent = current->parent; + if (parent != NULL && (parent->layout == L_TABBED || parent->layout == L_STACKED)) { + if (TAILQ_FIRST(&(parent->focus_head)) != current) + return true; + } + + current = parent; + } + + return false; +} + /* * Returns true if this node accepts a window (if the node swallows windows, * it might already have swallowed enough and cannot hold any more). From ffe25d9e4312f1c9b132bc650341a4fa95ec13ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Thu, 16 Apr 2015 19:43:23 +0200 Subject: [PATCH 2/3] Set the _NET_WM_STATE_HIDDEN atom on windows that are currently not visible due to being in the non-focused tab of a stacked or tabbed container. fixes #1648 --- include/atoms.xmacro | 1 + src/ewmh.c | 4 ++-- src/x.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/include/atoms.xmacro b/include/atoms.xmacro index b9ee4eb7..00a346db 100644 --- a/include/atoms.xmacro +++ b/include/atoms.xmacro @@ -5,6 +5,7 @@ xmacro(_NET_WM_MOVERESIZE) xmacro(_NET_WM_STATE_FULLSCREEN) xmacro(_NET_WM_STATE_DEMANDS_ATTENTION) xmacro(_NET_WM_STATE_MODAL) +xmacro(_NET_WM_STATE_HIDDEN) xmacro(_NET_WM_STATE) xmacro(_NET_WM_WINDOW_TYPE) xmacro(_NET_WM_WINDOW_TYPE_NORMAL) diff --git a/src/ewmh.c b/src/ewmh.c index 1a357f23..a1d2489e 100644 --- a/src/ewmh.c +++ b/src/ewmh.c @@ -234,6 +234,6 @@ void ewmh_setup_hints(void) { /* I’m not entirely sure if we need to keep _NET_WM_NAME on root. */ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3"); - /* only send the first 29 atoms (last one is _NET_CLOSE_WINDOW) increment that number when adding supported atoms */ - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 29, supported_atoms); + /* only send the first 30 atoms (last one is _NET_CLOSE_WINDOW) increment that number when adding supported atoms */ + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 30, supported_atoms); } diff --git a/src/x.c b/src/x.c index 7fe70445..2dcffe6b 100644 --- a/src/x.c +++ b/src/x.c @@ -36,6 +36,7 @@ typedef struct con_state { bool mapped; bool unmap_now; bool child_mapped; + bool is_hidden; /** The con for which this state is. */ Con *con; @@ -611,6 +612,33 @@ void x_deco_recurse(Con *con) { x_draw_decoration(con); } +/* + * Sets or removes the _NET_WM_STATE_HIDDEN property on con if necessary. + * + */ +static void set_hidden_state(Con *con) { + if (con->window == NULL) { + return; + } + + con_state *state = state_for_frame(con->frame); + bool should_be_hidden = con_is_hidden(con); + if (should_be_hidden == state->is_hidden) + return; + + unsigned int num = 0; + uint32_t values[1]; + if (should_be_hidden) { + DLOG("setting _NET_WM_STATE_HIDDEN for con = %p\n", con); + values[num++] = A__NET_WM_STATE_HIDDEN; + } else { + DLOG("removing _NET_WM_STATE_HIDDEN for con = %p\n", con); + } + + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id, A__NET_WM_STATE, XCB_ATOM_ATOM, 32, num, values); + state->is_hidden = should_be_hidden; +} + /* * This function pushes the properties of each node of the layout tree to * X11 if they have changed (like the map state, position of the window, …). @@ -814,6 +842,8 @@ void x_push_node(Con *con) { fake_absolute_configure_notify(con); } + set_hidden_state(con); + /* Handle all children and floating windows of this node. We recurse * in focus order to display the focused client in a stack first when * switching workspaces (reduces flickering). */ From cd0cf9d65137bb28974cdf06ac5ac7d2fd480a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Fri, 17 Apr 2015 20:09:53 +0200 Subject: [PATCH 3/3] Added testcases for setting _NET_WM_STATE_HIDDEN on unfocused containers in tabbed/stacked containers. --- testcases/t/243-net-wm-state-hidden.t | 201 ++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 testcases/t/243-net-wm-state-hidden.t diff --git a/testcases/t/243-net-wm-state-hidden.t b/testcases/t/243-net-wm-state-hidden.t new file mode 100644 index 00000000..3f2301c6 --- /dev/null +++ b/testcases/t/243-net-wm-state-hidden.t @@ -0,0 +1,201 @@ +#!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 setting and removing the _NET_WM_STATE_HIDDEN atom properly. +# Ticket: #1648 +use i3test; +use X11::XCB qw(:all); + +sub is_hidden { + sync_with_i3; + my $atom = $x->atom(name => '_NET_WM_STATE_HIDDEN'); + + my ($con) = @_; + my $cookie = $x->get_property( + 0, + $con->{id}, + $x->atom(name => '_NET_WM_STATE')->id, + GET_PROPERTY_TYPE_ANY, + 0, + 4096 + ); + + my $reply = $x->get_property_reply($cookie->{sequence}); + my $len = $reply->{length}; + return 0 if $len == 0; + + my @atoms = unpack("L$len", $reply->{value}); + for (my $i = 0; $i < $len; $i++) { + return 1 if $atoms[$i] == $atom->id; + } + + return 0; +} + +my ($tabA, $tabB, $tabC, $subtabA, $subtabB, $windowA, $windowB); + +############################################################################### +# Given two containers next to each other, when focusing one, then the other +# one does not have _NET_WM_STATE_HIDDEN set. +############################################################################### + +fresh_workspace; +$windowA = open_window; +$windowB = open_window; + +ok(!is_hidden($windowA), 'left window does not have _NET_WM_STATE_HIDDEN set'); +ok(!is_hidden($windowB), 'right window does not have _NET_WM_STATE_HIDDEN set'); + +############################################################################### +# Given two containers on different workspaces, when one is focused, then +# the other one does not have _NET_WM_STATE_HIDDEN set. +############################################################################### + +fresh_workspace; +$windowA = open_window; +fresh_workspace; +$windowB = open_window; + +ok(!is_hidden($windowA), 'left window does not have _NET_WM_STATE_HIDDEN set'); +ok(!is_hidden($windowB), 'right window does not have _NET_WM_STATE_HIDDEN set'); + +############################################################################### +# Given two containers in the same tabbed container, when one is focused, then +# (only) the other one has _NET_WM_STATE_HIDDEN set. +# Given the other tab is focused, then the atom is transferred. +############################################################################### + +fresh_workspace; +$tabA = open_window; +cmd 'layout tabbed'; +$tabB = open_window; + +ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set'); +ok(!is_hidden($tabB), 'focused tab does not have _NET_WM_STATE_HIDDEN set'); + +cmd 'focus left'; + +ok(!is_hidden($tabA), 'focused tab does not have _NET_WM_STATE_HIDDEN set'); +ok(is_hidden($tabB), 'unfocused tab has _NET_WM_STATE_HIDDEN set'); + +############################################################################### +# Given three containers in the same stacked container, when the focused tab +# is moved to another workspace, then the now focused tab does not have +# _NET_WM_STATE_HIDDEN set anymore. +############################################################################### + +fresh_workspace; +$tabA = open_window; +cmd 'layout stacked'; +$tabB = open_window; +$tabC = open_window; +cmd 'move window to workspace unused'; + +ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set'); +ok(!is_hidden($tabB), 'focused tab does not have _NET_WM_STATE_HIDDEN set'); +ok(!is_hidden($tabC), 'moved window does not have _NET_WM_STATE_HIDDEN set'); + +############################################################################### +# Given three containers in the same stacked container, when a not focused +# tab is moved to another workspace, then it does not have _NET_WM_STATE_HIDDEN +# set anymore. +############################################################################### + +fresh_workspace; +$tabA = open_window; +cmd 'layout stacked'; +$tabB = open_window; +cmd 'mark moveme'; +$tabC = open_window; +cmd '[con_mark="moveme"] move window to workspace unused'; + +ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set'); +ok(!is_hidden($tabB), 'moved window does not have _NET_WM_STATE_HIDDEN set'); +ok(!is_hidden($tabC), 'focused tab does not have _NET_WM_STATE_HIDDEN set'); + +############################################################################### +# Given a tabbed container and some other container, when the latter is moved +# into the tabbed container, then all other tabs have _NET_WM_STATE_HIDDEN +# set. +############################################################################### + +fresh_workspace; +$tabA = open_window; +cmd 'layout tabbed'; +$tabB = open_window; +cmd 'focus parent'; +cmd 'split h'; +$tabC = open_window; +cmd 'move left'; + +ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set'); +ok(is_hidden($tabB), 'unfocused tab has _NET_WM_STATE_HIDDEN set'); +ok(!is_hidden($tabC), 'focused tab does not have _NET_WM_STATE_HIDDEN set'); + +############################################################################### +# Given a stacked container nested inside another tabbed container with the +# inner one being in the currently focused tab, then the focused tab of the +# inner container does not have _NET_WM_STATE_HIDDEN set. +############################################################################### + +fresh_workspace; +$tabA = open_window; +cmd 'layout tabbed'; +$tabB = open_window; +cmd 'split h'; +open_window; +cmd 'split v'; +cmd 'layout stacked'; +$subtabA = open_window; +$subtabB = open_window; + +ok(is_hidden($tabA), 'unfocused outer tab has _NET_WM_STATE_HIDDEN set'); +ok(!is_hidden($tabB), 'focused outer tab does not have _NET_WM_STATE_HIDDEN set'); +ok(is_hidden($subtabA), 'unfocused inner tab has _NET_WM_STATE_HIDDEN set'); +ok(!is_hidden($subtabB), 'focused inner tab does not have _NET_WM_STATE_HIDDEN set'); + +cmd 'focus left'; + +ok(!is_hidden($subtabB), 'focused inner tab does not have _NET_WM_STATE_HIDDEN set'); + +############################################################################### +# Given a stacked container nested inside another tabbed container with the +# inner one being in a currently not focused tab, then all tabs of the inner +# container have _NET_WM_STATE_HIDDEN set. +############################################################################### + +fresh_workspace; +$tabA = open_window; +cmd 'layout tabbed'; +$tabB = open_window; +cmd 'split h'; +open_window; +cmd 'split v'; +cmd 'layout stacked'; +$subtabA = open_window; +$subtabB = open_window; +cmd 'focus parent'; +cmd 'focus parent'; +cmd 'focus left'; + +ok(!is_hidden($tabA), 'focused outer tab does not have _NET_WM_STATE_HIDDEN set'); +ok(is_hidden($tabB), 'unfocused outer tab has _NET_WM_STATE_HIDDEN set'); +ok(is_hidden($subtabA), 'unfocused inner tab has _NET_WM_STATE_HIDDEN set'); +ok(is_hidden($subtabB), 'unfocused inner tab has _NET_WM_STATE_HIDDEN set'); + +############################################################################### + +done_testing;