i3 - improved tiling WM


Handle WM_CLASS changes

Patch status: merged

Patch by Tony Crisci

Long description:

http://tronche.com/gui/x/icccm/sec-4.html

> The WM_CLASS property (of type STRING without control characters)
> contains two consecutive null-terminated strings. These specify the
> Instance and Class names to be used by both the client and the window
> manager for looking up resources for the application or as identifying
> information.

i3 processes changes to WM_CLASS by updating the cached property and
running assignments. This allows the property to be used in criteria
selection.

fixes #1052

To apply this patch, use:
curl http://cr.i3wm.org/patch/626/raw.patch | git am

b/src/handlers.c

28
@@ -1092,6 +1092,29 @@ static void handle_focus_in(xcb_focus_in_event_t *event) {
29
     return;
30
 }
31
 
32
+/*
33
+ * Handles the WM_CLASS property for assignments and criteria selection.
34
+ *
35
+ */
36
+static bool handle_class_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
37
+                                xcb_atom_t name, xcb_get_property_reply_t *prop) {
38
+    Con *con;
39
+    if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
40
+        return false;
41
+
42
+    if (prop == NULL) {
43
+        prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn,
44
+                    false, window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0, 32), NULL);
45
+
46
+        if (prop == NULL)
47
+            return false;
48
+    }
49
+
50
+    window_update_class(con->window, prop, false);
51
+
52
+    return true;
53
+}
54
+
55
 /* Returns false if the event could not be processed (e.g. the window could not
56
  * be found), true otherwise */
57
 typedef bool (*cb_property_handler_t)(void *data, xcb_connection_t *c, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *property);
58
@@ -1109,7 +1132,9 @@ static struct property_handler_t property_handlers[] = {
59
     {0, UINT_MAX, handle_normal_hints},
60
     {0, UINT_MAX, handle_clientleader_change},
61
     {0, UINT_MAX, handle_transient_for},
62
-    {0, 128, handle_windowrole_change}};
63
+    {0, 128, handle_windowrole_change},
64
+    {0, 128, handle_class_change}
65
+};
66
 #define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t))
67
 
68
 /*
69
@@ -1127,6 +1152,7 @@ void property_handlers_init(void) {
70
     property_handlers[4].atom = A_WM_CLIENT_LEADER;
71
     property_handlers[5].atom = XCB_ATOM_WM_TRANSIENT_FOR;
72
     property_handlers[6].atom = A_WM_WINDOW_ROLE;
73
+    property_handlers[7].atom = XCB_ATOM_WM_CLASS;
74
 }
75
 
76
 static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) {

b/testcases/t/235-wm-class-change-handler.t

82
@@ -0,0 +1,70 @@
83
+#!perl
84
+# vim:ts=4:sw=4:expandtab
85
+#
86
+# Please read the following documents before working on tests:
87
+# • http://build.i3wm.org/docs/testsuite.html
88
+#   (or docs/testsuite)
89
+#
90
+# • http://build.i3wm.org/docs/lib-i3test.html
91
+#   (alternatively: perldoc ./testcases/lib/i3test.pm)
92
+#
93
+# • http://build.i3wm.org/docs/ipc.html
94
+#   (or docs/ipc)
95
+#
96
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
97
+#   (unless you are already familiar with Perl)
98
+#
99
+# Test that changes to WM_CLASS are internally processed by i3 by updating the
100
+# cached property and running assignments. This allows the property to be used
101
+# in criteria selection
102
+# Ticket: #1052
103
+# Bug still in: 4.8-73-g6bf7f8e
104
+use i3test i3_autostart => 0;
105
+use X11::XCB qw(PROP_MODE_REPLACE);
106
+
107
+my $config = <<EOT;
108
+# i3 config file (v4)
109
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
110
+for_window [class="Special"] mark special_class_mark
111
+EOT
112
+
113
+my $pid = launch_with_config($config);
114
+
115
+sub change_window_class {
116
+    my ($window, $class) = @_;
117
+    my $atomname = $x->atom(name => 'WM_CLASS');
118
+    my $atomtype = $x->atom(name => 'STRING');
119
+    $x->change_property(
120
+        PROP_MODE_REPLACE,
121
+        $window->id,
122
+        $atomname->id,
123
+        $atomtype->id,
124
+        8,
125
+        length($class) + 1,
126
+        $class
127
+    );
128
+    sync_with_i3;
129
+}
130
+
131
+my $ws = fresh_workspace;
132
+
133
+my $win = open_window;
134
+
135
+change_window_class($win, "special\0Special");
136
+
137
+my $con = @{get_ws_content($ws)}[0];
138
+
139
+is($con->{window_properties}->{class}, 'Special',
140
+    'The container class should be updated when a window changes class');
141
+
142
+is($con->{window_properties}->{instance}, 'special',
143
+    'The container instance should be updated when a window changes instance');
144
+
145
+# The mark `special_class_mark` is added in a `for_window` assignment in the
146
+# config for testing purposes
147
+is($con->{mark}, 'special_class_mark',
148
+    'A `for_window` assignment should run for a match when the window changes class');
149
+
150
+exit_gracefully($pid);
151
+
152
+done_testing;