From 58297f4ab59445f7c502c4a5ef23dbc4041294de Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 14 Dec 2013 11:44:06 +0100 Subject: [PATCH] layout restore: support more criteria, match only once (+test) --- src/load_layout.c | 13 ++- src/manage.c | 8 ++ testcases/t/214-layout-restore-criteria.t | 110 ++++++++++++++++++++++ 3 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 testcases/t/214-layout-restore-criteria.t diff --git a/src/load_layout.c b/src/load_layout.c index ce17f3a5..d0c5a3b1 100644 --- a/src/load_layout.c +++ b/src/load_layout.c @@ -151,15 +151,20 @@ static int json_string(void *ctx, const unsigned char *val, unsigned int len) { #endif LOG("string: %.*s for key %s\n", (int)len, val, last_key); if (parsing_swallows) { - /* TODO: the other swallowing keys */ + char *sval; + sasprintf(&sval, "%.*s", len, val); if (strcasecmp(last_key, "class") == 0) { - char *sval; - sasprintf(&sval, "%.*s", len, val); current_swallow->class = regex_new(sval); - free(sval); + } else if (strcasecmp(last_key, "instance") == 0) { + current_swallow->instance = regex_new(sval); + } else if (strcasecmp(last_key, "window_role") == 0) { + current_swallow->role = regex_new(sval); + } else if (strcasecmp(last_key, "title") == 0) { + current_swallow->title = regex_new(sval); } else { ELOG("swallow key %s unknown\n", last_key); } + free(sval); } else { if (strcasecmp(last_key, "name") == 0) { json_node->name = scalloc((len+1) * sizeof(char)); diff --git a/src/manage.c b/src/manage.c index af9e8ef2..81da8f70 100644 --- a/src/manage.c +++ b/src/manage.c @@ -315,6 +315,14 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki if (match != NULL && match->insert_where == M_BELOW) { nc = tree_open_con(nc, cwindow); } + + /* If M_BELOW is not used, the container is replaced. This happens with + * "swallows" criteria that are used for stored layouts, in which case + * we need to remove that criterion, because they should only be valid + * once. */ + if (match != NULL && match->insert_where != M_BELOW) { + TAILQ_REMOVE(&(nc->swallow_head), match, matches); + } } DLOG("new container = %p\n", nc); diff --git a/testcases/t/214-layout-restore-criteria.t b/testcases/t/214-layout-restore-criteria.t new file mode 100644 index 00000000..4084f3f4 --- /dev/null +++ b/testcases/t/214-layout-restore-criteria.t @@ -0,0 +1,110 @@ +#!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 all supported criteria for the "swallows" key. +use i3test; +use File::Temp qw(tempfile); +use IO::Handle; +use X11::XCB qw(PROP_MODE_REPLACE); + +sub verify_swallow_criterion { + my ($cfgline, $open_window_cb) = @_; + + my $ws = fresh_workspace; + + my @content = @{get_ws_content($ws)}; + is(@content, 0, "no nodes on the new workspace yet ($cfgline)"); + + my ($fh, $filename) = tempfile(UNLINK => 1); + print $fh <flush; + cmd "append_layout $filename"; + + does_i3_live; + + @content = @{get_ws_content($ws)}; + is(@content, 1, "one node on the workspace now ($cfgline)"); + + my $top = $open_window_cb->(); + + @content = @{get_ws_content($ws)}; + is(@content, 1, "still one node on the workspace now ($cfgline)"); + my @nodes = @{$content[0]->{nodes}}; + is($nodes[0]->{window}, $top->id, "top window on top ($cfgline)"); + + close($fh); +} + +verify_swallow_criterion( + '"class": "^special_class$"', + sub { open_window(wm_class => 'special_class') } +); + +# Run the same test again to verify that the window is not being swallowed by +# the first container. Each swallow condition should only swallow precisely one +# window. +verify_swallow_criterion( + '"class": "^special_class$"', + sub { open_window(wm_class => 'special_class') } +); + +verify_swallow_criterion( + '"instance": "^special_instance$"', + sub { open_window(wm_class => '', instance => 'special_instance') } +); + +verify_swallow_criterion( + '"title": "^special_title$"', + sub { open_window(name => 'special_title') } +); + +verify_swallow_criterion( + '"role": "^special_role$"', + sub { + open_window( + name => 'roletest', + before_map => sub { + my ($window) = @_; + my $atomname = $x->atom(name => 'WM_WINDOW_ROLE'); + my $atomtype = $x->atom(name => 'STRING'); + $x->change_property( + PROP_MODE_REPLACE, + $window->id, + $atomname->id, + $atomtype->id, + 8, + length("special_role") + 1, + "special_role\x00" + ); + }, + ); + } +); + +done_testing;