diff --git a/src/handlers.c b/src/handlers.c index 65fa46a0..06878f5c 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -1092,6 +1092,30 @@ static void handle_focus_in(xcb_focus_in_event_t *event) { return; } +/* + * Handles the WM_CLASS property for assignments and criteria selection. + * + */ +static bool handle_class_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, + xcb_atom_t name, xcb_get_property_reply_t *prop) { + Con *con; + if ((con = con_by_window_id(window)) == NULL || con->window == NULL) + return false; + + if (prop == NULL) { + prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, + false, window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0, 32), + NULL); + + if (prop == NULL) + return false; + } + + window_update_class(con->window, prop, false); + + return true; +} + /* Returns false if the event could not be processed (e.g. the window could not * be found), true otherwise */ 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); @@ -1109,7 +1133,8 @@ static struct property_handler_t property_handlers[] = { {0, UINT_MAX, handle_normal_hints}, {0, UINT_MAX, handle_clientleader_change}, {0, UINT_MAX, handle_transient_for}, - {0, 128, handle_windowrole_change}}; + {0, 128, handle_windowrole_change}, + {0, 128, handle_class_change}}; #define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t)) /* @@ -1127,6 +1152,7 @@ void property_handlers_init(void) { property_handlers[4].atom = A_WM_CLIENT_LEADER; property_handlers[5].atom = XCB_ATOM_WM_TRANSIENT_FOR; property_handlers[6].atom = A_WM_WINDOW_ROLE; + property_handlers[7].atom = XCB_ATOM_WM_CLASS; } static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) { diff --git a/testcases/t/235-wm-class-change-handler.t b/testcases/t/235-wm-class-change-handler.t new file mode 100644 index 00000000..e6cacded --- /dev/null +++ b/testcases/t/235-wm-class-change-handler.t @@ -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) +# +# Test that changes to WM_CLASS are internally processed by i3 by updating the +# cached property and running assignments. This allows the property to be used +# in criteria selection +# Ticket: #1052 +# Bug still in: 4.8-73-g6bf7f8e +use i3test i3_autostart => 0; +use X11::XCB qw(PROP_MODE_REPLACE); + +my $config = <atom(name => 'WM_CLASS'); + my $atomtype = $x->atom(name => 'STRING'); + $x->change_property( + PROP_MODE_REPLACE, + $window->id, + $atomname->id, + $atomtype->id, + 8, + length($class) + 1, + $class + ); + sync_with_i3; +} + +my $ws = fresh_workspace; + +my $win = open_window; + +change_window_class($win, "special\0Special"); + +my $con = @{get_ws_content($ws)}[0]; + +is($con->{window_properties}->{class}, 'Special', + 'The container class should be updated when a window changes class'); + +is($con->{window_properties}->{instance}, 'special', + 'The container instance should be updated when a window changes instance'); + +# The mark `special_class_mark` is added in a `for_window` assignment in the +# config for testing purposes +is($con->{mark}, 'special_class_mark', + 'A `for_window` assignment should run for a match when the window changes class'); + +exit_gracefully($pid); + +done_testing;