]> git.sur5r.net Git - i3/i3/commitdiff
layout restore: support more criteria, match only once (+test)
authorMichael Stapelberg <michael@stapelberg.de>
Sat, 14 Dec 2013 10:44:06 +0000 (11:44 +0100)
committerMichael Stapelberg <michael@stapelberg.de>
Sun, 22 Dec 2013 20:52:49 +0000 (21:52 +0100)
src/load_layout.c
src/manage.c
testcases/t/214-layout-restore-criteria.t [new file with mode: 0644]

index ce17f3a55700244c37c599945450f4ee7ffe4052..d0c5a3b1d3ab63c90e66969279bc6aa6b1e13399 100644 (file)
@@ -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));
index af9e8ef2cbf4d3d0c646dc05b29c37b8041436ea..81da8f704c86c6c42e51e86c516328fe105858fc 100644 (file)
@@ -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 (file)
index 0000000..4084f3f
--- /dev/null
@@ -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 <<EOT;
+{
+    "layout": "splitv",
+    "nodes": [
+        {
+            "swallows": [
+                {
+                    $cfgline
+                }
+            ]
+        }
+    ]
+}
+EOT
+    $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;