]> git.sur5r.net Git - i3/i3/commitdiff
Support _NET_WM_STATE_FOCUSED 3178/head
authorTony Crisci <tony@dubstepdish.com>
Mon, 25 Jul 2016 00:43:56 +0000 (20:43 -0400)
committerOrestis Floros <orestisf1993@gmail.com>
Fri, 23 Mar 2018 12:30:57 +0000 (14:30 +0200)
_NET_WM_STATE_FOCUSED is set on _NET_WM_STATE to indicate that the
window is focused. It must be set when the window is newly focused and
removed once the window no longer has focus.

> _NET_WM_STATE_FOCUSED indicates whether the window's decorations are
> drawn in an active state. Clients MUST regard it as a read-only hint.
> It cannot be set at map time or changed via a _NET_WM_STATE client
> message.

For example, this is used by GTK applications to show the decoration in
an active or inactive state. This change can be tested by opening a GTK
application (like evince), focusing the window and unfocusing the
window, and observing a change in the window decorations.

Fixes #2273

include/atoms_NET_SUPPORTED.xmacro
include/ewmh.h
src/ewmh.c
src/x.c
testcases/lib/i3test.pm.in
testcases/t/158-wm_take_focus.t
testcases/t/295-net-wm-state-focused.t [new file with mode: 0644]

index a7b9676d9817e21dcd142c6f62fc9606cada2773..a81948a983fde44853e7da19889743a4adb78f96 100644 (file)
@@ -8,6 +8,7 @@ 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_FOCUSED)
 xmacro(_NET_WM_STATE)
 xmacro(_NET_WM_WINDOW_TYPE)
 xmacro(_NET_WM_WINDOW_TYPE_NORMAL)
index 5844faa660a3886f78d0e1669f3a162c1385fbc9..01ae67f969c943392ab8506b583cd3b911d5e019 100644 (file)
@@ -83,6 +83,12 @@ void ewmh_update_client_list_stacking(xcb_window_t *stack, int num_windows);
  */
 void ewmh_update_sticky(xcb_window_t window, bool sticky);
 
+/**
+ * Set or remove _NEW_WM_STATE_FOCUSED on the window.
+ *
+ */
+void ewmh_update_focused(xcb_window_t window, bool is_focused);
+
 /**
  * Set up the EWMH hints on the root window.
  *
index f8422bdadaf1f103f146ecde24a4a94cd279a30a..e5dcafcb012a6572e336e23594fae26301bd3a95 100644 (file)
@@ -284,6 +284,20 @@ void ewmh_update_sticky(xcb_window_t window, bool sticky) {
     }
 }
 
+/*
+ * Set or remove _NEW_WM_STATE_FOCUSED on the window.
+ *
+ */
+void ewmh_update_focused(xcb_window_t window, bool is_focused) {
+    if (is_focused) {
+        DLOG("Setting _NET_WM_STATE_FOCUSED for window = %d.\n", window);
+        xcb_add_property_atom(conn, window, A__NET_WM_STATE, A__NET_WM_STATE_FOCUSED);
+    } else {
+        DLOG("Removing _NET_WM_STATE_FOCUSED for window = %d.\n", window);
+        xcb_remove_property_atom(conn, window, A__NET_WM_STATE, A__NET_WM_STATE_FOCUSED);
+    }
+}
+
 /*
  * Set up the EWMH hints on the root window.
  *
diff --git a/src/x.c b/src/x.c
index 7829079bb0363f8514e5b1abe6513434c0db809a..629520d4e0d2ab189e3607e06bcdae24d2cbb305 100644 (file)
--- a/src/x.c
+++ b/src/x.c
@@ -99,6 +99,23 @@ static con_state *state_for_frame(xcb_window_t window) {
     return NULL;
 }
 
+/*
+ * Changes the atoms on the root window and the windows themselves to properly
+ * reflect the current focus for ewmh compliance.
+ *
+ */
+static void change_ewmh_focus(xcb_window_t new_focus, xcb_window_t old_focus) {
+    ewmh_update_active_window(new_focus);
+
+    if (new_focus != XCB_WINDOW_NONE) {
+        ewmh_update_focused(new_focus, true);
+    }
+
+    if (old_focus != XCB_WINDOW_NONE) {
+        ewmh_update_focused(old_focus, false);
+    }
+}
+
 /*
  * Initializes the X11 part for the given container. Called exactly once for
  * every container from con_new().
@@ -1120,7 +1137,7 @@ void x_push_changes(Con *con) {
                      to_focus, focused, focused->name);
                 send_take_focus(to_focus, last_timestamp);
 
-                ewmh_update_active_window((con_has_managed_window(focused) ? focused->window->id : XCB_WINDOW_NONE));
+                change_ewmh_focus((con_has_managed_window(focused) ? focused->window->id : XCB_WINDOW_NONE), last_focused);
 
                 if (to_focus != last_focused && is_con_attached(focused))
                     ipc_send_window_event("focus", focused);
@@ -1139,7 +1156,7 @@ void x_push_changes(Con *con) {
                     xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values);
                 }
 
-                ewmh_update_active_window((con_has_managed_window(focused) ? focused->window->id : XCB_WINDOW_NONE));
+                change_ewmh_focus((con_has_managed_window(focused) ? focused->window->id : XCB_WINDOW_NONE), last_focused);
 
                 if (to_focus != XCB_NONE && to_focus != last_focused && focused->window != NULL && is_con_attached(focused))
                     ipc_send_window_event("focus", focused);
@@ -1154,7 +1171,8 @@ void x_push_changes(Con *con) {
          * root window in order to avoid an X11 fallback mechanism causing a ghosting effect (see #1378). */
         DLOG("Still no window focused, better set focus to the EWMH support window (%d)\n", ewmh_window);
         xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, ewmh_window, last_timestamp);
-        ewmh_update_active_window(XCB_WINDOW_NONE);
+        change_ewmh_focus(XCB_WINDOW_NONE, last_focused);
+
         focused_id = ewmh_window;
     }
 
index e754c0c17f6bf3b8b5b92e72233ebbe05ab8198a..68ac1ee569827edbef609ff00c1f388d1a7a8d9b 100644 (file)
@@ -51,6 +51,7 @@ our @EXPORT = qw(
     kill_all_windows
     events_for
     listen_for_binding
+    is_net_wm_state_focused
 );
 
 =head1 NAME
@@ -1026,6 +1027,40 @@ sub listen_for_binding {
     return $command;
 }
 
+=head2 is_net_wm_state_focused
+
+Returns true if the given window has the _NET_WM_STATE_FOCUSED atom.
+
+    ok(is_net_wm_state_focused($window), '_NET_WM_STATE_FOCUSED set');
+
+=cut
+sub is_net_wm_state_focused {
+    my ($window) = @_;
+
+    sync_with_i3;
+    my $atom = $x->atom(name => '_NET_WM_STATE_FOCUSED');
+    my $cookie = $x->get_property(
+        0,
+        $window->{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;
+}
+
+
 =head1 AUTHOR
 
 Michael Stapelberg <michael@i3wm.org>
index 41d400d5cd15cc34f91ff6822273b56f3ea810b0..c4b42964fc3875580b2dc40e6ee691785e13ce0a 100644 (file)
@@ -55,6 +55,7 @@ subtest 'Window without WM_TAKE_FOCUS', sub {
     my $window = open_window;
 
     ok(!recv_take_focus($window), 'did not receive ClientMessage');
+    ok(is_net_wm_state_focused($window), '_NET_WM_STATE_FOCUSED set');
 
     my ($nodes) = get_ws_content($ws);
     my $con = shift @$nodes;
@@ -91,6 +92,7 @@ subtest 'Window with WM_TAKE_FOCUS and without InputHint', sub {
     $window->map;
 
     ok(!recv_take_focus($window), 'did not receive ClientMessage');
+    ok(is_net_wm_state_focused($window), '_NET_WM_STATE_FOCUSED set');
 
     my ($nodes) = get_ws_content($ws);
     my $con = shift @$nodes;
@@ -112,6 +114,7 @@ subtest 'Window with WM_TAKE_FOCUS and unspecified InputHint', sub {
     my $window = open_window({ protocols => [ $take_focus ] });
 
     ok(!recv_take_focus($window), 'did not receive ClientMessage');
+    ok(is_net_wm_state_focused($window), '_NET_WM_STATE_FOCUSED set');
 
     my ($nodes) = get_ws_content($ws);
     my $con = shift @$nodes;
diff --git a/testcases/t/295-net-wm-state-focused.t b/testcases/t/295-net-wm-state-focused.t
new file mode 100644 (file)
index 0000000..1881154
--- /dev/null
@@ -0,0 +1,37 @@
+#!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_FOCUSED atom properly.
+# Ticket: #2273
+use i3test;
+use X11::XCB qw(:all);
+
+my ($windowA, $windowB);
+
+fresh_workspace;
+$windowA = open_window;
+
+ok(is_net_wm_state_focused($windowA), 'a newly opened window that is focused should have _NET_WM_STATE_FOCUSED set');
+
+$windowB = open_window;
+
+ok(!is_net_wm_state_focused($windowA), 'when a another window is focused, the old window should not have _NET_WM_STATE_FOCUSED set');
+
+fresh_workspace;
+
+ok(!is_net_wm_state_focused($windowB), 'when focus moves to the ewmh support window, neither window should have _NET_WM_STATE_FOCUSED set');
+
+done_testing;