]> git.sur5r.net Git - i3/i3/commitdiff
Set and unset individual atoms in _NET_WM_STATE instead of overwriting the entire... 1920/head
authorIngo Bürk <ingo.buerk@tngtech.com>
Mon, 14 Sep 2015 11:34:15 +0000 (13:34 +0200)
committerIngo Bürk <ingo.buerk@tngtech.com>
Thu, 17 Sep 2015 07:13:12 +0000 (09:13 +0200)
fixes #1873

include/xcb.h
src/con.c
src/ewmh.c
src/x.c
src/xcb.c
testcases/t/253-multiple-net-wm-state-atoms.t [new file with mode: 0644]

index 7dab5d100c8a39014dcc5d9f11db6588d853d3ab..2c87a19c688499aaa977eadbb41efe3e3c210b7f 100644 (file)
@@ -145,3 +145,18 @@ uint16_t get_visual_depth(xcb_visualid_t visual_id);
  *
  */
 xcb_visualid_t get_visualid_by_depth(uint16_t depth);
+
+/**
+ * Add an atom to a list of atoms the given property defines.
+ * This is useful, for example, for manipulating _NET_WM_STATE.
+ *
+ */
+void xcb_add_property_atom(xcb_connection_t *conn, xcb_window_t window, xcb_atom_t property, xcb_atom_t atom);
+
+/**
+ * Remove an atom from a list of atoms the given property defines without
+ * removing any other potentially set atoms.  This is useful, for example, for
+ * manipulating _NET_WM_STATE.
+ *
+ */
+void xcb_remove_property_atom(xcb_connection_t *conn, xcb_window_t window, xcb_atom_t property, xcb_atom_t atom);
index 7a8ccd5830f9a0ad519c85b9498f7c937fadecca..1d2795c303c179019f38c5ad29778a8215a3d790 100644 (file)
--- a/src/con.c
+++ b/src/con.c
@@ -658,14 +658,13 @@ static void con_set_fullscreen_mode(Con *con, fullscreen_mode_t fullscreen_mode)
     if (con->window == NULL)
         return;
 
-    uint32_t values[1];
-    unsigned int num = 0;
-
-    if (con->fullscreen_mode != CF_NONE)
-        values[num++] = A__NET_WM_STATE_FULLSCREEN;
-
-    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id,
-                        A__NET_WM_STATE, XCB_ATOM_ATOM, 32, num, values);
+    if (con->fullscreen_mode != CF_NONE) {
+        DLOG("Setting _NET_WM_STATE_FULLSCREEN for con = %p / window = %d.\n", con, con->window->id);
+        xcb_add_property_atom(conn, con->window->id, A__NET_WM_STATE, A__NET_WM_STATE_FULLSCREEN);
+    } else {
+        DLOG("Removing _NET_WM_STATE_FULLSCREEN for con = %p / window = %d.\n", con, con->window->id);
+        xcb_remove_property_atom(conn, con->window->id, A__NET_WM_STATE, A__NET_WM_STATE_FULLSCREEN);
+    }
 }
 
 /*
index eb6a6ea60d39af252364885f87f9b0f695ecb9ba..b2260d64301e45e330f9950d289506b1c35ce5e2 100644 (file)
@@ -218,13 +218,13 @@ void ewmh_update_client_list_stacking(xcb_window_t *stack, int num_windows) {
  *
  */
 void ewmh_update_sticky(xcb_window_t window, bool sticky) {
-    uint32_t values[1];
-    unsigned int num = 0;
-
-    if (sticky)
-        values[num++] = A__NET_WM_STATE_STICKY;
-
-    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, window, A__NET_WM_STATE, XCB_ATOM_ATOM, 32, num, values);
+    if (sticky) {
+        DLOG("Setting _NET_WM_STATE_STICKY for window = %d.\n", window);
+        xcb_add_property_atom(conn, window, A__NET_WM_STATE, A__NET_WM_STATE_STICKY);
+    } else {
+        DLOG("Removing _NET_WM_STATE_STICKY for window = %d.\n", window);
+        xcb_remove_property_atom(conn, window, A__NET_WM_STATE, A__NET_WM_STATE_STICKY);
+    }
 }
 
 /*
diff --git a/src/x.c b/src/x.c
index f0da9fff5e5caf1897b04f03855aba1a4a90c2b2..bf0cb914d6621303a1b23c8716d590b3d6c0ab0f 100644 (file)
--- a/src/x.c
+++ b/src/x.c
@@ -631,16 +631,14 @@ static void set_hidden_state(Con *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;
+        xcb_add_property_atom(conn, con->window->id, A__NET_WM_STATE, A__NET_WM_STATE_HIDDEN);
     } else {
         DLOG("removing _NET_WM_STATE_HIDDEN for con = %p\n", con);
+        xcb_remove_property_atom(conn, con->window->id, A__NET_WM_STATE, A__NET_WM_STATE_HIDDEN);
     }
 
-    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;
 }
 
index 001f6b51958cf46651304d3644134a3c40e46ba2..f98115f5c80ce25049e081d9ea303636acc180bd 100644 (file)
--- a/src/xcb.c
+++ b/src/xcb.c
@@ -274,3 +274,48 @@ xcb_visualid_t get_visualid_by_depth(uint16_t depth) {
     }
     return 0;
 }
+
+/*
+ * Add an atom to a list of atoms the given property defines.
+ * This is useful, for example, for manipulating _NET_WM_STATE.
+ *
+ */
+void xcb_add_property_atom(xcb_connection_t *conn, xcb_window_t window, xcb_atom_t property, xcb_atom_t atom) {
+    xcb_change_property(conn, XCB_PROP_MODE_APPEND, window, property, XCB_ATOM_ATOM, 32, 1, (uint32_t[]){atom});
+}
+
+/*
+ * Remove an atom from a list of atoms the given property defines without
+ * removing any other potentially set atoms.  This is useful, for example, for
+ * manipulating _NET_WM_STATE.
+ *
+ */
+void xcb_remove_property_atom(xcb_connection_t *conn, xcb_window_t window, xcb_atom_t property, xcb_atom_t atom) {
+    xcb_grab_server(conn);
+
+    xcb_get_property_reply_t *reply =
+        xcb_get_property_reply(conn,
+                               xcb_get_property(conn, false, window, property, XCB_GET_PROPERTY_TYPE_ANY, 0, 4096), NULL);
+    if (reply == NULL || xcb_get_property_value_length(reply) == 0)
+        goto release_grab;
+    xcb_atom_t *atoms = xcb_get_property_value(reply);
+    if (atoms == NULL) {
+        goto release_grab;
+    }
+
+    {
+        int num = 0;
+        const int current_size = xcb_get_property_value_length(reply) / (reply->format / 8);
+        xcb_atom_t values[current_size];
+        for (int i = 0; i < current_size; i++) {
+            if (atoms[i] != atom)
+                values[num++] = atoms[i];
+        }
+
+        xcb_change_property(conn, XCB_PROP_MODE_REPLACE, window, property, XCB_ATOM_ATOM, 32, num, values);
+    }
+
+release_grab:
+    FREE(reply);
+    xcb_ungrab_server(conn);
+}
diff --git a/testcases/t/253-multiple-net-wm-state-atoms.t b/testcases/t/253-multiple-net-wm-state-atoms.t
new file mode 100644 (file)
index 0000000..0ce7f9f
--- /dev/null
@@ -0,0 +1,70 @@
+#!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)
+#
+# Ticket: #1873
+use i3test;
+use X11::XCB qw(:all);
+
+sub get_wm_state {
+    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});
+    return \@atoms;
+}
+
+my $wm_state_sticky = $x->atom(name => '_NET_WM_STATE_STICKY')->id;
+my $wm_state_fullscreen = $x->atom(name => '_NET_WM_STATE_FULLSCREEN')->id;
+
+##########################################################################
+# Given a sticky container, when it is fullscreened, then both wm state
+# atoms are set. When the container is unfullscreened, then only the
+# sticky atom is still set.
+##########################################################################
+
+fresh_workspace;
+my $window = open_window;
+cmd 'sticky enable';
+is_deeply(get_wm_state($window), [ $wm_state_sticky ], 'sanity check: _NET_WM_STATE_STICKY is set');
+
+cmd 'fullscreen enable';
+is_deeply(get_wm_state($window), [ $wm_state_sticky, $wm_state_fullscreen ],
+    'both _NET_WM_STATE_FULLSCREEN and _NET_WM_STATE_STICKY are set');
+
+cmd 'sticky disable';
+is_deeply(get_wm_state($window), [ $wm_state_fullscreen ], 'only _NET_WM_STATE_FULLSCREEN is set');
+
+cmd 'sticky enable';
+cmd 'fullscreen disable';
+is_deeply(get_wm_state($window), [ $wm_state_sticky ], 'only _NET_WM_STATE_STICKY is set');
+
+##########################################################################
+
+done_testing;