]> git.sur5r.net Git - i3/i3/commitdiff
Merge pull request #2887 from orestisf1993/next
authorIngo Bürk <admin@airblader.de>
Fri, 15 Sep 2017 19:26:35 +0000 (21:26 +0200)
committerGitHub <noreply@github.com>
Fri, 15 Sep 2017 19:26:35 +0000 (21:26 +0200)
Allow assign to workspace by number

134 files changed:
.editorconfig [new file with mode: 0644]
AnyEvent-I3/lib/AnyEvent/I3.pm
docs/hacking-howto
docs/testsuite
docs/userguide
include/con.h
include/data.h
include/load_layout.h
include/output.h
include/util.h
src/commands.c
src/con.c
src/config_parser.c
src/fake_outputs.c
src/handlers.c
src/ipc.c
src/load_layout.c
src/main.c
src/manage.c
src/move.c
src/output.c
src/randr.c
src/tree.c
src/util.c
src/workspace.c
src/xinerama.c
testcases/inject_randr1.5.c
testcases/lib/SocketActivation.pm
testcases/lib/StartXServer.pm
testcases/lib/TestWorker.pm
testcases/lib/i3test.pm.in
testcases/lib/i3test/Util.pm
testcases/lib/i3test/XTEST.pm
testcases/t/000-load-deps.t
testcases/t/156-fullscreen-focus.t
testcases/t/165-for_window_tilingfloating.t [deleted file]
testcases/t/173-regress-focus-assign.t [deleted file]
testcases/t/174-regress-focus-toggle.t [deleted file]
testcases/t/186-regress-assign-focus-parent.t
testcases/t/199-ipc-mode-event.t
testcases/t/200-urgency-timer.t
testcases/t/207-shmlog.t
testcases/t/208-regress-floating-criteria.t
testcases/t/211-regress-urgency-assign.t
testcases/t/213-move-branch-position.t [deleted file]
testcases/t/215-layout-restore-crash.t
testcases/t/222-regress-dock-resize.t
testcases/t/230-floating-fullscreen-restart.t
testcases/t/231-ipc-window-close.t [deleted file]
testcases/t/231-ipc-window-move.t [deleted file]
testcases/t/232-ipc-window-urgent.t [deleted file]
testcases/t/233-regress-manage-focus-unmapped.t
testcases/t/234-layout-restore-output.t [deleted file]
testcases/t/234-regress-default-floating-border.t [deleted file]
testcases/t/235-wm-class-change-handler.t [deleted file]
testcases/t/237-regress-assign-focus.t
testcases/t/238-regress-reload-bindsym.t [deleted file]
testcases/t/240-tabbed-floating-disable-crash.t [deleted file]
testcases/t/241-consistent-center.t
testcases/t/243-net-wm-state-hidden.t [deleted file]
testcases/t/244-new-workspace-floating-enable-center.t
testcases/t/248-regress-urgency-clear.t
testcases/t/251-ewmh-visible-name.t [deleted file]
testcases/t/251-sticky.t [deleted file]
testcases/t/254-move-to-output-with-criteria.t
testcases/t/257-keypress-group1-fallback.t
testcases/t/258-keypress-release.t
testcases/t/262-root-window-mouse-binding.t [deleted file]
testcases/t/263-config-reload-reverts-bind-mode.t
testcases/t/263-edge-borders.t [deleted file]
testcases/t/263-i3-floating-window-atom.t [deleted file]
testcases/t/264-dock-criteria.t
testcases/t/264-ipc-shutdown-event.t [deleted file]
testcases/t/264-keypress-numlock.t [deleted file]
testcases/t/265-swap.t [deleted file]
testcases/t/270-config-no-newline-end.t [new file with mode: 0644]
testcases/t/271-for_window_tilingfloating.t [new file with mode: 0644]
testcases/t/272-regress-focus-assign.t [new file with mode: 0644]
testcases/t/273-regress-focus-toggle.t [new file with mode: 0644]
testcases/t/274-move-branch-position.t [new file with mode: 0644]
testcases/t/275-ipc-window-close.t [new file with mode: 0644]
testcases/t/276-ipc-window-move.t [new file with mode: 0644]
testcases/t/277-ipc-window-urgent.t [new file with mode: 0644]
testcases/t/278-layout-restore-output.t [new file with mode: 0644]
testcases/t/279-regress-default-floating-border.t [new file with mode: 0644]
testcases/t/280-wm-class-change-handler.t [new file with mode: 0644]
testcases/t/281-regress-reload-bindsym.t [new file with mode: 0644]
testcases/t/282-tabbed-floating-disable-crash.t [new file with mode: 0644]
testcases/t/283-net-wm-state-hidden.t [new file with mode: 0644]
testcases/t/284-ewmh-visible-name.t [new file with mode: 0644]
testcases/t/285-sticky.t [new file with mode: 0644]
testcases/t/286-root-window-mouse-binding.t [new file with mode: 0644]
testcases/t/287-edge-borders.t [new file with mode: 0644]
testcases/t/288-i3-floating-window-atom.t [new file with mode: 0644]
testcases/t/289-ipc-shutdown-event.t [new file with mode: 0644]
testcases/t/290-keypress-numlock.t [new file with mode: 0644]
testcases/t/291-swap.t [new file with mode: 0644]
testcases/t/500-multi-monitor.t
testcases/t/501-scratchpad.t
testcases/t/502-focus-output.t
testcases/t/503-workspace.t
testcases/t/504-move-workspace-to-output.t
testcases/t/505-scratchpad-resolution.t
testcases/t/507-workspace-move-crash.t
testcases/t/509-workspace_layout.t
testcases/t/510-focus-across-outputs.t
testcases/t/511-scratchpad-configure-request.t
testcases/t/512-move-wraps.t
testcases/t/513-move-workspace.t
testcases/t/514-ipc-workspace-multi-monitor.t
testcases/t/515-create-workspace.t
testcases/t/516-move.t
testcases/t/517-regress-move-direction-ipc.t
testcases/t/518-interpret-workspace-numbers.t
testcases/t/519-mouse-warping.t
testcases/t/520-regress-focus-direction-floating.t
testcases/t/521-ewmh-desktop-viewport.t
testcases/t/522-rename-assigned-workspace.t
testcases/t/523-move-position-center.t
testcases/t/524-move.t
testcases/t/525-i3bar-mouse-bindings.t
testcases/t/526-reconfigure-dock.t
testcases/t/528-workspace-next-prev-reversed.t
testcases/t/528-workspace-next-prev.t [deleted file]
testcases/t/529-net-wm-desktop.t
testcases/t/529-net-wm-desktop_mm.t [deleted file]
testcases/t/530-bug-2229.t
testcases/t/531-fullscreen-on-given-output.t
testcases/t/533-randr15.t
testcases/t/534-dont-warp.t
testcases/t/535-workspace-next-prev.t [new file with mode: 0644]
testcases/t/536-net-wm-desktop_mm.t [new file with mode: 0644]
testcases/t/537-move-single-to-output.t [new file with mode: 0644]
travis/travis-base.Dockerfile

diff --git a/.editorconfig b/.editorconfig
new file mode 100644 (file)
index 0000000..c25745f
--- /dev/null
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+charset = utf-8
+
+[*.{c,h}]
+indent_style = space
+indent_size = 4
+trim_trailing_whitespace = true
index 75845ccdad918573d9f7b5711ecf8f9dd3474dfd..8598f8501e94d9448f0af0c80853b7694f695620 100644 (file)
@@ -9,6 +9,7 @@ use AnyEvent::Socket;
 use AnyEvent;
 use Encode;
 use Scalar::Util qw(tainted);
+use Carp;
 
 =head1 NAME
 
@@ -186,7 +187,7 @@ sub new {
         # We use getpwuid() instead of $ENV{HOME} because the latter is tainted
         # and thus produces warnings when running tests with perl -T
         my $home = (getpwuid($<))[7];
-        die "Could not get home directory" unless $home and -d $home;
+        confess "Could not get home directory" unless $home and -d $home;
         $path =~ s/~/$home/g;
     }
 
@@ -330,9 +331,9 @@ scalar), if specified.
 sub message {
     my ($self, $type, $content) = @_;
 
-    die "No message type specified" unless defined($type);
+    confess "No message type specified" unless defined($type);
 
-    die "No connection to i3" unless defined($self->{ipchdl});
+    confess "No connection to i3" unless defined($self->{ipchdl});
 
     my $payload = "";
     if ($content) {
@@ -373,7 +374,7 @@ sub _ensure_connection {
 
     return if defined($self->{ipchdl});
 
-    $self->connect->recv or die "Unable to connect to i3 (socket path " . $self->{path} . ")";
+    $self->connect->recv or confess "Unable to connect to i3 (socket path " . $self->{path} . ")";
 }
 
 =head2 get_workspaces
index 52436da67418adf62578d1637e5752a09bf96f25..d585c2d7795c954a17d2df278c4b0e52fb846e11 100644 (file)
@@ -8,6 +8,84 @@ touching i3’s source code. It should contain all important information to help
 you understand why things are like they are. If it does not mention something
 you find necessary, please do not hesitate to contact me.
 
+== Building i3
+
+You can build i3 like you build any other software package which uses autotools.
+Here’s a memory refresher:
+
+    $ autoreconf -fi
+    $ mkdir -p build && cd build
+    $ ../configure
+    $ make -j8
+
+(The autoreconf -fi step is unnecessary if you are building from a release tarball,
+ but shouldn’t hurt either.)
+
+=== Build system features
+
+* We use the AX_ENABLE_BUILDDIR macro to enforce builds happening in a separate
+  directory. This is a prerequisite for the AX_EXTEND_SRCDIR macro and building
+  in a separate directory is common practice anyway. In case this causes any
+  trouble when packaging i3 for your distribution, please open an issue.
+
+* “make check” runs the i3 testsuite. See docs/testsuite for details.
+
+* “make distcheck” (runs testsuite on “make dist” result, tiny bit quicker
+  feedback cycle than waiting for the travis build to catch the issue).
+
+* “make uninstall” (occasionally requested by users who compile from source)
+
+* “make” will build manpages/docs by default if the tools are installed.
+  Conversely, manpages/docs are not tried to be built for users who don’t want
+  to install all these dependencies to get started hacking on i3.
+
+* non-release builds will enable address sanitizer by default. Use the
+  --disable-sanitizers configure option to turn off all sanitizers, and see
+  --help for available sanitizers.
+
+* Support for pre-compiled headers (PCH) has been dropped for now in the
+  interest of simplicity. If you need support for PCH, please open an issue.
+
+* Coverage reports are now generated using “make check-code-coverage”, which
+  requires specifying --enable-code-coverage when calling configure.
+
+== Using git / sending patches
+
+For a short introduction into using git, see
+http://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy
+or, for more documentation, see http://git-scm.com/documentation
+
+Please talk to us before working on new features to see whether they will be
+accepted. A good way for this is to open an issue and asking for opinions on it.
+Even for accepted features, this can be a good way to refine an idea upfront. However,
+we don't want to see certain features in i3, e.g., switching window focus in an
+Alt+Tab like way.
+
+When working on bugfixes, please make sure you mention that you are working on
+it in the corresponding bug report at https://github.com/i3/i3/issues. In case
+there is no bug report yet, please create one.
+
+After you are done, please submit your work for review as a pull request at
+https://github.com/i3/i3.
+
+Do not send emails to the mailing list or any author directly, and don’t submit
+them in the bugtracker, since all reviews should be done in public at
+https://github.com/i3/i3. In order to make your review go as fast as possible, you
+could have a look at previous reviews and see what the common mistakes are.
+
+=== Which branch to use?
+
+Work on i3 generally happens in two branches: “master” and “next” (the latter
+being the default branch, the one that people get when they check out the git
+repository).
+
+The contents of “master” are always stable. That is, it contains the source code
+of the latest release, plus any bugfixes that were applied since that release.
+
+New features are only found in the “next” branch. Therefore, if you are working
+on a new feature, use the “next” branch. If you are working on a bugfix, use the
+“next” branch, too, but make sure your code also works on “master”.
+
 == Window Managers
 
 A window manager is not necessarily needed to run X, but it is usually used in
@@ -951,86 +1029,6 @@ Without much ado, here is the list of cases which need to be considered:
   not relative to workspace boundaries, so you must correct their coordinates
   or those containers will show up in the wrong workspace or not at all.
 
-== Using git / sending patches
-
-=== Introduction
-
-For a short introduction into using git, see
-http://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy
-or, for more documentation, see http://git-scm.com/documentation
-
-Please talk to us before working on new features to see whether they will be
-accepted. A good way for this is to open an issue and asking for opinions on it.
-Even for accepted features, this can be a good way to refine an idea upfront. However,
-we don't want to see certain features in i3, e.g., switching window focus in an
-Alt+Tab like way.
-
-When working on bugfixes, please make sure you mention that you are working on
-it in the corresponding bug report at https://github.com/i3/i3/issues. In case
-there is no bug report yet, please create one.
-
-After you are done, please submit your work for review as a pull request at
-https://github.com/i3/i3.
-
-Do not send emails to the mailing list or any author directly, and don’t submit
-them in the bugtracker, since all reviews should be done in public at
-https://github.com/i3/i3. In order to make your review go as fast as possible, you
-could have a look at previous reviews and see what the common mistakes are.
-
-=== Which branch to use?
-
-Work on i3 generally happens in two branches: “master” and “next” (the latter
-being the default branch, the one that people get when they check out the git
-repository).
-
-The contents of “master” are always stable. That is, it contains the source code
-of the latest release, plus any bugfixes that were applied since that release.
-
-New features are only found in the “next” branch. Therefore, if you are working
-on a new feature, use the “next” branch. If you are working on a bugfix, use the
-“next” branch, too, but make sure your code also works on “master”.
-
-=== How to build?
-
-You can build i3 like you build any other software package which uses autotools.
-Here’s a memory refresher:
-
-    $ autoreconf -fi
-    $ mkdir -p build && cd build
-    $ ../configure
-    $ make -j8
-
-(The autoreconf -fi step is unnecessary if you are building from a release tarball,
- but shouldn’t hurt either.)
-
-==== Build system features
-
-* We use the AX_ENABLE_BUILDDIR macro to enforce builds happening in a separate
-  directory. This is a prerequisite for the AX_EXTEND_SRCDIR macro and building
-  in a separate directory is common practice anyway. In case this causes any
-  trouble when packaging i3 for your distribution, please open an issue.
-
-* “make check” runs the i3 testsuite. See docs/testsuite for details.
-
-* “make distcheck” (runs testsuite on “make dist” result, tiny bit quicker
-  feedback cycle than waiting for the travis build to catch the issue).
-
-* “make uninstall” (occasionally requested by users who compile from source)
-
-* “make” will build manpages/docs by default if the tools are installed.
-  Conversely, manpages/docs are not tried to be built for users who don’t want
-  to install all these dependencies to get started hacking on i3.
-
-* non-release builds will enable address sanitizer by default. Use the
-  --disable-sanitizers configure option to turn off all sanitizers, and see
-  --help for available sanitizers.
-
-* Support for pre-compiled headers (PCH) has been dropped for now in the
-  interest of simplicity. If you need support for PCH, please open an issue.
-
-* Coverage reports are now generated using “make check-code-coverage”, which
-  requires specifying --enable-code-coverage when calling configure.
-
 == Thought experiments
 
 In this section, we collect thought experiments, so that we don’t forget our
index 4fa3e9e1dcaf650a5273fab5a455d4a29320f679..4f9cdbab06798d609cd1d06cbf528438ba837a11 100644 (file)
@@ -83,6 +83,7 @@ $ cd ~/i3/testcases
 $ sudo apt-get install cpanminus
 $ sudo cpanm .
 $ cd ~/i3/AnyEvent-I3
+$ sudo cpanm Module::Install
 $ sudo cpanm .
 --------------------------------------------------------------------------------
 
@@ -93,6 +94,7 @@ If you don’t want to use cpanminus for some reason, the same works with cpan:
 $ cd ~/i3/testcases
 $ sudo cpan .
 $ cd ~/i3/AnyEvent-I3
+$ sudo cpan Module::Install
 $ sudo cpan .
 --------------------------------------------------------------------------------
 
index 52165e2dffe3b46013af118e81f5e268bd8fe81f..cc9b5a01ae850219f26b6f7c1f0be4b7ccf51536 100644 (file)
@@ -875,6 +875,18 @@ The 'output' is the name of the RandR output you attach your screen to. On a
 laptop, you might have VGA1 and LVDS1 as output names. You can see the
 available outputs by running +xrandr --current+.
 
+If your X server supports RandR 1.5 or newer, i3 will use RandR monitor objects
+instead of output objects. Run +xrandr --listmonitors+ to see a list. Usually,
+a monitor object contains exactly one output, and has the same name as the
+output; but should that not be the case, you may specify the name of either the
+monitor or the output in i3's configuration. For example, the Dell UP2414Q uses
+two scalers internally, so its output names might be “DP1” and “DP2”, but the
+monitor name is “Dell UP2414Q”.
+
+(Note that even if you specify the name of an output which doesn't span the
+entire monitor, i3 will still use the entire area of the containing monitor
+rather than that of just the output's.)
+
 If you use named workspaces, they must be quoted:
 
 *Examples*:
@@ -1955,7 +1967,6 @@ bindsym $mod+x focus output HDMI-2
 bindsym $mod+x focus output primary
 -------------------------------------------------
 
--------------------------------
 Note that you might not have a primary output configured yet. To do so, run:
 -------------------------
 xrandr --output <output> --primary
index 1c7bb9322a3fa995ab7a0722ace0a3a3fc2e595a..6cd1ef3e41eca1ad90c3af125ecae7fa10161675 100644 (file)
@@ -25,6 +25,12 @@ Con *con_new_skeleton(Con *parent, i3Window *window);
  */
 Con *con_new(Con *parent, i3Window *window);
 
+/**
+ * Frees the specified container.
+ *
+ */
+void con_free(Con *con);
+
 /**
  * Sets input focus to the given container. Will be updated in X11 in the next
  * run of x_push_changes().
@@ -96,7 +102,7 @@ Con *con_get_output(Con *con);
 Con *con_get_workspace(Con *con);
 
 /**
- * Searches parenst of the given 'con' until it reaches one with the specified
+ * Searches parents of the given 'con' until it reaches one with the specified
  * 'orientation'. Aborts when it comes across a floating_con.
  *
  */
index 54333e049e2b5789ad4ec922f784037cd08eb78e..7411ac20cdb5780627068ace61fa95e54b43c9b4 100644 (file)
@@ -349,6 +349,13 @@ struct Autostart {
     autostarts_always;
 };
 
+struct output_name {
+    char *name;
+
+    SLIST_ENTRY(output_name)
+    names;
+};
+
 /**
  * An Output is a physical output on your graphics driver. Outputs which
  * are currently in use have (output->active == true). Each output has a
@@ -370,8 +377,11 @@ struct xoutput {
     bool to_be_disabled;
     bool primary;
 
-    /** Name of the output */
-    char *name;
+    /** List of names for the output.
+     * An output always has at least one name; the first name is
+     * considered the primary one. */
+    SLIST_HEAD(names_head, output_name)
+    names_head;
 
     /** Pointer to the Con which represents this output */
     Con *con;
index 0dd8131842ba2adb3f75f7b11a5151e6c41d406a..9205800f6fd3c4d390db4119010a702f3b6cd3d8 100644 (file)
@@ -31,6 +31,12 @@ typedef enum {
  * determine whether the file contains workspaces or regular containers, which
  * is important to know when deciding where (and how) to append the contents.
  * */
-json_content_t json_determine_content(const char *filename);
+json_content_t json_determine_content(const char *buf, const size_t len);
 
-void tree_append_json(Con *con, const char *filename, char **errormsg);
+/**
+ * Returns true if the provided JSON could be parsed by yajl.
+ *
+ */
+bool json_validate(const char *buf, const size_t len);
+
+void tree_append_json(Con *con, const char *buf, const size_t len, char **errormsg);
index b13c97283ff840cf5bc4eb7509118ef2d45565ea..31084da19772c74e743d7025f79a0ef6afd772cd 100644 (file)
@@ -24,6 +24,12 @@ Con *output_get_content(Con *output);
  */
 Output *get_output_from_string(Output *current_output, const char *output_str);
 
+/**
+ * Retrieves the primary name of an output.
+ *
+ */
+char *output_primary_name(Output *output);
+
 /**
  * Returns the output for the given con.
  *
index 6c5fc4c659eb1c5f7230a143ad314aa6dae2de64..de6fa56850eb8f0ba8ba89c941b921a13796b61c 100644 (file)
@@ -164,3 +164,11 @@ void kill_nagbar(pid_t *nagbar_pid, bool wait_for_it);
  * if the number could be parsed.
  */
 bool parse_long(const char *str, long *out, int base);
+
+/**
+ * Slurp reads path in its entirety into buf, returning the length of the file
+ * or -1 if the file could not be read. buf is set to a buffer of appropriate
+ * size, or NULL if -1 is returned.
+ *
+ */
+ssize_t slurp(const char *path, char **buf);
index faee3916ff03ea5e209890785d0bb872c1a87756..2697d6e1255f7e1e49b72de6d9bbc3054795af95 100644 (file)
@@ -773,13 +773,25 @@ void cmd_append_layout(I3_CMD, const char *cpath) {
     /* Make sure we allow paths like '~/.i3/layout.json' */
     path = resolve_tilde(path);
 
-    json_content_t content = json_determine_content(path);
+    char *buf = NULL;
+    ssize_t len;
+    if ((len = slurp(path, &buf)) < 0) {
+        /* slurp already logged an error. */
+        goto out;
+    }
+
+    if (!json_validate(buf, len)) {
+        ELOG("Could not parse \"%s\" as JSON, not loading.\n", path);
+        yerror("Could not parse \"%s\" as JSON.", path);
+        goto out;
+    }
+
+    json_content_t content = json_determine_content(buf, len);
     LOG("JSON content = %d\n", content);
     if (content == JSON_CONTENT_UNKNOWN) {
         ELOG("Could not determine the contents of \"%s\", not loading.\n", path);
         yerror("Could not determine the contents of \"%s\".", path);
-        free(path);
-        return;
+        goto out;
     }
 
     Con *parent = focused;
@@ -795,7 +807,7 @@ void cmd_append_layout(I3_CMD, const char *cpath) {
     }
     DLOG("Appending to parent=%p instead of focused=%p\n", parent, focused);
     char *errormsg = NULL;
-    tree_append_json(parent, path, &errormsg);
+    tree_append_json(parent, buf, len, &errormsg);
     if (errormsg != NULL) {
         yerror(errormsg);
         free(errormsg);
@@ -820,8 +832,10 @@ void cmd_append_layout(I3_CMD, const char *cpath) {
     if (content == JSON_CONTENT_WORKSPACE)
         ipc_send_workspace_event("restored", parent, NULL);
 
-    free(path);
     cmd_output->needs_tree_render = true;
+out:
+    free(path);
+    free(buf);
 }
 
 /*
index b520c110bb2530833a985c4f9aeba77b6b7df7f7..04aacd32578ed554869acbf2b85aed52976b2f99 100644 (file)
--- a/src/con.c
+++ b/src/con.c
@@ -73,6 +73,30 @@ Con *con_new(Con *parent, i3Window *window) {
     return new;
 }
 
+/*
+ * Frees the specified container.
+ *
+ */
+void con_free(Con *con) {
+    free(con->name);
+    FREE(con->deco_render_params);
+    TAILQ_REMOVE(&all_cons, con, all_cons);
+    while (!TAILQ_EMPTY(&(con->swallow_head))) {
+        Match *match = TAILQ_FIRST(&(con->swallow_head));
+        TAILQ_REMOVE(&(con->swallow_head), match, matches);
+        match_free(match);
+        free(match);
+    }
+    while (!TAILQ_EMPTY(&(con->marks_head))) {
+        mark_t *mark = TAILQ_FIRST(&(con->marks_head));
+        TAILQ_REMOVE(&(con->marks_head), mark, marks);
+        FREE(mark->name);
+        FREE(mark);
+    }
+    free(con);
+    DLOG("con %p freed\n", con);
+}
+
 static void _con_attach(Con *con, Con *parent, Con *previous, bool ignore_focus) {
     con->parent = parent;
     Con *loop;
@@ -379,7 +403,7 @@ Con *con_get_workspace(Con *con) {
 }
 
 /*
- * Searches parenst of the given 'con' until it reaches one with the specified
+ * Searches parents of the given 'con' until it reaches one with the specified
  * 'orientation'. Aborts when it comes across a floating_con.
  *
  */
@@ -1264,7 +1288,7 @@ void con_move_to_output(Con *con, Output *output) {
     Con *ws = NULL;
     GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child));
     assert(ws != NULL);
-    DLOG("Moving con %p to output %s\n", con, output->name);
+    DLOG("Moving con %p to output %s\n", con, output_primary_name(output));
     con_move_to_workspace(con, ws, false, false, false);
 }
 
index c88e9d1e3c2e7919b277f95b9eabf4e2d8885654..58a5552ceb27d77d7f267004cf457a636c902fe1 100644 (file)
@@ -1025,7 +1025,7 @@ bool parse_file(const char *f, bool use_nagbar) {
     /* Then, allocate a new buffer and copy the file over to the new one,
      * but replace occurrences of our variables */
     char *walk = buf, *destwalk;
-    char *new = smalloc(stbuf.st_size + extra_bytes + 1);
+    char *new = scalloc(stbuf.st_size + extra_bytes + 1, 1);
     destwalk = new;
     while (walk < (buf + stbuf.st_size)) {
         /* Find the next variable */
index b898ce987db9120498870056eb1e2ab15fb72b86..6639b3611005b3a393a1c493558724ff0122490c 100644 (file)
@@ -47,9 +47,12 @@ void fake_outputs_init(const char *output_spec) {
             new_output->rect.width = min(new_output->rect.width, width);
             new_output->rect.height = min(new_output->rect.height, height);
         } else {
+            struct output_name *output_name = scalloc(1, sizeof(struct output_name));
             new_output = scalloc(1, sizeof(Output));
-            sasprintf(&(new_output->name), "fake-%d", num_screens);
-            DLOG("Created new fake output %s (%p)\n", new_output->name, new_output);
+            sasprintf(&(output_name->name), "fake-%d", num_screens);
+            SLIST_INIT(&(new_output->names_head));
+            SLIST_INSERT_HEAD(&(new_output->names_head), output_name, names);
+            DLOG("Created new fake output %s (%p)\n", output_primary_name(new_output), new_output);
             new_output->active = true;
             new_output->rect.x = x;
             new_output->rect.y = y;
index c273e1161285df78230f7c8c3551d61ea74c6505..8d500fd9f24d7b177d30da82dc01a3babf3ed860 100644 (file)
@@ -388,7 +388,7 @@ static void handle_configure_request(xcb_configure_request_event_t *event) {
             Con *current_output = con_get_output(con);
             Output *target = get_output_containing(x, y);
             if (target != NULL && current_output != target->con) {
-                DLOG("Dock client is requested to be moved to output %s, moving it there.\n", target->name);
+                DLOG("Dock client is requested to be moved to output %s, moving it there.\n", output_primary_name(target));
                 Match *match;
                 Con *nc = con_for_window(target->con, con->window, &match);
                 DLOG("Dock client will be moved to container %p.\n", nc);
index 18d6075d09901a54b2414c26b60d9da43d3baa96..274f60106bd2eb67fb4562a40f62aa053b405291 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -579,6 +579,11 @@ static void dump_bar_bindings(yajl_gen gen, Barconfig *config) {
     y(array_close);
 }
 
+static char *canonicalize_output_name(char *name) {
+    Output *output = get_output_by_name(name, false);
+    return output ? output_primary_name(output) : name;
+}
+
 static void dump_bar_config(yajl_gen gen, Barconfig *config) {
     y(map_open);
 
@@ -588,8 +593,13 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
     if (config->num_outputs > 0) {
         ystr("outputs");
         y(array_open);
-        for (int c = 0; c < config->num_outputs; c++)
-            ystr(config->outputs[c]);
+        for (int c = 0; c < config->num_outputs; c++) {
+            /* Convert monitor names (RandR ≥ 1.5) or output names
+             * (RandR < 1.5) into monitor names. This way, existing
+             * configs which use output names transparently keep
+             * working. */
+            ystr(canonicalize_output_name(config->outputs[c]));
+        }
         y(array_close);
     }
 
@@ -599,7 +609,7 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
 
         struct tray_output_t *tray_output;
         TAILQ_FOREACH(tray_output, &(config->tray_outputs), tray_outputs) {
-            ystr(tray_output->output);
+            ystr(canonicalize_output_name(tray_output->output));
         }
 
         y(array_close);
@@ -829,7 +839,7 @@ IPC_HANDLER(get_outputs) {
         y(map_open);
 
         ystr("name");
-        ystr(output->name);
+        ystr(output_primary_name(output));
 
         ystr("active");
         y(bool, output->active);
index 7961e17f96a2ef75e420014d99802634c75e995d..071b3ccd650a1833fbf4efdb4d7d91fe4649dff2 100644 (file)
@@ -18,6 +18,7 @@
 /* TODO: refactor the whole parsing thing */
 
 static char *last_key;
+static int incomplete;
 static Con *json_node;
 static Con *to_focus;
 static bool parsing_swallows;
@@ -68,6 +69,9 @@ static int json_start_map(void *ctx) {
                 json_node->name = NULL;
                 json_node->parent = parent;
             }
+            /* json_node is incomplete and should be removed if parsing fails */
+            incomplete++;
+            DLOG("incomplete = %d\n", incomplete);
         }
     }
     return 1;
@@ -166,6 +170,8 @@ static int json_end_map(void *ctx) {
         LOG("Creating window\n");
         x_con_init(json_node);
         json_node = json_node->parent;
+        incomplete--;
+        DLOG("incomplete = %d\n", incomplete);
     }
 
     if (parsing_swallows && swallow_is_empty) {
@@ -532,36 +538,42 @@ static int json_determine_content_string(void *ctx, const unsigned char *val, si
     return 0;
 }
 
+/*
+ * Returns true if the provided JSON could be parsed by yajl.
+ *
+ */
+bool json_validate(const char *buf, const size_t len) {
+    bool valid = true;
+    yajl_handle hand = yajl_alloc(NULL, NULL, NULL);
+    /* Allowing comments allows for more user-friendly layout files. */
+    yajl_config(hand, yajl_allow_comments, true);
+    /* Allow multiple values, i.e. multiple nodes to attach */
+    yajl_config(hand, yajl_allow_multiple_values, true);
+
+    setlocale(LC_NUMERIC, "C");
+    if (yajl_parse(hand, (const unsigned char *)buf, len) != yajl_status_ok) {
+        unsigned char *str = yajl_get_error(hand, 1, (const unsigned char *)buf, len);
+        ELOG("JSON parsing error: %s\n", str);
+        yajl_free_error(hand, str);
+        valid = false;
+    }
+    setlocale(LC_NUMERIC, "");
+
+    yajl_complete_parse(hand);
+    yajl_free(hand);
+
+    return valid;
+}
+
 /* Parses the given JSON file until it encounters the first “type” property to
  * determine whether the file contains workspaces or regular containers, which
  * is important to know when deciding where (and how) to append the contents.
  * */
-json_content_t json_determine_content(const char *filename) {
-    FILE *f;
-    if ((f = fopen(filename, "r")) == NULL) {
-        ELOG("Cannot open file \"%s\"\n", filename);
-        return JSON_CONTENT_UNKNOWN;
-    }
-    struct stat stbuf;
-    if (fstat(fileno(f), &stbuf) != 0) {
-        ELOG("Cannot fstat() \"%s\"\n", filename);
-        fclose(f);
-        return JSON_CONTENT_UNKNOWN;
-    }
-    char *buf = smalloc(stbuf.st_size);
-    int n = fread(buf, 1, stbuf.st_size, f);
-    if (n != stbuf.st_size) {
-        ELOG("File \"%s\" could not be read entirely, not loading.\n", filename);
-        fclose(f);
-        return JSON_CONTENT_UNKNOWN;
-    }
-    DLOG("read %d bytes\n", n);
+json_content_t json_determine_content(const char *buf, const size_t len) {
     // We default to JSON_CONTENT_CON because it is legal to not include
     // “"type": "con"” in the JSON files for better readability.
     content_result = JSON_CONTENT_CON;
     content_level = 0;
-    yajl_gen g;
-    yajl_handle hand;
     static yajl_callbacks callbacks = {
         .yajl_string = json_determine_content_string,
         .yajl_map_key = json_key,
@@ -570,51 +582,27 @@ json_content_t json_determine_content(const char *filename) {
         .yajl_end_map = json_determine_content_shallower,
         .yajl_end_array = json_determine_content_shallower,
     };
-    g = yajl_gen_alloc(NULL);
-    hand = yajl_alloc(&callbacks, NULL, (void *)g);
+    yajl_handle hand = yajl_alloc(&callbacks, NULL, NULL);
     /* Allowing comments allows for more user-friendly layout files. */
     yajl_config(hand, yajl_allow_comments, true);
     /* Allow multiple values, i.e. multiple nodes to attach */
     yajl_config(hand, yajl_allow_multiple_values, true);
-    yajl_status stat;
     setlocale(LC_NUMERIC, "C");
-    stat = yajl_parse(hand, (const unsigned char *)buf, n);
+    const yajl_status stat = yajl_parse(hand, (const unsigned char *)buf, len);
     if (stat != yajl_status_ok && stat != yajl_status_client_canceled) {
-        unsigned char *str = yajl_get_error(hand, 1, (const unsigned char *)buf, n);
+        unsigned char *str = yajl_get_error(hand, 1, (const unsigned char *)buf, len);
         ELOG("JSON parsing error: %s\n", str);
         yajl_free_error(hand, str);
     }
 
     setlocale(LC_NUMERIC, "");
     yajl_complete_parse(hand);
-
-    fclose(f);
+    yajl_free(hand);
 
     return content_result;
 }
 
-void tree_append_json(Con *con, const char *filename, char **errormsg) {
-    FILE *f;
-    if ((f = fopen(filename, "r")) == NULL) {
-        ELOG("Cannot open file \"%s\"\n", filename);
-        return;
-    }
-    struct stat stbuf;
-    if (fstat(fileno(f), &stbuf) != 0) {
-        ELOG("Cannot fstat() \"%s\"\n", filename);
-        fclose(f);
-        return;
-    }
-    char *buf = smalloc(stbuf.st_size);
-    int n = fread(buf, 1, stbuf.st_size, f);
-    if (n != stbuf.st_size) {
-        ELOG("File \"%s\" could not be read entirely, not loading.\n", filename);
-        fclose(f);
-        return;
-    }
-    DLOG("read %d bytes\n", n);
-    yajl_gen g;
-    yajl_handle hand;
+void tree_append_json(Con *con, const char *buf, const size_t len, char **errormsg) {
     static yajl_callbacks callbacks = {
         .yajl_boolean = json_bool,
         .yajl_integer = json_int,
@@ -625,15 +613,14 @@ void tree_append_json(Con *con, const char *filename, char **errormsg) {
         .yajl_end_map = json_end_map,
         .yajl_end_array = json_end_array,
     };
-    g = yajl_gen_alloc(NULL);
-    hand = yajl_alloc(&callbacks, NULL, (void *)g);
+    yajl_handle hand = yajl_alloc(&callbacks, NULL, NULL);
     /* Allowing comments allows for more user-friendly layout files. */
     yajl_config(hand, yajl_allow_comments, true);
     /* Allow multiple values, i.e. multiple nodes to attach */
     yajl_config(hand, yajl_allow_multiple_values, true);
-    yajl_status stat;
     json_node = con;
     to_focus = NULL;
+    incomplete = 0;
     parsing_swallows = false;
     parsing_rect = false;
     parsing_deco_rect = false;
@@ -642,13 +629,19 @@ void tree_append_json(Con *con, const char *filename, char **errormsg) {
     parsing_focus = false;
     parsing_marks = false;
     setlocale(LC_NUMERIC, "C");
-    stat = yajl_parse(hand, (const unsigned char *)buf, n);
+    const yajl_status stat = yajl_parse(hand, (const unsigned char *)buf, len);
     if (stat != yajl_status_ok) {
-        unsigned char *str = yajl_get_error(hand, 1, (const unsigned char *)buf, n);
+        unsigned char *str = yajl_get_error(hand, 1, (const unsigned char *)buf, len);
         ELOG("JSON parsing error: %s\n", str);
         if (errormsg != NULL)
             *errormsg = sstrdup((const char *)str);
         yajl_free_error(hand, str);
+        while (incomplete-- > 0) {
+            Con *parent = json_node->parent;
+            DLOG("freeing incomplete container %p\n", json_node);
+            con_free(json_node);
+            json_node = parent;
+        }
     }
 
     /* In case not all containers were restored, we need to fix the
@@ -659,10 +652,8 @@ void tree_append_json(Con *con, const char *filename, char **errormsg) {
     setlocale(LC_NUMERIC, "");
     yajl_complete_parse(hand);
     yajl_free(hand);
-    yajl_gen_free(g);
 
-    fclose(f);
-    free(buf);
-    if (to_focus)
+    if (to_focus) {
         con_focus(to_focus);
+    }
 }
index 212654466954649c47628b53ecb0dbc5cca5b524..44e4517ee6678681c9a2b72b5b4c86d772a78649 100644 (file)
@@ -687,7 +687,7 @@ int main(int argc, char *argv[]) {
         TAILQ_FOREACH(con, &(croot->nodes_head), nodes) {
             Output *output;
             TAILQ_FOREACH(output, &outputs, outputs) {
-                if (output->active || strcmp(con->name, output->name) != 0)
+                if (output->active || strcmp(con->name, output_primary_name(output)) != 0)
                     continue;
 
                 /* This will correctly correlate the output with its content
index e35965481a9cb046b971c1717a3f8fb963316607..f155603d4f9a390e515f90496f4456eb4c039873 100644 (file)
@@ -219,7 +219,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
         LOG("This window is of type dock\n");
         Output *output = get_output_containing(geom->x, geom->y);
         if (output != NULL) {
-            DLOG("Starting search at output %s\n", output->name);
+            DLOG("Starting search at output %s\n", output_primary_name(output));
             search_at = output->con;
         }
 
index 70c8c7885521c52724eaa29c1ec3724ad535ed85..3ecc69e4c762c7c63e2e38ab6a9ceadaeb91edf0 100644 (file)
@@ -248,7 +248,8 @@ void tree_move(Con *con, int direction) {
                         ? AFTER
                         : BEFORE);
         insert_con_into(con, target, position);
-    } else if (con->parent->parent->type == CT_WORKSPACE &&
+    } else if (!next &&
+               con->parent->parent->type == CT_WORKSPACE &&
                con->parent->layout != L_DEFAULT &&
                con_num_children(con->parent) == 1) {
         /* Con is the lone child of a non-default layout container at the edge
index e3c54a67274e31821be38e7ecd62663b4bca363e..e76903844821c532f14cca9b132beeb18ea3af93 100644 (file)
@@ -44,6 +44,14 @@ Output *get_output_from_string(Output *current_output, const char *output_str) {
     return get_output_by_name(output_str, true);
 }
 
+/*
+ * Retrieves the primary name of an output.
+ *
+ */
+char *output_primary_name(Output *output) {
+    return SLIST_FIRST(&output->names_head)->name;
+}
+
 Output *get_output_for_con(Con *con) {
     Con *output_con = con_get_output(con);
     if (output_con == NULL) {
index 48bffb4622d1c86bbc1bb129e57a28036fc74e92..bc7916964356eb75ff760fa0dc07e5546af758da 100644 (file)
@@ -48,10 +48,18 @@ Output *get_output_by_name(const char *name, const bool require_active) {
     Output *output;
     bool get_primary = (strcasecmp("primary", name) == 0);
     TAILQ_FOREACH(output, &outputs, outputs) {
-        if ((output->primary && get_primary) ||
-            ((!require_active || output->active) && strcasecmp(output->name, name) == 0)) {
+        if (output->primary && get_primary) {
             return output;
         }
+        if (require_active && !output->active) {
+            continue;
+        }
+        struct output_name *output_name;
+        SLIST_FOREACH(output_name, &output->names_head, names) {
+            if (strcasecmp(output_name->name, name) == 0) {
+                return output;
+            }
+        }
     }
 
     return NULL;
@@ -178,7 +186,7 @@ Output *get_output_next_wrap(direction_t direction, Output *current) {
     }
     if (!best)
         best = current;
-    DLOG("current = %s, best = %s\n", current->name, best->name);
+    DLOG("current = %s, best = %s\n", output_primary_name(current), output_primary_name(best));
     return best;
 }
 
@@ -250,7 +258,7 @@ Output *get_output_next(direction_t direction, Output *current, output_close_far
         }
     }
 
-    DLOG("current = %s, best = %s\n", current->name, (best ? best->name : "NULL"));
+    DLOG("current = %s, best = %s\n", output_primary_name(current), (best ? output_primary_name(best) : "NULL"));
     return best;
 }
 
@@ -266,7 +274,11 @@ Output *create_root_output(xcb_connection_t *conn) {
     s->rect.y = 0;
     s->rect.width = root_screen->width_in_pixels;
     s->rect.height = root_screen->height_in_pixels;
-    s->name = "xroot-0";
+
+    struct output_name *output_name = scalloc(1, sizeof(struct output_name));
+    output_name->name = "xroot-0";
+    SLIST_INIT(&s->names_head);
+    SLIST_INSERT_HEAD(&s->names_head, output_name, names);
 
     return s;
 }
@@ -280,12 +292,12 @@ void output_init_con(Output *output) {
     Con *con = NULL, *current;
     bool reused = false;
 
-    DLOG("init_con for output %s\n", output->name);
+    DLOG("init_con for output %s\n", output_primary_name(output));
 
     /* Search for a Con with that name directly below the root node. There
      * might be one from a restored layout. */
     TAILQ_FOREACH(current, &(croot->nodes_head), nodes) {
-        if (strcmp(current->name, output->name) != 0)
+        if (strcmp(current->name, output_primary_name(output)) != 0)
             continue;
 
         con = current;
@@ -297,7 +309,7 @@ void output_init_con(Output *output) {
     if (con == NULL) {
         con = con_new(croot, NULL);
         FREE(con->name);
-        con->name = sstrdup(output->name);
+        con->name = sstrdup(output_primary_name(output));
         con->type = CT_OUTPUT;
         con->layout = L_OUTPUT;
         con_fix_percent(croot);
@@ -384,7 +396,7 @@ void init_ws_for_output(Output *output, Con *content) {
     /* go through all assignments and move the existing workspaces to this output */
     struct Workspace_Assignment *assignment;
     TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
-        if (strcmp(assignment->output, output->name) != 0)
+        if (strcmp(assignment->output, output_primary_name(output)) != 0)
             continue;
 
         /* check if this workspace actually exists */
@@ -402,13 +414,13 @@ void init_ws_for_output(Output *output, Con *content) {
             LOG("Workspace \"%s\" assigned to output \"%s\", but it is already "
                 "there. Do you have two assignment directives for the same "
                 "workspace in your configuration file?\n",
-                workspace->name, output->name);
+                workspace->name, output_primary_name(output));
             continue;
         }
 
         /* if so, move it over */
         LOG("Moving workspace \"%s\" from output \"%s\" to \"%s\" due to assignment\n",
-            workspace->name, workspace_out->name, output->name);
+            workspace->name, workspace_out->name, output_primary_name(output));
 
         /* if the workspace is currently visible on that output, we need to
          * switch to a different workspace - otherwise the output would end up
@@ -445,7 +457,7 @@ void init_ws_for_output(Output *output, Con *content) {
                 workspace_out->name);
             init_ws_for_output(get_output_by_name(workspace_out->name, true),
                                output_get_content(workspace_out));
-            DLOG("Done re-initializing, continuing with \"%s\"\n", output->name);
+            DLOG("Done re-initializing, continuing with \"%s\"\n", output_primary_name(output));
         }
     }
 
@@ -465,7 +477,7 @@ void init_ws_for_output(Output *output, Con *content) {
 
     /* otherwise, we create the first assigned ws for this output */
     TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
-        if (strcmp(assignment->output, output->name) != 0)
+        if (strcmp(assignment->output, output_primary_name(output)) != 0)
             continue;
 
         LOG("Initializing first assigned workspace \"%s\" for output \"%s\"\n",
@@ -594,7 +606,42 @@ static bool randr_query_outputs_15(void) {
         Output *new = get_output_by_name(name, false);
         if (new == NULL) {
             new = scalloc(1, sizeof(Output));
-            new->name = sstrdup(name);
+
+            SLIST_INIT(&new->names_head);
+
+            /* Register associated output names in addition to the monitor name */
+            xcb_randr_output_t *randr_outputs = xcb_randr_monitor_info_outputs(monitor_info);
+            int randr_output_len = xcb_randr_monitor_info_outputs_length(monitor_info);
+            for (int i = 0; i < randr_output_len; i++) {
+                xcb_randr_output_t randr_output = randr_outputs[i];
+
+                xcb_randr_get_output_info_reply_t *info =
+                    xcb_randr_get_output_info_reply(conn,
+                                                    xcb_randr_get_output_info(conn, randr_output, monitors->timestamp),
+                                                    NULL);
+
+                if (info != NULL && info->crtc != XCB_NONE) {
+                    char *oname;
+                    sasprintf(&oname, "%.*s",
+                              xcb_randr_get_output_info_name_length(info),
+                              xcb_randr_get_output_info_name(info));
+
+                    if (strcmp(name, oname) != 0) {
+                        struct output_name *output_name = scalloc(1, sizeof(struct output_name));
+                        output_name->name = sstrdup(oname);
+                        SLIST_INSERT_HEAD(&new->names_head, output_name, names);
+                    } else {
+                        free(oname);
+                    }
+                }
+                FREE(info);
+            }
+
+            /* Insert the monitor name last, so that it's used as the primary name */
+            struct output_name *output_name = scalloc(1, sizeof(struct output_name));
+            output_name->name = sstrdup(name);
+            SLIST_INSERT_HEAD(&new->names_head, output_name, names);
+
             if (monitor_info->primary) {
                 TAILQ_INSERT_HEAD(&outputs, new, outputs);
             } else {
@@ -643,16 +690,25 @@ static void handle_output(xcb_connection_t *conn, xcb_randr_output_t id,
 
     Output *new = get_output_by_id(id);
     bool existing = (new != NULL);
-    if (!existing)
+    if (!existing) {
         new = scalloc(1, sizeof(Output));
+        SLIST_INIT(&new->names_head);
+    }
     new->id = id;
     new->primary = (primary && primary->output == id);
-    FREE(new->name);
-    sasprintf(&new->name, "%.*s",
+    while (!SLIST_EMPTY(&new->names_head)) {
+        FREE(SLIST_FIRST(&new->names_head)->name);
+        struct output_name *old_head = SLIST_FIRST(&new->names_head);
+        SLIST_REMOVE_HEAD(&new->names_head, names);
+        FREE(old_head);
+    }
+    struct output_name *output_name = scalloc(1, sizeof(struct output_name));
+    sasprintf(&output_name->name, "%.*s",
               xcb_randr_get_output_info_name_length(output),
               xcb_randr_get_output_info_name(output));
+    SLIST_INSERT_HEAD(&new->names_head, output_name, names);
 
-    DLOG("found output with name %s\n", new->name);
+    DLOG("found output with name %s\n", output_primary_name(new));
 
     /* Even if no CRTC is used at the moment, we store the output so that
      * we do not need to change the list ever again (we only update the
@@ -672,7 +728,7 @@ static void handle_output(xcb_connection_t *conn, xcb_randr_output_t id,
     icookie = xcb_randr_get_crtc_info(conn, output->crtc, cts);
     if ((crtc = xcb_randr_get_crtc_info_reply(conn, icookie, NULL)) == NULL) {
         DLOG("Skipping output %s: could not get CRTC (%p)\n",
-             new->name, crtc);
+             output_primary_name(new), crtc);
         free(new);
         return;
     }
@@ -789,7 +845,7 @@ void randr_query_outputs(void) {
         if (!output->active || output->to_be_disabled)
             continue;
         DLOG("output %p / %s, position (%d, %d), checking for clones\n",
-             output, output->name, output->rect.x, output->rect.y);
+             output, output_primary_name(output), output->rect.x, output->rect.y);
 
         for (other = output;
              other != TAILQ_END(&outputs);
@@ -813,7 +869,7 @@ void randr_query_outputs(void) {
             update_if_necessary(&(other->rect.width), width);
             update_if_necessary(&(other->rect.height), height);
 
-            DLOG("disabling output %p (%s)\n", other, other->name);
+            DLOG("disabling output %p (%s)\n", other, output_primary_name(other));
             other->to_be_disabled = true;
 
             DLOG("new output mode %d x %d, other mode %d x %d\n",
@@ -828,7 +884,7 @@ void randr_query_outputs(void) {
      * LVDS1 gets disabled. */
     TAILQ_FOREACH(output, &outputs, outputs) {
         if (output->active && output->con == NULL) {
-            DLOG("Need to initialize a Con for output %s\n", output->name);
+            DLOG("Need to initialize a Con for output %s\n", output_primary_name(output));
             output_init_con(output);
             output->changed = false;
         }
@@ -854,7 +910,7 @@ void randr_query_outputs(void) {
         Con *content = output_get_content(output->con);
         if (!TAILQ_EMPTY(&(content->nodes_head)))
             continue;
-        DLOG("Should add ws for output %s\n", output->name);
+        DLOG("Should add ws for output %s\n", output_primary_name(output));
         init_ws_for_output(output, content);
     }
 
@@ -863,7 +919,7 @@ void randr_query_outputs(void) {
         if (!output->primary || !output->con)
             continue;
 
-        DLOG("Focusing primary output %s\n", output->name);
+        DLOG("Focusing primary output %s\n", output_primary_name(output));
         con_focus(con_descend_focused(output->con));
     }
 
@@ -881,7 +937,7 @@ void randr_disable_output(Output *output) {
     assert(output->to_be_disabled);
 
     output->active = false;
-    DLOG("Output %s disabled, re-assigning workspaces/docks\n", output->name);
+    DLOG("Output %s disabled, re-assigning workspaces/docks\n", output_primary_name(output));
 
     Output *first = get_first_output();
 
index 2d4647f8dfba08b7d6c97abe3fc1588119d905a0..b3d2ce93c04408e3d8e6b87318b2fefa018baccb 100644 (file)
@@ -64,12 +64,19 @@ static Con *_create___i3(void) {
  *
  */
 bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry) {
+    bool result = false;
     char *globbed = resolve_tilde(path);
 
     if (!path_exists(globbed)) {
         LOG("%s does not exist, not restoring tree\n", globbed);
-        free(globbed);
-        return false;
+        goto out;
+    }
+
+    char *buf = NULL;
+    ssize_t len;
+    if ((len = slurp(globbed, &buf)) < 0) {
+        /* slurp already logged an error. */
+        goto out;
     }
 
     /* TODO: refactor the following */
@@ -81,8 +88,7 @@ bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry) {
         geometry->height};
     focused = croot;
 
-    tree_append_json(focused, globbed, NULL);
-    free(globbed);
+    tree_append_json(focused, buf, len, NULL);
 
     DLOG("appended tree, using new root\n");
     croot = TAILQ_FIRST(&(croot->nodes_head));
@@ -104,8 +110,12 @@ bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry) {
     }
 
     restore_open_placeholder_windows(croot);
+    result = true;
 
-    return true;
+out:
+    free(globbed);
+    free(buf);
+    return result;
 }
 
 /*
@@ -320,22 +330,7 @@ bool tree_close_internal(Con *con, kill_window_t kill_window, bool dont_kill_par
         DLOG("parent container killed\n");
     }
 
-    free(con->name);
-    FREE(con->deco_render_params);
-    TAILQ_REMOVE(&all_cons, con, all_cons);
-    while (!TAILQ_EMPTY(&(con->swallow_head))) {
-        Match *match = TAILQ_FIRST(&(con->swallow_head));
-        TAILQ_REMOVE(&(con->swallow_head), match, matches);
-        match_free(match);
-        free(match);
-    }
-    while (!TAILQ_EMPTY(&(con->marks_head))) {
-        mark_t *mark = TAILQ_FIRST(&(con->marks_head));
-        TAILQ_REMOVE(&(con->marks_head), mark, marks);
-        FREE(mark->name);
-        FREE(mark);
-    }
-    free(con);
+    con_free(con);
 
     /* in the case of floating windows, we already focused another container
      * when closing the parent, so we can exit now. */
@@ -537,7 +532,7 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
 
         if (!current_output)
             return false;
-        DLOG("Current output is %s\n", current_output->name);
+        DLOG("Current output is %s\n", output_primary_name(current_output));
 
         /* Try to find next output */
         direction_t direction;
@@ -555,7 +550,7 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
         next_output = get_output_next(direction, current_output, CLOSEST_OUTPUT);
         if (!next_output)
             return false;
-        DLOG("Next output is %s\n", next_output->name);
+        DLOG("Next output is %s\n", output_primary_name(next_output));
 
         /* Find visible workspace on next output */
         Con *workspace = NULL;
index 32c3c57e15d5bd2f206bab5a1984a00e19dff06a..cd5ee03e49e607f689f265a95bcb9acc64cbdbcc 100644 (file)
@@ -475,3 +475,35 @@ bool parse_long(const char *str, long *out, int base) {
     *out = result;
     return true;
 }
+
+/*
+ * Slurp reads path in its entirety into buf, returning the length of the file
+ * or -1 if the file could not be read. buf is set to a buffer of appropriate
+ * size, or NULL if -1 is returned.
+ *
+ */
+ssize_t slurp(const char *path, char **buf) {
+    FILE *f;
+    if ((f = fopen(path, "r")) == NULL) {
+        ELOG("Cannot open file \"%s\": %s\n", path, strerror(errno));
+        return -1;
+    }
+    struct stat stbuf;
+    if (fstat(fileno(f), &stbuf) != 0) {
+        ELOG("Cannot fstat() \"%s\": %s\n", path, strerror(errno));
+        fclose(f);
+        return -1;
+    }
+    /* Allocate one extra NUL byte to make the buffer usable with C string
+     * functions. yajl doesn’t need this, but this makes slurp safer. */
+    *buf = scalloc(stbuf.st_size + 1, 1);
+    size_t n = fread(*buf, 1, stbuf.st_size, f);
+    fclose(f);
+    if ((ssize_t)n != stbuf.st_size) {
+        ELOG("File \"%s\" could not be read entirely: got %zd, want %zd\n", path, n, stbuf.st_size);
+        free(buf);
+        *buf = NULL;
+        return -1;
+    }
+    return (ssize_t)n;
+}
index d7f2ce7c55e8ce631b18a2f41aede336dc48983c..4b350b822730938ea4f0d6f73ef1b34b9d6e2909 100644 (file)
@@ -188,7 +188,7 @@ Con *create_workspace_on_output(Output *output, Con *content) {
         struct Workspace_Assignment *assignment;
         TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
             if (strcmp(assignment->name, target_name) != 0 ||
-                strcmp(assignment->output, output->name) == 0)
+                strcmp(assignment->output, output_primary_name(output)) == 0)
                 continue;
 
             assigned = true;
@@ -935,7 +935,7 @@ bool workspace_move_to_output(Con *ws, const char *name) {
         bool used_assignment = false;
         struct Workspace_Assignment *assignment;
         TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
-            if (assignment->output == NULL || strcmp(assignment->output, current_output->name) != 0)
+            if (assignment->output == NULL || strcmp(assignment->output, output_primary_name(current_output)) != 0)
                 continue;
 
             /* check if this workspace is already attached to the tree */
index 25bfa6b194970257fffeaa90cc4365605dbc3acb..d0651a851dadf9acf0787c4502d67564a941b474 100644 (file)
@@ -55,8 +55,11 @@ static void query_screens(xcb_connection_t *conn) {
             s->rect.height = min(s->rect.height, screen_info[screen].height);
         } else {
             s = scalloc(1, sizeof(Output));
-            sasprintf(&(s->name), "xinerama-%d", num_screens);
-            DLOG("Created new Xinerama screen %s (%p)\n", s->name, s);
+            struct output_name *output_name = scalloc(1, sizeof(struct output_name));
+            sasprintf(&output_name->name, "xinerama-%d", num_screens);
+            SLIST_INIT(&s->names_head);
+            SLIST_INSERT_HEAD(&s->names_head, output_name, names);
+            DLOG("Created new Xinerama screen %s (%p)\n", output_primary_name(s), s);
             s->active = true;
             s->rect.x = screen_info[screen].x_org;
             s->rect.y = screen_info[screen].y_org;
index 5796ef05519edf5b6ed34b751e6fbdbb4eccfdaf..6cccfa762d1a09b12a9704711d5c25fbcdadcc9b 100644 (file)
@@ -41,9 +41,14 @@ void cleanup_socket(void) {
     }
 }
 
+struct injected_reply {
+    void *buf;
+    off_t len;
+};
+
 /* BEGIN RandR 1.5 specific */
-static void *injected_reply = NULL;
-static off_t injected_reply_len = 0;
+static struct injected_reply getmonitors_reply = {NULL, 0};
+static struct injected_reply getoutputinfo_reply = {NULL, 0};
 /* END RandR 1.5 specific */
 
 #define XCB_PAD(i) (-(i)&3)
@@ -66,6 +71,8 @@ struct connstate {
     int getext_randr;
     /* sequence number of the most recent RRGetMonitors request */
     int getmonitors;
+    /* sequence number of the most recent RRGetOutputInfo request */
+    int getoutputinfo;
 
     int randr_major_opcode;
     /* END RandR 1.5 specific */
@@ -259,6 +266,8 @@ static void read_client_x11_packet_cb(EV_P_ ev_io *w, int revents) {
         const uint8_t randr_opcode = ((generic_x11_request_t *)request)->pad0;
         if (randr_opcode == XCB_RANDR_GET_MONITORS) {
             connstate->getmonitors = connstate->sequence;
+        } else if (randr_opcode == XCB_RANDR_GET_OUTPUT_INFO) {
+            connstate->getoutputinfo = connstate->sequence;
         }
     }
     /* END RandR 1.5 specific */
@@ -267,6 +276,32 @@ static void read_client_x11_packet_cb(EV_P_ ev_io *w, int revents) {
     free(request);
 }
 
+static bool handle_sequence(struct connstate *connstate, uint16_t sequence) {
+    /* BEGIN RandR 1.5 specific */
+    if (sequence == connstate->getmonitors) {
+        printf("RRGetMonitors reply!\n");
+        if (getmonitors_reply.buf != NULL) {
+            printf("injecting reply\n");
+            ((generic_x11_reply_t *)getmonitors_reply.buf)->sequence = sequence;
+            must_write(writeall(connstate->clientw->fd, getmonitors_reply.buf, getmonitors_reply.len));
+            return true;
+        }
+    }
+
+    if (sequence == connstate->getoutputinfo) {
+        printf("RRGetOutputInfo reply!\n");
+        if (getoutputinfo_reply.buf != NULL) {
+            printf("injecting reply\n");
+            ((generic_x11_reply_t *)getoutputinfo_reply.buf)->sequence = sequence;
+            must_write(writeall(connstate->clientw->fd, getoutputinfo_reply.buf, getoutputinfo_reply.len));
+            return true;
+        }
+    }
+    /* END RandR 1.5 specific */
+
+    return false;
+}
+
 static void read_server_x11_packet_cb(EV_P_ ev_io *w, int revents) {
     struct connstate *connstate = (struct connstate *)w->data;
     // all packets from the server are at least 32 bytes in length
@@ -274,9 +309,14 @@ static void read_server_x11_packet_cb(EV_P_ ev_io *w, int revents) {
     void *packet = smalloc(len);
     must_read(readall_into(packet, len, connstate->serverw->fd));
     switch (((generic_x11_reply_t *)packet)->code) {
-        case 0:  // error
+        case 0: {  // error
+            const uint16_t sequence = ((xcb_request_error_t *)packet)->sequence;
+            if (handle_sequence(connstate, sequence)) {
+                free(packet);
+                return;
+            }
             break;
-
+        }
         case 1:  // reply
             len += ((generic_x11_reply_t *)packet)->length * 4;
             if (len > 32) {
@@ -291,18 +331,12 @@ static void read_server_x11_packet_cb(EV_P_ ev_io *w, int revents) {
                 xcb_query_extension_reply_t *reply = packet;
                 connstate->randr_major_opcode = reply->major_opcode;
             }
+            /* END RandR 1.5 specific */
 
-            if (sequence == connstate->getmonitors) {
-                printf("RRGetMonitors reply!\n");
-                if (injected_reply != NULL) {
-                    printf("injecting reply\n");
-                    ((generic_x11_reply_t *)injected_reply)->sequence = sequence;
-                    must_write(writeall(connstate->clientw->fd, injected_reply, injected_reply_len));
-                    free(packet);
-                    return;
-                }
+            if (handle_sequence(connstate, sequence)) {
+                free(packet);
+                return;
             }
-            /* END RandR 1.5 specific */
 
             break;
 
@@ -322,7 +356,7 @@ static void child_cb(EV_P_ ev_child *w, int revents) {
     }
 }
 
-static void must_read_reply(const char *filename) {
+static void must_read_reply(const char *filename, struct injected_reply *reply) {
     FILE *f;
     if ((f = fopen(filename, "r")) == NULL) {
         err(EXIT_FAILURE, "fopen(%s)", filename);
@@ -331,11 +365,9 @@ static void must_read_reply(const char *filename) {
     if (fstat(fileno(f), &stbuf) != 0) {
         err(EXIT_FAILURE, "fstat(%s)", filename);
     }
-    /* BEGIN RandR 1.5 specific */
-    injected_reply_len = stbuf.st_size;
-    injected_reply = smalloc(stbuf.st_size);
-    int n = fread(injected_reply, 1, stbuf.st_size, f);
-    /* END RandR 1.5 specific */
+    reply->len = stbuf.st_size;
+    reply->buf = smalloc(stbuf.st_size);
+    int n = fread(reply->buf, 1, stbuf.st_size, f);
     if (n != stbuf.st_size) {
         err(EXIT_FAILURE, "fread(%s)", filename);
     }
@@ -345,6 +377,7 @@ static void must_read_reply(const char *filename) {
 int main(int argc, char *argv[]) {
     static struct option long_options[] = {
         {"getmonitors_reply", required_argument, 0, 0},
+        {"getoutputinfo_reply", required_argument, 0, 0},
         {0, 0, 0, 0},
     };
     char *options_string = "";
@@ -353,11 +386,15 @@ int main(int argc, char *argv[]) {
 
     while ((opt = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
         switch (opt) {
-            case 0:
-                if (strcmp(long_options[option_index].name, "getmonitors_reply") == 0) {
-                    must_read_reply(optarg);
+            case 0: {
+                const char *option_name = long_options[option_index].name;
+                if (strcmp(option_name, "getmonitors_reply") == 0) {
+                    must_read_reply(optarg, &getmonitors_reply);
+                } else if (strcmp(option_name, "getoutputinfo_reply") == 0) {
+                    must_read_reply(optarg, &getoutputinfo_reply);
                 }
                 break;
+            }
             default:
                 exit(EXIT_FAILURE);
         }
index 0f307eb3366abc04c230f068ee050fd9347d6a70..5a5a4484262b349e7c8c808b9711e9f84222441c 100644 (file)
@@ -49,6 +49,11 @@ sub activate_i3 {
         die "could not fork()";
     }
     if ($pid == 0) {
+        # Start a process group so that in the parent, we can kill the entire
+        # process group and immediately kill i3bar and any other child
+        # processes.
+        setpgrp;
+
         $ENV{LISTEN_PID} = $$;
         $ENV{LISTEN_FDS} = 1;
         delete $ENV{DESKTOP_STARTUP_ID};
@@ -145,7 +150,12 @@ sub activate_i3 {
         if ($args{inject_randr15}) {
             # See comment in $args{strace} branch.
             $cmd = 'test.inject_randr15 --getmonitors_reply="' .
-                   $args{inject_randr15} . '" -- ' .
+                   $args{inject_randr15} . '" ' .
+                   ($args{inject_randr15_outputinfo}
+                    ? '--getoutputinfo_reply="' .
+                      $args{inject_randr15_outputinfo} . '" '
+                    : '') .
+                   '-- ' .
                    'sh -c "export LISTEN_PID=\$\$; ' . $cmd . '"';
         }
 
index d3042c07195ead9543266a592036e49772b7dbc2..49976394f2bc93a2f47474343da05a88396c3778 100644 (file)
@@ -87,7 +87,7 @@ sub start_xserver {
 
     # First get the last used display number, then increment it by one.
     # Effectively falls back to 1 if no X server is running.
-    my ($displaynum) = map { /(\d+)$/ } reverse sort glob($x_socketpath . '*');
+    my ($displaynum) = reverse sort { $a <=> $b } map{ /(\d+)$/ } glob($x_socketpath . '*');
     $displaynum++;
 
     say "Starting $parallel Xephyr instances, starting at :$displaynum...";
@@ -105,7 +105,7 @@ sub start_xserver {
     for (1 .. $parallel) {
         my $socket = fork_xserver($keep_xserver_output, $displaynum,
                 'Xephyr', ":$displaynum", '-screen', '1280x800',
-                '-nolisten', 'tcp');
+                '-nolisten', 'tcp', '-name', "i3test");
         push(@displays, ":$displaynum");
         push(@sockets_waiting, $socket);
         $displaynum++;
index c56767c4172fec49279cd0c6a55dc153fed69ecc..9716f7f1966bdc355fe0110d4791695617fc2921 100644 (file)
@@ -47,12 +47,9 @@ sub worker {
 
         $worker->{ipc} = $ipc_child;
 
+        # Preload the i3test module: reduces user CPU from 25s to 18s
         require i3test;
-        # TODO: recycle $x
-        # unfortunately this fails currently with:
-        # Could not get reply for: xcb_intern_atom_reply at X11/XCB/Atom.pm line 22.
 
-        # $i3test::x = bless $x, 'i3test::X11';
         worker_wait($worker, $outdir);
         exit 23;
 
@@ -86,11 +83,11 @@ sub worker_wait {
 
         exit unless $file;
 
-        die "tried to launch nonexistend testfile $file: $!\n"
+        die "tried to launch nonexistent testfile $file: $!\n"
             unless -e $file;
 
         # start a new and self contained process:
-        # whatever happens in the testfile should *NOT* effect us.
+        # whatever happens in the testfile should *NOT* affect us.
 
         my $pid = fork // die "could not fork: $!";
         if ($pid == 0) {
index 4046e620c5765240c0ece5e2548f888df94d5f5b..222babcfabf4224ca82acbf476bca09ec44f18ae 100644 (file)
@@ -100,14 +100,8 @@ my $i3_pid;
 my $i3_autostart;
 
 END {
-
-    # testcases which start i3 manually should always call exit_gracefully
-    # on their own. Let’s see, whether they really did.
-    if (! $i3_autostart) {
-        return unless $i3_pid;
-
-        $tester->ok(undef, 'testcase called exit_gracefully()');
-    }
+    # Skip the remaining cleanup for testcases which set i3_autostart => 0:
+    return if !defined($i3_pid) && !$i3_autostart;
 
     # don't trigger SIGCHLD handler
     local $SIG{CHLD};
@@ -126,7 +120,7 @@ END {
         exit_gracefully($i3_pid, "/tmp/nested-$ENV{DISPLAY}");
 
     } else {
-        kill(9, $i3_pid)
+        kill(-9, $i3_pid)
             or $tester->BAIL_OUT("could not kill i3");
 
         waitpid $i3_pid, 0;
@@ -138,8 +132,9 @@ sub import {
     my $pkg = caller;
 
     $i3_autostart = delete($args{i3_autostart}) // 1;
+    my $i3_config = delete($args{i3_config}) // '-default';
 
-    my $cv = launch_with_config('-default', dont_block => 1)
+    my $cv = launch_with_config($i3_config, dont_block => 1)
         if $i3_autostart;
 
     my $test_more_args = '';
@@ -793,14 +788,7 @@ sub get_socket_path {
     if ($cache && defined($_cached_socket_path)) {
         return $_cached_socket_path;
     }
-
-    my $atom = $x->atom(name => 'I3_SOCKET_PATH');
-    my $cookie = $x->get_property(0, $x->get_root_window(), $atom->id, GET_PROPERTY_TYPE_ANY, 0, 256);
-    my $reply = $x->get_property_reply($cookie->{sequence});
-    my $socketpath = $reply->{value};
-    if ($socketpath eq "/tmp/nested-$ENV{DISPLAY}") {
-        $socketpath .= '-activation';
-    }
+    my $socketpath = i3test::Util::get_socket_path($x);
     $_cached_socket_path = $socketpath;
     return $socketpath;
 }
@@ -835,8 +823,11 @@ sub launch_with_config {
 
     my ($fh, $tmpfile) = tempfile("i3-cfg-for-$ENV{TESTNAME}-XXXXX", UNLINK => 1);
 
+    say $fh "ipc-socket $tmp_socket_path"
+        unless $args{dont_add_socket_path};
+
     if ($config ne '-default') {
-        say $fh $config;
+        print $fh $config;
     } else {
         open(my $conf_fh, '<', '@abs_top_srcdir@/testcases/i3-test.config')
             or $tester->BAIL_OUT("could not open default config: $!");
@@ -844,9 +835,6 @@ sub launch_with_config {
         say $fh scalar <$conf_fh>;
     }
 
-    say $fh "ipc-socket $tmp_socket_path"
-        unless $args{dont_add_socket_path};
-
     close($fh);
 
     my $cv = AnyEvent->condvar;
@@ -864,6 +852,7 @@ sub launch_with_config {
         dont_create_temp_dir => $args{dont_create_temp_dir},
         validate_config => $args{validate_config},
         inject_randr15 => $args{inject_randr15},
+        inject_randr15_outputinfo => $args{inject_randr15_outputinfo},
     );
 
     # If we called i3 with -C, we wait for it to exit and then return as
index 749136813d0492bcbe99798aec54af3af543bc13..725ca4e174d81c5a4dd205be40979e325dfacaa4 100644 (file)
@@ -5,9 +5,13 @@ use strict;
 use warnings;
 use v5.10;
 
+use X11::XCB qw(GET_PROPERTY_TYPE_ANY);
+use X11::XCB::Connection;
+
 use Exporter qw(import);
 our @EXPORT = qw(
     slurp
+    get_socket_path
 );
 
 =encoding utf-8
@@ -38,6 +42,25 @@ sub slurp {
     return $content;
 }
 
+=head2 get_socket_path([X11::XCB::Connection])
+
+Gets the socket path from the C<I3_SOCKET_PATH> atom stored on the X11 root
+window.
+
+=cut
+sub get_socket_path {
+    my ($x) = @_;
+    $x //= X11::XCB::Connection->new();
+    my $atom = $x->atom(name => 'I3_SOCKET_PATH');
+    my $cookie = $x->get_property(0, $x->get_root_window(), $atom->id, GET_PROPERTY_TYPE_ANY, 0, 256);
+    my $reply = $x->get_property_reply($cookie->{sequence});
+    my $socketpath = $reply->{value};
+    if ($socketpath eq "/tmp/nested-$ENV{DISPLAY}") {
+        $socketpath .= '-activation';
+    }
+    return $socketpath;
+}
+
 =head1 AUTHOR
 
 Michael Stapelberg <michael@i3wm.org>
index 3937b70acd65c934f3ff46f616199b9170f7dc2d..b7a9cdc5e256792d0cafe988ceb1752f81f20d9c 100644 (file)
@@ -5,7 +5,8 @@ use strict;
 use warnings;
 use v5.10;
 
-use i3test i3_autostart => 0;
+use Test::More;
+use i3test::Util qw(get_socket_path);
 use lib qw(@abs_top_srcdir@/AnyEvent-I3/blib/lib);
 use AnyEvent::I3;
 use ExtUtils::PkgConfig;
index e0408338c2d1dd974d48ade772e1c2742c34cce0..65795305808d6b42779c52528993e1d451ed7b71 100644 (file)
@@ -11,7 +11,6 @@ BEGIN {
         IPC::Run
         ExtUtils::PkgConfig
         Inline
-        Test::More
     );
     for my $dep (@deps) {
         use_ok($dep) or BAIL_OUT(qq|The Perl module "$dep" could not be loaded. Please see http://build.i3wm.org/docs/testsuite.html#_installing_the_dependencies|);
index b337de9a1db1a25db443dbbbdb71177e72d8ced7..eefdac9ff8884f49fa7e1c5087a0e0cb53965aaa 100644 (file)
 # the time of launching the new one. Also make sure that focusing containers
 # in other workspaces work even when there is a fullscreen container.
 #
-use i3test i3_autostart => 0;
-
-# Screen setup looks like this:
-# +----+----+
-# | S1 | S2 |
-# +----+----+
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
 fake-outputs 1024x768+0+0,1024x768+1024+0
 EOT
-
-my $pid = launch_with_config($config);
-
-my $i3 = i3(get_socket_path());
+# Screen setup looks like this:
+# +----+----+
+# | S1 | S2 |
+# +----+----+
 
 my $tmp = fresh_workspace;
 
@@ -331,6 +325,4 @@ verify_move(2, 'prevented move to workspace by position');
 
 # TODO: Tests for "move to output" and "move workspace to output".
 
-exit_gracefully($pid);
-
 done_testing;
diff --git a/testcases/t/165-for_window_tilingfloating.t b/testcases/t/165-for_window_tilingfloating.t
deleted file mode 100644 (file)
index 760ac53..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#!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)
-#
-use i3test i3_autostart => 0;
-use X11::XCB qw(PROP_MODE_REPLACE);
-
-##############################################################
-# 13: check that the tiling / floating criteria work.
-##############################################################
-
-my $config = <<"EOT";
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-for_window [tiling] mark tiled
-for_window [floating] mark floated
-EOT
-
-my $pid = launch_with_config($config);
-my $tmp = fresh_workspace;
-
-open_window;
-open_floating_window;
-
-my @nodes = @{get_ws($tmp)->{nodes}};
-cmp_ok(@nodes, '==', 1, 'one tiling container on this workspace');
-is_deeply($nodes[0]->{marks}, [ 'tiled' ], "mark set for 'tiling' criterion");
-
-@nodes = @{get_ws($tmp)->{floating_nodes}};
-cmp_ok(@nodes, '==', 1, 'one floating container on this workspace');
-is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'floated' ], "mark set for 'floating' criterion");
-
-exit_gracefully($pid);
-
-##############################################################
-
-done_testing;
diff --git a/testcases/t/173-regress-focus-assign.t b/testcases/t/173-regress-focus-assign.t
deleted file mode 100644 (file)
index b010963..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-#!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)
-#
-# Regression: Checks if focus is stolen when a window is managed which is
-# assigned to an invisible workspace
-#
-use i3test i3_autostart => 0;
-
-sub open_special {
-    my %args = @_;
-    $args{name} //= 'special window';
-
-    # We use dont_map because i3 will not map the window on the current
-    # workspace. Thus, open_window would time out in wait_for_map (2 seconds).
-    my $window = open_window(
-        %args,
-        wm_class => 'special',
-        dont_map => 1,
-    );
-    $window->map;
-    return $window;
-}
-
-#####################################################################
-# start a window and see that it does not get assigned with an empty config
-#####################################################################
-
-my $config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-assign [class="special"] targetws
-EOT
-
-my $pid = launch_with_config($config);
-
-my $tmp = fresh_workspace;
-
-ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
-ok(get_ws($tmp)->{focused}, 'current workspace focused');
-
-my $window = open_special;
-sync_with_i3;
-
-ok(@{get_ws_content($tmp)} == 0, 'special window not on current workspace');
-ok(@{get_ws_content('targetws')} == 1, 'special window on targetws');
-ok(get_ws($tmp)->{focused}, 'current workspace still focused');
-
-#####################################################################
-# the same test, but with a floating window
-#####################################################################
-
-$window = open_special(
-    window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
-);
-
-ok(@{get_ws_content($tmp)} == 0, 'special window not on current workspace');
-ok(@{get_ws_content('targetws')} == 1, 'special window on targetws');
-ok(get_ws($tmp)->{focused}, 'current workspace still focused');
-
-exit_gracefully($pid);
-
-$window->destroy;
-
-done_testing;
diff --git a/testcases/t/174-regress-focus-toggle.t b/testcases/t/174-regress-focus-toggle.t
deleted file mode 100644 (file)
index 192e975..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#!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)
-#
-# Regression: Checks if i3 still lives after using 'focus mode_toggle' on an
-# empty workspace. This regression was fixed in
-# 0848844f2d41055f6ffc69af1149d7a873460976.
-#
-use i3test;
-use v5.10;
-
-my $tmp = fresh_workspace;
-
-cmd 'focus mode_toggle';
-
-does_i3_live;
-
-done_testing;
index 7562ad90e8f82ed1c0cc27b6d25f339fec0c9785..5f58ca09e4a9ce6c6ea1c3a9912b105c9666597f 100644 (file)
 # Wrong behaviour manifested itself up to (including) commit
 # f78caf8c5815ae7a66de9e4b734546fd740cc19d
 #
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
 assign [title="testcase"] targetws
 EOT
 
-my $pid = launch_with_config($config);
-
 my $i3 = i3(get_socket_path(0));
 
 cmd 'workspace targetws';
@@ -51,6 +47,4 @@ cmd 'focus parent';
 open_window(name => "testcase");
 is_num_children('targetws', 3, 'new window opened next to last one');
 
-exit_gracefully($pid);
-
 done_testing;
index 43f7b1783f80b977e86ba92e6909030177cbaa6e..62675b4477a7b8c4e6eeb9090698f42159894dab 100644 (file)
@@ -15,9 +15,7 @@
 #   (unless you are already familiar with Perl)
 #
 # Verifies that the IPC 'mode' event is sent when modes are changed.
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
@@ -30,8 +28,6 @@ mode "with spaces" {
 }
 EOT
 
-my $pid = launch_with_config($config);
-
 my $i3 = i3(get_socket_path(0));
 $i3->connect->recv;
 
@@ -52,6 +48,4 @@ $t = AnyEvent->timer(after => 0.5, cb => sub { $cv->send(0); });
 
 ok($cv->recv, 'Mode event received');
 
-exit_gracefully($pid);
-
 done_testing;
index b6ebc876a14c9020a154f828e0646c6c11a09869..bd0b407894c38c8fd26af8cfe63c55abaa9fdbc3 100644 (file)
 #
 
 use List::Util qw(first);
-use i3test i3_autostart => 0;
-use Time::HiRes qw(sleep);
-
-# Ensure the pointer is at (0, 0) so that we really start on the first
-# (the left) workspace.
-$x->root->warp_pointer(0, 0);
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
 force_display_urgency_hint 500ms
 EOT
-my $pid = launch_with_config($config);
+use Time::HiRes qw(sleep);
 
 #####################################################################
 # Initial setup: one window on ws1, empty ws2
@@ -145,6 +138,4 @@ sync_with_i3;
 sleep(0.6);
 is(count_total_urgent(get_ws($tmp3)), 0, "no more urgent windows on workspace $tmp3");
 
-exit_gracefully($pid);
-
 done_testing;
index b6c6d7767a000b1fab353c58ff85d5169a1e5b25..695234c9b5217bb0a3c0e0731626167bff5d56da 100644 (file)
@@ -14,7 +14,7 @@
 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
 #   (unless you are already familiar with Perl)
 #
-use i3test i3_autostart => 0;
+use i3test;
 use IPC::Run qw(run);
 use File::Temp;
 
@@ -22,14 +22,8 @@ use File::Temp;
 # 1: test that shared memory logging does not work yet
 ################################################################################
 
-my $config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-EOT
-
-# NB: launch_with_config sets --shmlog-size=0 because the logfile gets
-# redirected via stdout redirection anyways.
-my $pid = launch_with_config($config);
+# NB: launch_with_config (called in i3test) sets --shmlog-size=0 because the
+# logfile gets redirected via stdout redirection anyways.
 
 my $stdout;
 my $stderr;
@@ -82,6 +76,4 @@ run [ 'i3-dump-log' ],
 like($stderr, qr#^i3-dump-log: ERROR: i3 is running, but SHM logging is not enabled\.#,
     'shm logging not enabled');
 
-exit_gracefully($pid);
-
 done_testing;
index a13e0ad36695f758ef1f21ac8198e83dfab4a401..0a9c39ab5512284ea1b3cf4899bbcda92987a4b1 100644 (file)
@@ -18,9 +18,7 @@
 # criteria for windows on non-visible workspaces.
 # Ticket: #1027
 # Bug still in: 4.5.1-90-g6582da9
-use i3test i3_autostart => 0;
-
-my $config = <<'EOT';
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
@@ -28,8 +26,6 @@ assign [class="^special$"] → mail
 for_window [class="^special$"] floating enable, floating disable
 EOT
 
-my $pid = launch_with_config($config);
-
 my $window = open_window(
     name => 'Borderless window',
     wm_class => 'special',
@@ -43,6 +39,4 @@ cmd '[class="^special$"] focus';
 
 does_i3_live;
 
-exit_gracefully($pid);
-
 done_testing;
index 57d8b434d116c41e96953379703beb992f7977a7..1e1df7beaddc771129158e625fcc3668203459e7 100644 (file)
 #
 # Ticket: #1086
 # Bug still in: 4.6-62-g7098ef6
-use i3test i3_autostart => 0;
+use i3test i3_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+assign [class="special"] nonvisible
+EOT
 
 sub open_special {
     my %args = @_;
@@ -38,14 +43,6 @@ sub open_special {
     return $window;
 }
 
-my $config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-assign [class="special"] nonvisible
-EOT
-my $pid = launch_with_config($config);
-
 my $tmp = fresh_workspace;
 
 ok((scalar grep { $_ eq 'nonvisible' } @{get_workspace_names()}) == 0,
@@ -60,6 +57,4 @@ ok((scalar grep { $_ eq 'nonvisible' } @{get_workspace_names()}) > 0,
 my @urgent = grep { $_->{urgent} } @{get_ws_content('nonvisible')};
 isnt(@urgent, 0, 'urgent window(s) found on destination workspace');
 
-exit_gracefully($pid);
-
 done_testing;
diff --git a/testcases/t/213-move-branch-position.t b/testcases/t/213-move-branch-position.t
deleted file mode 100644 (file)
index c2928c9..0000000
+++ /dev/null
@@ -1,376 +0,0 @@
-#!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 movement of a con into a branch will place the moving con at the
-# correct position within the branch.
-#
-# If the direction of movement is the same as the orientation of the branch
-# container, append or prepend the container to the branch in the obvious way.
-# If the movement is to the right or downward, insert the moving container in
-# the first position (i.e., the leftmost or top position resp.) If the movement
-# is to the left or upward, insert the moving container in the last position
-# (i.e., the rightmost or bottom position resp.)
-#
-# If the direction of movement is different from the orientation of the branch
-# container, insert the container into the branch after the focused-inactive
-# container.
-#
-# For testing purposes, we will demonstrate the behavior for tabbed containers
-# to represent the case of split-horizontal branches and stacked containers to
-# represent the case of split-vertical branches.
-#
-# Ticket: #1060
-# Bug still in: 4.6-109-g18cfc36
-
-use i3test;
-
-# Opens tabs on the presently focused branch and adds several additional
-# windows. Shifts focus to somewhere in the middle of the tabs so the most
-# general case can be assumed.
-sub open_tabs {
-    cmd 'layout tabbed';
-    open_window;
-    open_window;
-    open_window;
-    open_window;
-    cmd 'focus left; focus left'
-}
-
-# Likewise for a stack
-sub open_stack {
-    cmd 'layout stacking';
-    open_window;
-    open_window;
-    open_window;
-    open_window;
-    cmd 'focus up; focus up'
-}
-
-# Gets the position of the given leaf within the given branch. The first
-# position is one (1). Returns negative one (-1) if the leaf cannot be found
-# within the branch.
-sub get_leaf_position {
-    my ($branch, $leaf) = @_;
-    my $position = -1;
-    for my $i (0 .. @{$branch->{nodes}}) {
-        if ($branch->{nodes}[$i]->{id} == $leaf) {
-            $position = $i + 1;
-            last;
-        };
-    }
-    return $position;
-}
-
-# convenience function to focus a con by id to avoid having to type an ugly
-# command each time
-sub focus_con {
-    my $con_id = shift @_;
-    cmd "[con_id=\"$con_id\"] focus";
-}
-
-# Places a leaf into a branch and focuses the leaf. The newly created branch
-# will have orientation specified by the second parameter.
-sub branchify {
-    my ($con_id, $orientation) = @_;
-    focus_con($con_id);
-    $orientation eq 'horizontal' ? cmd 'splith' : cmd 'splitv';
-    open_window;
-    focus_con($con_id);
-}
-
-##############################################################################
-# When moving a con right into tabs, the moving con should be placed as the
-# first tab in the branch
-##############################################################################
-my $ws = fresh_workspace;
-
-# create the target leaf
-open_window;
-my $target_leaf = get_focused($ws);
-
-# create the tabbed branch container
-open_window;
-cmd 'splith';
-open_tabs;
-
-# move the target leaf into the tabbed branch
-focus_con($target_leaf);
-cmd 'move right';
-
-# the target leaf should be the first in the branch
-my $branch = shift @{get_ws_content($ws)};
-is($branch->{nodes}[0]->{id}, $target_leaf, 'moving con right into tabs placed it as the first tab in the branch');
-
-# repeat the test when the target is in a branch
-cmd 'move up; move left';
-branchify($target_leaf, 'vertical');
-cmd 'move right';
-
-$branch = pop @{get_ws_content($ws)};
-is($branch->{nodes}[0]->{id}, $target_leaf, 'moving con right into tabs from a branch placed it as the first tab in the branch');
-
-##############################################################################
-# When moving a con right into a stack, the moving con should be placed
-# below the focused-inactive leaf
-##############################################################################
-$ws = fresh_workspace;
-
-# create the target leaf
-open_window;
-$target_leaf = get_focused($ws);
-
-# create the stacked branch container and find the focused leaf
-open_window;
-cmd 'splith';
-open_stack;
-my $secondary_leaf = get_focused($ws);
-
-# move the target leaf into the stacked branch
-focus_con($target_leaf);
-cmd 'move right';
-
-# the secondary focus leaf should be below the target
-$branch = shift @{get_ws_content($ws)};
-my $target_leaf_position = get_leaf_position($branch, $target_leaf);
-my $secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
-
-is($target_leaf_position, $secondary_leaf_position + 1, 'moving con right into a stack placed it below the focused-inactive leaf');
-
-# repeat the test when the target is in a branch
-cmd 'move up; move left';
-branchify($target_leaf, 'vertical');
-cmd 'move right';
-
-$branch = pop @{get_ws_content($ws)};
-$target_leaf_position = get_leaf_position($branch, $target_leaf);
-$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
-
-is($target_leaf_position, $secondary_leaf_position + 1, 'moving con right into a stack from a branch placed it below the focused-inactive leaf');
-
-##############################################################################
-# When moving a con down into a stack, the moving con should be placed at the
-# top of the stack
-##############################################################################
-$ws = fresh_workspace;
-cmd 'layout splitv';
-
-# create the target leaf
-open_window;
-$target_leaf = get_focused($ws);
-
-# create the stacked branch container
-open_window;
-cmd 'splitv';
-open_stack;
-
-# move the target leaf into the stacked branch
-focus_con($target_leaf);
-cmd 'move down';
-
-# the target leaf should be on the top of the stack
-$branch = shift @{get_ws_content($ws)};
-is($branch->{nodes}[0]->{id}, $target_leaf, 'moving con down into a stack placed it on the top of the stack');
-
-# repeat the test when the target is in a branch
-cmd 'move right; move up';
-branchify($target_leaf, 'horizontal');
-cmd 'move down';
-
-$branch = pop @{get_ws_content($ws)};
-is($branch->{nodes}[0]->{id}, $target_leaf, 'moving con down into a stack from a branch placed it on the top of the stack');
-
-##############################################################################
-# When moving a con down into tabs, the moving con should be placed after the
-# focused-inactive tab
-##############################################################################
-$ws = fresh_workspace;
-cmd 'layout splitv';
-
-# create the target leaf
-open_window;
-$target_leaf = get_focused($ws);
-
-# create the tabbed branch container and find the focused tab
-open_window;
-cmd 'splitv';
-open_tabs;
-$secondary_leaf = get_focused($ws);
-
-# move the target leaf into the tabbed branch
-focus_con($target_leaf);
-cmd 'move down';
-
-# the secondary focus tab should be to the right
-$branch = shift @{get_ws_content($ws)};
-$target_leaf_position = get_leaf_position($branch, $target_leaf);
-$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
-
-is($target_leaf_position, $secondary_leaf_position + 1, 'moving con down into tabs placed it after the focused-inactive tab');
-
-# repeat the test when the target is in a branch
-cmd 'move right; move up';
-branchify($target_leaf, 'horizontal');
-cmd 'move down';
-
-$branch = pop @{get_ws_content($ws)};
-$target_leaf_position = get_leaf_position($branch, $target_leaf);
-$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
-
-is($target_leaf_position, $secondary_leaf_position + 1, 'moving con down into tabs from a branch placed it after the focused-inactive tab');
-
-##############################################################################
-# When moving a con left into tabs, the moving con should be placed as the last
-# tab in the branch
-##############################################################################
-$ws = fresh_workspace;
-
-# create the tabbed branch container
-open_window;
-cmd 'splith';
-open_tabs;
-
-# create the target leaf
-cmd 'focus parent';
-open_window;
-$target_leaf = get_focused($ws);
-
-# move the target leaf into the tabbed branch
-cmd 'move left';
-
-# the target leaf should be last in the branch
-$branch = shift @{get_ws_content($ws)};
-
-is($branch->{nodes}->[-1]->{id}, $target_leaf, 'moving con left into tabs placed it as the last tab in the branch');
-
-# repeat the test when the target leaf is in a branch
-cmd 'move up; move right';
-branchify($target_leaf, 'vertical');
-cmd 'move left';
-
-$branch = shift @{get_ws_content($ws)};
-is($branch->{nodes}->[-1]->{id}, $target_leaf, 'moving con left into tabs from a branch placed it as the last tab in the branch');
-
-##############################################################################
-# When moving a con left into a stack, the moving con should be placed below
-# the focused-inactive leaf
-##############################################################################
-$ws = fresh_workspace;
-
-# create the stacked branch container and find the focused leaf
-open_window;
-open_stack;
-$secondary_leaf = get_focused($ws);
-
-# create the target leaf to the right
-cmd 'focus parent';
-open_window;
-$target_leaf = get_focused($ws);
-
-# move the target leaf into the stacked branch
-cmd 'move left';
-
-# the secondary focus leaf should be below
-$branch = shift @{get_ws_content($ws)};
-$target_leaf_position = get_leaf_position($branch, $target_leaf);
-$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
-
-is($target_leaf_position, $secondary_leaf_position + 1, 'moving con left into a stack placed it below the focused-inactive leaf');
-
-# repeat the test when the target leaf is in a branch
-cmd 'move up; move right';
-branchify($target_leaf, 'vertical');
-cmd 'move left';
-
-$branch = shift @{get_ws_content($ws)};
-$target_leaf_position = get_leaf_position($branch, $target_leaf);
-$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
-
-is($target_leaf_position, $secondary_leaf_position + 1, 'moving con left into a stack from a branch placed it below the focused-inactive leaf');
-
-##############################################################################
-# When moving a con up into a stack, the moving con should be placed last in
-# the stack
-##############################################################################
-$ws = fresh_workspace;
-cmd 'layout splitv';
-
-# create the stacked branch container
-open_window;
-cmd 'splitv';
-open_stack;
-
-# create the target leaf
-cmd 'focus parent';
-open_window;
-$target_leaf = get_focused($ws);
-
-# move the target leaf into the stacked branch
-cmd 'move up';
-
-# the target leaf should be on the bottom of the stack
-$branch = shift @{get_ws_content($ws)};
-
-is($branch->{nodes}->[-1]->{id}, $target_leaf, 'moving con up into stack placed it on the bottom of the stack');
-
-# repeat the test when the target leaf is in a branch
-cmd 'move right; move down';
-branchify($target_leaf, 'horizontal');
-cmd 'move up';
-
-$branch = shift @{get_ws_content($ws)};
-
-is($branch->{nodes}->[-1]->{id}, $target_leaf, 'moving con up into stack from a branch placed it on the bottom of the stack');
-
-##############################################################################
-# When moving a con up into tabs, the moving con should be placed after the
-# focused-inactive tab
-##############################################################################
-$ws = fresh_workspace;
-cmd 'layout splitv';
-
-# create the tabbed branch container and find the focused leaf
-open_window;
-cmd 'splitv';
-open_tabs;
-$secondary_leaf = get_focused($ws);
-
-# create the target leaf below
-cmd 'focus parent';
-open_window;
-$target_leaf = get_focused($ws);
-
-# move the target leaf into the tabbed branch
-cmd 'move up';
-
-# the secondary focus tab should be to the right
-$branch = shift @{get_ws_content($ws)};
-$target_leaf_position = get_leaf_position($branch, $target_leaf);
-$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
-
-is($target_leaf_position, $secondary_leaf_position + 1, 'moving con up into tabs placed it after the focused-inactive tab');
-
-# repeat the test when the target leaf is in a branch
-cmd 'move right; move down';
-branchify($target_leaf, 'horizontal');
-cmd 'move up';
-
-$branch = shift @{get_ws_content($ws)};
-$target_leaf_position = get_leaf_position($branch, $target_leaf);
-$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
-
-is($target_leaf_position, $secondary_leaf_position + 1, 'moving con up into tabs from a branch placed it after the focused-inactive tab');
-
-done_testing;
index 4430dac8f7b3fc173a5a822dcde5cbdce3b3eba5..5b34c29c751d708e43b345de13976275222cc4b5 100644 (file)
@@ -131,14 +131,103 @@ print $fh <<'EOT';
 EOT
 $fh->flush;
 my $reply = cmd "append_layout $filename";
-diag('reply = ' . Dumper($reply));
+ok(!$reply->[0]->{success}, 'IPC reply did not indicate success');
 
 does_i3_live;
 
-ok(!$reply->[0]->{success}, 'IPC reply did not indicate success');
 
 close($fh);
 
+################################################################################
+# another file with a superfluous trailing comma (issue #2755)
+################################################################################
+
+subtest 'issue 2755' => sub {
+    plan tests => 4;
+    $ws = fresh_workspace;
+
+    @content = @{get_ws_content($ws)};
+    is(@content, 0, 'no nodes on the new workspace yet');
+
+    ($fh, $filename) = tempfile(UNLINK => 1);
+    print $fh <<'EOT';
+// vim:ts=4:sw=4:et
+{
+    // splith split container with 2 children
+    "border": "normal",
+    "floating": "auto_off",
+    "layout": "splith",
+    "percent": null,
+    "type": "con",
+    "nodes": [
+        {
+            "border": "normal",
+            "current_border_width": 2,
+            "floating": "auto_off",
+            "geometry": {
+               "height": 860,
+               "width": 1396,
+               "x": 1922,
+               "y": 38
+            },
+            "name": "Chromium1",
+            "percent": 0.5,
+            "swallows": [
+               {
+                  "class": "^Chromium$",
+               // "instance": "^chromium$",
+               // "title": "^Git\\ Tutorial\\ \\-\\ corp\\ \\-\\ Chromium$",
+               // "transient_for": "^$",
+               // "window_role": "^browser$"
+               }
+            ],
+            "type": "con"
+        },
+        {
+            "border": "normal",
+            "current_border_width": 2,
+            "floating": "auto_off",
+            "geometry": {
+               "height": 1040,
+               "width": 956,
+               "x": 2,
+               "y": 38
+            },
+            "name": "Chromium2",
+            "percent": 0.5,
+            "swallows": [
+               {
+                  "class": "^Chromium$",
+               // "instance": "^chromium$",
+               // "title": "^Nutanix\\ \\-\\ Prod\\ \\-\\ Sign\\ In\\ \\-\\ Chromium$",
+               // "transient_for": "^$",
+               // "window_role": "^browser$"
+               }
+            ],
+            "type": "con"
+        }
+    ]
+}
+
+EOT
+    $fh->flush;
+    $reply = cmd "append_layout $filename";
+    ok(!$reply->[0]->{success}, 'IPC reply indicated success');
+
+    does_i3_live;
+
+    # Move to a different workspace rendered the half-attached con’s con->parent
+    # invalid.
+    fresh_workspace;
+
+    cmd '[urgent=latest] focus';
+    $reply = cmd 'scratchpad show';
+
+    does_i3_live;
+
+    close($fh);
+};
+
 ################################################################################
 # wrong percent key in a child node
 ################################################################################
index b9f9a79799d39033a81960750e762bca66dd7d60..bfd7aa4bd92e7a06024fb69ca1f36eae0c06fd89 100644 (file)
 # client.
 # Ticket: #1201
 # Bug still in: 4.7.2-107-g9b03be6
-use i3test i3_autostart => 0;
-
-my $config = <<'EOT';
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 EOT
 
-my $pid = launch_with_config($config);
-
-my $i3 = i3(get_socket_path());
-$i3->connect()->recv;
-
 my $window = open_window(
     wm_class => 'special',
     window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
@@ -39,6 +32,4 @@ cmd('[class="special"] resize grow height 160 px or 16 ppt');
 
 does_i3_live;
 
-exit_gracefully($pid);
-
 done_testing;
index e4d51e2eb6c52db46be355761fc26770eee37fa5..9d1ac70c4a72eb673af30f811ae1c83927a105ae 100644 (file)
 # and that they keep their geometry.
 # Ticket: #1263
 # Bug still in: 4.7.2-200-g570b572
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
 for_window [instance=__i3-test-window] floating enable, border pixel 1
 EOT
 
-my $pid = launch_with_config($config);
 my $tmp = fresh_workspace;
 
 my $window = open_window(wm_class => '__i3-test-window');
@@ -46,6 +43,4 @@ $floating_win = $nodes->{floating_nodes}->[0]->{nodes}->[0];
 is($floating_win->{fullscreen_mode}, 1, 'floating window still in fullscreen mode');
 is_deeply($floating_win->{geometry}, $old_geometry, 'floating window geometry still the same');
 
-exit_gracefully($pid);
-
 done_testing;
diff --git a/testcases/t/231-ipc-window-close.t b/testcases/t/231-ipc-window-close.t
deleted file mode 100644 (file)
index 3483cf4..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-#!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 that the ipc close event works properly
-#
-# Bug still in: 4.8-7-gf4a8253
-use i3test;
-
-my $i3 = i3(get_socket_path());
-$i3->connect()->recv;
-
-my $cv;
-my $t;
-
-sub reset_test {
-    $cv = AE::cv;
-    $t = AE::timer(0.5, 0, sub { $cv->send(0); });
-}
-
-reset_test;
-
-$i3->subscribe({
-        window => sub {
-            my ($e) = @_;
-            if ($e->{change} eq 'close') {
-                $cv->send($e->{container});
-            }
-        },
-    })->recv;
-
-my $window = open_window;
-
-cmd 'kill';
-my $con = $cv->recv;
-
-ok($con, 'closing a window should send the window::close event');
-is($con->{window}, $window->{id}, 'the event should contain information about the window');
-
-done_testing;
diff --git a/testcases/t/231-ipc-window-move.t b/testcases/t/231-ipc-window-move.t
deleted file mode 100644 (file)
index 117d27f..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-#!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 that the ipc window::move event works properly
-#
-# Bug still in: 4.8-7-gf4a8253
-use i3test;
-
-my $i3 = i3(get_socket_path());
-$i3->connect()->recv;
-
-my $cv;
-my $t;
-
-sub reset_test {
-    $cv = AE::cv;
-    $t = AE::timer(0.5, 0, sub { $cv->send(0); });
-}
-
-reset_test;
-
-$i3->subscribe({
-        window => sub {
-            my ($e) = @_;
-            if ($e->{change} eq 'move') {
-                $cv->send($e->{container});
-            }
-        },
-    })->recv;
-
-my $dummy_window = open_window;
-my $window = open_window;
-
-cmd 'move right';
-my $con = $cv->recv;
-
-ok($con, 'moving a window should emit the window::move event');
-is($con->{window}, $window->{id}, 'the event should contain info about the window');
-
-reset_test;
-
-cmd 'move to workspace ws_new';
-$con = $cv->recv;
-
-ok($con, 'moving a window to a different workspace should emit the window::move event');
-is($con->{window}, $window->{id}, 'the event should contain info about the window');
-
-done_testing;
diff --git a/testcases/t/232-ipc-window-urgent.t b/testcases/t/232-ipc-window-urgent.t
deleted file mode 100644 (file)
index 09226ff..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-#!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 the window::urgent event works correctly. The window::urgent event
-# should be emitted when a window becomes urgent or loses its urgent status.
-#
-use i3test;
-
-my $config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-force_display_urgency_hint 0ms
-EOT
-
-my $i3 = i3(get_socket_path());
-$i3->connect()->recv;
-
-my $cv;
-$i3->subscribe({
-    window => sub {
-        my ($event) = @_;
-        $cv->send($event) if $event->{change} eq 'urgent';
-    }
-})->recv;
-
-my $t;
-$t = AnyEvent->timer(
-    after => 0.5,
-    cb => sub {
-        $cv->send(0);
-    }
-);
-
-$cv = AnyEvent->condvar;
-fresh_workspace;
-my $win = open_window;
-my $dummy_win = open_window;
-
-$win->add_hint('urgency');
-my $event = $cv->recv;
-
-isnt($event, 0, 'an urgent con should emit the window::urgent event');
-is($event->{container}->{window}, $win->{id}, 'the event should contain information about the window');
-is($event->{container}->{urgent}, 1, 'the container should be urgent');
-
-$cv = AnyEvent->condvar;
-$win->delete_hint('urgency');
-$event = $cv->recv;
-
-isnt($event, 0, 'an urgent con should emit the window::urgent event');
-is($event->{container}->{window}, $win->{id}, 'the event should contain information about the window');
-is($event->{container}->{urgent}, 0, 'the container should not be urgent');
-
-done_testing;
index 1b78f253af4ce69313f8b63cee42bef541f5857b..3f47970cd255dedad5949670b355b3eea8ef5222 100644 (file)
@@ -19,9 +19,7 @@
 # which can lead to complications
 # Ticket: #1283
 # Bug still in: 4.8-24-g60070de
-use i3test i3_autostart => 0;
-
-my $config = <<'EOT';
+use i3test i3_config => <<'EOT';
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
@@ -29,8 +27,6 @@ for_window [class="^special_kill$"] kill
 for_window [class="^special_scratchpad$"] move scratchpad
 EOT
 
-my $pid = launch_with_config($config);
-
 my $win = open_window;
 
 my $scratch_window = open_window(
@@ -53,6 +49,4 @@ sync_with_i3;
 is($x->input_focus, $win->{id},
     'an assignment that kills a window should not disturb focus');
 
-exit_gracefully($pid);
-
 done_testing;
diff --git a/testcases/t/234-layout-restore-output.t b/testcases/t/234-layout-restore-output.t
deleted file mode 100644 (file)
index 5a1f376..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-#!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)
-#
-# Verifies that entire outputs can be saved and restored properly by i3.
-# Ticket: #1306
-# Bug still in: 4.8-26-gf96ec19
-use i3test;
-use File::Temp qw(tempfile);
-use IO::Handle;
-
-my $ws = fresh_workspace;
-
-################################################################################
-# Append a new workspace with a name.
-################################################################################
-
-ok(!workspace_exists('ws_new'), 'workspace "ws_new" does not exist yet');
-
-my ($fh, $filename) = tempfile(UNLINK => 1);
-print $fh <<'EOT';
-// vim:ts=4:sw=4:et
-{
-    // workspace with 1 children
-    "border": "pixel",
-    "floating": "auto_off",
-    "layout": "splith",
-    "percent": null,
-    "type": "workspace",
-    "name": "ws_new",
-    "nodes": [
-        {
-            "border": "pixel",
-            "floating": "auto_off",
-            "geometry": {
-               "height": 268,
-               "width": 484,
-               "x": 0,
-               "y": 0
-            },
-            "name": "vals@w00t: ~",
-            "percent": 1,
-            "swallows": [
-               {
-                  "class": "^URxvt$"
-               // "instance": "^urxvt$",
-               // "title": "^vals\\@w00t\\:\\ \\~$"
-               }
-            ],
-            "type": "con"
-        }
-    ]
-}
-EOT
-$fh->flush;
-cmd "append_layout $filename";
-
-ok(workspace_exists('ws_new'), 'workspace "ws_new" exists now');
-
-does_i3_live;
-
-close($fh);
-
-################################################################################
-# Append a new workspace with a name that clashes with an existing workspace.
-################################################################################
-
-my @old_workspaces = @{get_workspace_names()};
-
-cmd "append_layout $filename";
-
-my @new_workspaces = @{get_workspace_names()};
-cmp_ok(scalar @new_workspaces, '>', scalar @old_workspaces, 'more workspaces than before');
-
-my %created_workspaces = map { ($_, 1) } @new_workspaces;
-delete $created_workspaces{$_} for @old_workspaces;
-diag('created workspaces = ' . Dumper(keys %created_workspaces));
-cmp_ok(scalar keys %created_workspaces, '>', 0, 'new workspaces appeared');
-
-################################################################################
-# Append a new workspace without a name.
-################################################################################
-
-ok(!workspace_exists('unnamed'), 'workspace "unnamed" does not exist yet');
-
-($fh, $filename) = tempfile(UNLINK => 1);
-print $fh <<'EOT';
-// vim:ts=4:sw=4:et
-{
-    // workspace with 1 children
-    "border": "pixel",
-    "floating": "auto_off",
-    "layout": "splith",
-    "percent": null,
-    "type": "workspace",
-    "nodes": [
-        {
-            "border": "pixel",
-            "floating": "auto_off",
-            "geometry": {
-               "height": 268,
-               "width": 484,
-               "x": 0,
-               "y": 0
-            },
-            "name": "vals@w00t: ~",
-            "percent": 1,
-            "swallows": [
-               {
-                  "class": "^URxvt$"
-               // "instance": "^urxvt$",
-               // "title": "^vals\\@w00t\\:\\ \\~$"
-               }
-            ],
-            "type": "con"
-        }
-    ]
-}
-EOT
-$fh->flush;
-cmd "append_layout $filename";
-
-ok(workspace_exists('unnamed'), 'workspace "unnamed" exists now');
-
-################################################################################
-# Append a workspace with a numeric name, ensure it has ->num set.
-################################################################################
-
-ok(!workspace_exists('4'), 'workspace "4" does not exist yet');
-
-($fh, $filename) = tempfile(UNLINK => 1);
-print $fh <<'EOT';
-// vim:ts=4:sw=4:et
-{
-    // workspace with 1 children
-    "border": "pixel",
-    "floating": "auto_off",
-    "layout": "splith",
-    "percent": null,
-    "type": "workspace",
-    "name": "4",
-    "nodes": [
-        {
-            "border": "pixel",
-            "floating": "auto_off",
-            "geometry": {
-               "height": 268,
-               "width": 484,
-               "x": 0,
-               "y": 0
-            },
-            "name": "vals@w00t: ~",
-            "percent": 1,
-            "swallows": [
-               {
-                  "class": "^URxvt$"
-               // "instance": "^urxvt$",
-               // "title": "^vals\\@w00t\\:\\ \\~$"
-               }
-            ],
-            "type": "con"
-        }
-    ]
-}
-EOT
-$fh->flush;
-cmd "append_layout $filename";
-
-ok(workspace_exists('4'), 'workspace "4" exists now');
-$ws = get_ws("4");
-is($ws->{num}, 4, 'workspace number is 4');
-
-################################################################################
-# Append a workspace with a numeric name, with the “type” property at the end
-# of the JSON blurb (which is valid and sometimes happens).
-################################################################################
-
-ok(!workspace_exists('5'), 'workspace "5" does not exist yet');
-
-($fh, $filename) = tempfile(UNLINK => 1);
-print $fh <<'EOT';
-// vim:ts=4:sw=4:et
-{
-    // workspace with 1 children
-    "border": "pixel",
-    "floating": "auto_off",
-    "layout": "splith",
-    "percent": null,
-    "name": "5",
-    "nodes": [
-        {
-            "border": "pixel",
-            "floating": "auto_off",
-            "geometry": {
-               "height": 268,
-               "width": 484,
-               "x": 0,
-               "y": 0
-            },
-            "name": "vals@w00t: ~",
-            "percent": 1,
-            "swallows": [
-               {
-                  "class": "^URxvt$"
-               // "instance": "^urxvt$",
-               // "title": "^vals\\@w00t\\:\\ \\~$"
-               }
-            ],
-            "type": "con"
-        }
-    ],
-    "type": "workspace"
-}
-EOT
-$fh->flush;
-cmd "append_layout $filename";
-
-ok(workspace_exists('5'), 'workspace "5" exists now');
-$ws = get_ws("5");
-is($ws->{num}, 5, 'workspace number is 5');
-
-done_testing;
diff --git a/testcases/t/234-regress-default-floating-border.t b/testcases/t/234-regress-default-floating-border.t
deleted file mode 100644 (file)
index d5994f5..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#!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)
-#
-# This is a regression test for a bug where a normal floating default border is
-# not applied when the default tiling border is set to a pixel value.
-# Ticket: #1305
-# Bug still in: 4.8-62-g7381b50
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-new_window pixel 5
-new_float normal
-EOT
-
-my $pid = launch_with_config($config);
-
-my $ws = fresh_workspace;
-
-my $float_window = open_floating_window;
-
-my @floating = @{get_ws($ws)->{floating_nodes}};
-
-is($floating[0]->{nodes}[0]->{border}, 'normal', 'default floating border is `normal`');
-
-exit_gracefully($pid);
-
-done_testing;
diff --git a/testcases/t/235-wm-class-change-handler.t b/testcases/t/235-wm-class-change-handler.t
deleted file mode 100644 (file)
index ce237b5..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#!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 = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-for_window [class="Special"] mark special_class_mark
-EOT
-
-my $pid = launch_with_config($config);
-
-sub change_window_class {
-    my ($window, $class, $length) = @_;
-    my $atomname = $x->atom(name => 'WM_CLASS');
-    my $atomtype = $x->atom(name => 'STRING');
-    $length ||= length($class) + 1;
-    $x->change_property(
-        PROP_MODE_REPLACE,
-        $window->id,
-        $atomname->id,
-        $atomtype->id,
-        8,
-        $length,
-        $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_deeply($con->{marks}, [ 'special_class_mark' ],
-    'A `for_window` assignment should run for a match when the window changes class');
-
-change_window_class($win, "abcdefghijklmnopqrstuv\0abcd", 24);
-
-$con = @{get_ws_content($ws)}[0];
-
-is($con->{window_properties}->{class}, 'a',
-    'Non-null-terminated strings should be handled correctly');
-
-exit_gracefully($pid);
-
-done_testing;
index e9cb537ebc05b32016c830ec183d6d8794afdb37..e193c02178193db8818b5676d1fe5a767cbf6c9e 100644 (file)
@@ -18,9 +18,7 @@
 # assigned to an invisible workspace) will not crash i3.
 # Ticket: #1338
 # Bug still in: 4.8-91-g294d52e
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
@@ -28,8 +26,6 @@ assign [title=".*"] 1
 for_window [title=".*"] layout tabbed, focus
 EOT
 
-my $pid = launch_with_config($config);
-
 # Switch away from workspace 1
 my $tmp = fresh_workspace;
 
@@ -37,6 +33,4 @@ my $win = open_window;
 
 does_i3_live;
 
-exit_gracefully($pid);
-
 done_testing;
diff --git a/testcases/t/238-regress-reload-bindsym.t b/testcases/t/238-regress-reload-bindsym.t
deleted file mode 100644 (file)
index 6d5d12c..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#!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 the binding event works properly
-# Ticket: #1210
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-bindsym r reload
-EOT
-
-SKIP: {
-    qx(which xdotool 2> /dev/null);
-
-    skip 'xdotool is required to test the binding event. `[apt-get install|pacman -S] xdotool`', 1 if $?;
-
-    my $pid = launch_with_config($config);
-
-    my $i3 = i3(get_socket_path());
-    $i3->connect->recv;
-
-    qx(xdotool key r);
-
-    does_i3_live;
-
-    exit_gracefully($pid);
-
-}
-done_testing;
diff --git a/testcases/t/240-tabbed-floating-disable-crash.t b/testcases/t/240-tabbed-floating-disable-crash.t
deleted file mode 100644 (file)
index 7947158..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#!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)
-#
-# Verifies that i3 does not crash when floating and then unfloating an
-# unfocused window within a tabbed container.
-# Ticket: #1484
-# Bug still in: 4.9.1-124-g856e1f9
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-workspace_layout tabbed
-EOT
-
-my $pid = launch_with_config($config);
-
-open_window;
-open_window;
-
-# Mark the second window, then focus the workspace.
-cmd 'mark foo, focus parent, focus parent';
-
-# Float and unfloat the marked window (without it being focused).
-cmd '[con_mark=foo] floating enable, floating disable';
-
-does_i3_live;
-
-exit_gracefully($pid);
-
-done_testing;
index 283d173180904e7cf0a222a57eca9e66d966a910..3fcd24167b02b4bdf182d66b2862eac8b68f23d5 100644 (file)
@@ -18,9 +18,7 @@
 # Decorations are disabled to avoid floating_enable's logic which shifts
 # windows upwards dependent on their decoration height.
 #
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
@@ -28,8 +26,6 @@ new_window none
 new_float none
 EOT
 
-my $pid = launch_with_config($config);
-
 #####################################################################
 # Open a floating window, verifying that its initial position is
 # centered, and also verify that both centering methods leave it in
@@ -101,6 +97,4 @@ my $child = $fourth->rect;
 is($new->{x}, $child->{x}, 'x coordinates match');
 is($new->{y}, $child->{y}, 'y coordinates match');
 
-exit_gracefully($pid);
-
 done_testing;
diff --git a/testcases/t/243-net-wm-state-hidden.t b/testcases/t/243-net-wm-state-hidden.t
deleted file mode 100644 (file)
index 3f2301c..0000000
+++ /dev/null
@@ -1,201 +0,0 @@
-#!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 for setting and removing the _NET_WM_STATE_HIDDEN atom properly.
-# Ticket: #1648
-use i3test;
-use X11::XCB qw(:all);
-
-sub is_hidden {
-    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});
-    for (my $i = 0; $i < $len; $i++) {
-        return 1 if $atoms[$i] == $atom->id;
-    }
-
-    return 0;
-}
-
-my ($tabA, $tabB, $tabC, $subtabA, $subtabB, $windowA, $windowB);
-
-###############################################################################
-# Given two containers next to each other, when focusing one, then the other
-# one does not have _NET_WM_STATE_HIDDEN set.
-###############################################################################
-
-fresh_workspace;
-$windowA = open_window;
-$windowB = open_window;
-
-ok(!is_hidden($windowA), 'left window does not have _NET_WM_STATE_HIDDEN set');
-ok(!is_hidden($windowB), 'right window does not have _NET_WM_STATE_HIDDEN set');
-
-###############################################################################
-# Given two containers on different workspaces, when one is focused, then
-# the other one does not have _NET_WM_STATE_HIDDEN set.
-###############################################################################
-
-fresh_workspace;
-$windowA = open_window;
-fresh_workspace;
-$windowB = open_window;
-
-ok(!is_hidden($windowA), 'left window does not have _NET_WM_STATE_HIDDEN set');
-ok(!is_hidden($windowB), 'right window does not have _NET_WM_STATE_HIDDEN set');
-
-###############################################################################
-# Given two containers in the same tabbed container, when one is focused, then
-# (only) the other one has _NET_WM_STATE_HIDDEN set.
-# Given the other tab is focused, then the atom is transferred.
-###############################################################################
-
-fresh_workspace;
-$tabA = open_window;
-cmd 'layout tabbed';
-$tabB = open_window;
-
-ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
-ok(!is_hidden($tabB), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
-
-cmd 'focus left';
-
-ok(!is_hidden($tabA), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
-ok(is_hidden($tabB), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
-
-###############################################################################
-# Given three containers in the same stacked container, when the focused tab
-# is moved to another workspace, then the now focused tab does not have
-# _NET_WM_STATE_HIDDEN set anymore.
-###############################################################################
-
-fresh_workspace;
-$tabA = open_window;
-cmd 'layout stacked';
-$tabB = open_window;
-$tabC = open_window;
-cmd 'move window to workspace unused';
-
-ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
-ok(!is_hidden($tabB), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
-ok(!is_hidden($tabC), 'moved window does not have _NET_WM_STATE_HIDDEN set');
-
-###############################################################################
-# Given three containers in the same stacked container, when a not focused
-# tab is moved to another workspace, then it does not have _NET_WM_STATE_HIDDEN
-# set anymore.
-###############################################################################
-
-fresh_workspace;
-$tabA = open_window;
-cmd 'layout stacked';
-$tabB = open_window;
-cmd 'mark moveme';
-$tabC = open_window;
-cmd '[con_mark="moveme"] move window to workspace unused';
-
-ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
-ok(!is_hidden($tabB), 'moved window does not have _NET_WM_STATE_HIDDEN set');
-ok(!is_hidden($tabC), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
-
-###############################################################################
-# Given a tabbed container and some other container, when the latter is moved
-# into the tabbed container, then all other tabs have _NET_WM_STATE_HIDDEN
-# set.
-###############################################################################
-
-fresh_workspace;
-$tabA = open_window;
-cmd 'layout tabbed';
-$tabB = open_window;
-cmd 'focus parent';
-cmd 'split h';
-$tabC = open_window;
-cmd 'move left';
-
-ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
-ok(is_hidden($tabB), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
-ok(!is_hidden($tabC), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
-
-###############################################################################
-# Given a stacked container nested inside another tabbed container with the
-# inner one being in the currently focused tab, then the focused tab of the
-# inner container does not have _NET_WM_STATE_HIDDEN set.
-###############################################################################
-
-fresh_workspace;
-$tabA = open_window;
-cmd 'layout tabbed';
-$tabB = open_window;
-cmd 'split h';
-open_window;
-cmd 'split v';
-cmd 'layout stacked';
-$subtabA = open_window;
-$subtabB = open_window;
-
-ok(is_hidden($tabA), 'unfocused outer tab has _NET_WM_STATE_HIDDEN set');
-ok(!is_hidden($tabB), 'focused outer tab does not have _NET_WM_STATE_HIDDEN set');
-ok(is_hidden($subtabA), 'unfocused inner tab has _NET_WM_STATE_HIDDEN set');
-ok(!is_hidden($subtabB), 'focused inner tab does not have _NET_WM_STATE_HIDDEN set');
-
-cmd 'focus left';
-
-ok(!is_hidden($subtabB), 'focused inner tab does not have _NET_WM_STATE_HIDDEN set');
-
-###############################################################################
-# Given a stacked container nested inside another tabbed container with the
-# inner one being in a currently not focused tab, then all tabs of the inner
-# container have _NET_WM_STATE_HIDDEN set.
-###############################################################################
-
-fresh_workspace;
-$tabA = open_window;
-cmd 'layout tabbed';
-$tabB = open_window;
-cmd 'split h';
-open_window;
-cmd 'split v';
-cmd 'layout stacked';
-$subtabA = open_window;
-$subtabB = open_window;
-cmd 'focus parent';
-cmd 'focus parent';
-cmd 'focus left';
-
-ok(!is_hidden($tabA), 'focused outer tab does not have _NET_WM_STATE_HIDDEN set');
-ok(is_hidden($tabB), 'unfocused outer tab has _NET_WM_STATE_HIDDEN set');
-ok(is_hidden($subtabA), 'unfocused inner tab has _NET_WM_STATE_HIDDEN set');
-ok(is_hidden($subtabB), 'unfocused inner tab has _NET_WM_STATE_HIDDEN set');
-
-###############################################################################
-
-done_testing;
index dbc9a80ae82e431f7ee74fca31cb29b87304bcc5..b1c7fca03e6005ea8b39bae62706139aac029e76 100644 (file)
@@ -17,9 +17,7 @@
 # Ensures that 'move workspace $new, floating enable' on a marked window
 # leaves the window centered on the new workspace.
 # Bug still in: 4.10.2-137-ga4f0ed6
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
@@ -27,8 +25,6 @@ new_window none
 new_float none
 EOT
 
-my $pid = launch_with_config($config);
-
 #####################################################################
 # Open a tiled window, and then simultaneously move it to another
 # workspace and float it, ensuring that it ends up centered.
@@ -48,6 +44,4 @@ is(int($pos->{x} + $pos->{width} / 2), int($x->root->rect->width / 2),
 is(int($pos->{y} + $pos->{height} / 2), int($x->root->rect->height / 2),
     'y coordinates match');
 
-exit_gracefully($pid);
-
 done_testing;
index 10ef3774833036436e422bbe865495a2168ecf7a..314a85114be0bf41b4852fd377d31dc6080919c9 100644 (file)
 # to focus_on_window_activation=urgent), hence the application not clearing it.
 # Ticket: #1825
 # Bug still in: 4.10.3-253-g03799dd
-use i3test i3_autostart => 0;
+use i3test i3_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+focus_on_window_activation urgent
+EOT
 
 sub send_net_active_window {
     my ($id) = @_;
@@ -35,15 +40,6 @@ sub send_net_active_window {
     $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
 }
 
-my $config = <<'EOT';
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-focus_on_window_activation urgent
-EOT
-
-my $pid = launch_with_config($config);
-my $i3 = i3(get_socket_path(0));
 my $ws = fresh_workspace;
 my $first = open_window;
 my $second = open_window;
@@ -64,6 +60,4 @@ cmd '[urgent=latest] focus';
 sync_with_i3;
 is($x->input_focus, $second->id, 'second window still focused');
 
-exit_gracefully($pid);
-
 done_testing;
diff --git a/testcases/t/251-ewmh-visible-name.t b/testcases/t/251-ewmh-visible-name.t
deleted file mode 100644 (file)
index c201b39..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#!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 that _NET_WM_VISIBLE_NAME is set correctly.
-# Ticket: #1872
-use i3test;
-use X11::XCB qw(:all);
-
-my ($con);
-
-sub get_visible_name {
-    sync_with_i3;
-    my ($con) = @_;
-
-    my $cookie = $x->get_property(
-        0,
-        $con->{id},
-        $x->atom(name => '_NET_WM_VISIBLE_NAME')->id,
-        $x->atom(name => 'UTF8_STRING')->id,
-        0,
-        4096
-    );
-
-    my $reply = $x->get_property_reply($cookie->{sequence});
-    return undef if $reply->{value_len} == 0;
-    return $reply->{value};
-}
-
-###############################################################################
-# 1: _NET_WM_VISIBLE_NAME is set when the title format of a window is changed.
-###############################################################################
-
-fresh_workspace;
-$con = open_window(name => 'boring title');
-is(get_visible_name($con), undef, 'sanity check: initially no visible name is set');
-
-cmd 'title_format custom';
-is(get_visible_name($con), 'custom', 'the visible name is updated');
-
-cmd 'title_format "<s>%title</s>"';
-is(get_visible_name($con), '<s>boring title</s>', 'markup is returned as is');
-
-###############################################################################
-# 2: _NET_WM_VISIBLE_NAME is removed if not needed.
-###############################################################################
-
-fresh_workspace;
-$con = open_window(name => 'boring title');
-cmd 'title_format custom';
-is(get_visible_name($con), 'custom', 'sanity check: a visible name is set');
-
-cmd 'title_format %title';
-is(get_visible_name($con), undef, 'the visible name is removed again');
-
-###############################################################################
-
-done_testing;
diff --git a/testcases/t/251-sticky.t b/testcases/t/251-sticky.t
deleted file mode 100644 (file)
index 85fc11f..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-#!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 sticky windows.
-# Ticket: #1455
-use i3test;
-
-my ($ws, $tmp, $focused);
-
-###############################################################################
-# 1: Given a sticky tiling container, when the workspace is switched, then
-#    nothing happens.
-###############################################################################
-fresh_workspace;
-open_window(wm_class => 'findme');
-cmd 'sticky enable';
-$ws = fresh_workspace;
-
-is(@{get_ws($ws)->{nodes}}, 0, 'tiling sticky container did not move');
-is(@{get_ws($ws)->{floating_nodes}}, 0, 'tiling sticky container did not move');
-cmd '[class="findme"] kill';
-
-###############################################################################
-# 2: Given a sticky floating container, when the workspace is switched, then
-#    the container moves to the new workspace.
-###############################################################################
-$ws = fresh_workspace;
-open_floating_window(wm_class => 'findme');
-$focused = get_focused($ws);
-cmd 'sticky enable';
-$ws = fresh_workspace;
-
-is(@{get_ws($ws)->{floating_nodes}}, 1, 'floating sticky container moved to new workspace');
-is(get_focused($ws), $focused, 'sticky container has focus');
-cmd '[class="findme"] kill';
-
-###############################################################################
-# 3: Given two sticky floating containers, when the workspace is switched,
-#    then both containers move to the new workspace.
-###############################################################################
-fresh_workspace;
-open_floating_window(wm_class => 'findme');
-cmd 'sticky enable';
-open_floating_window(wm_class => 'findme');
-cmd 'sticky enable';
-$ws = fresh_workspace;
-
-is(@{get_ws($ws)->{floating_nodes}}, 2, 'multiple sticky windows can be used at the same time');
-cmd '[class="findme"] kill';
-
-###############################################################################
-# 4: Given an unfocused sticky floating container and a tiling container on the
-#    target workspace, when the workspace is switched, then the tiling container
-#    is focused.
-###############################################################################
-$ws = fresh_workspace;
-open_window;
-$focused = get_focused($ws);
-fresh_workspace;
-open_floating_window(wm_class => 'findme');
-cmd 'sticky enable';
-open_window;
-cmd 'workspace ' . $ws;
-
-is(get_focused($ws), $focused, 'the tiling container has focus');
-cmd '[class="findme"] kill';
-
-###############################################################################
-# 5: Given a focused sticky floating container and a tiling container on the
-#    target workspace, when the workspace is switched, then the tiling container
-#    is focused.
-###############################################################################
-$ws = fresh_workspace;
-open_window;
-$tmp = fresh_workspace;
-open_floating_window(wm_class => 'findme');
-$focused = get_focused($tmp);
-cmd 'sticky enable';
-cmd 'workspace ' . $ws;
-
-is(get_focused($ws), $focused, 'the sticky container has focus');
-cmd '[class="findme"] kill';
-
-###############################################################################
-# 6: Given a floating container on a non-visible workspace, when the window
-#    is made sticky, then the window immediately jumps to the currently
-#    visible workspace.
-###############################################################################
-fresh_workspace;
-open_floating_window(wm_class => 'findme');
-cmd 'mark sticky';
-$ws = fresh_workspace;
-cmd '[con_mark=sticky] sticky enable';
-
-is(@{get_ws($ws)->{floating_nodes}}, 1, 'the sticky window jumps to the front');
-cmd '[class="findme"] kill';
-
-###############################################################################
-
-done_testing;
index b27ded3ef527b96b5329ced6a596c1f234a8485d..7d8229dd19870f2832faead7eee023af2c41a1dd 100644 (file)
 # Verifies that "move container to output" works correctly when
 # used with command criteria.
 # Bug still in: 4.10.4-349-gee5db87
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
 fake-outputs 800x600+0+0,800x600+800+0,800x600+0+600,800x600+800+600
 EOT
 
-my $pid = launch_with_config($config);
-
 my $ws_top_left = fresh_workspace(output => 0);
 my $ws_top_right = fresh_workspace(output => 1);
 my $ws_bottom_left = fresh_workspace(output => 2);
@@ -45,6 +41,4 @@ is_num_children($ws_top_right, 1, 'one container on the upper right workspace');
 is_num_children($ws_bottom_left, 0, 'no containers on the lower left workspace');
 is_num_children($ws_bottom_right, 1, 'one container on the lower right workspace');
 
-exit_gracefully($pid);
-
 done_testing;
index 212dfd15f40bb30913ca00d6c6f9559dbc7e2305..1c19cb7850892a3cf252710cd6f49df0e2064569 100644 (file)
 # Ticket: #2062
 # Bug still in: 4.11-103-gc8d51b4
 # Bug introduced with commit 0e5180cae9e9295678e3f053042b559e82cb8c98
-use i3test i3_autostart => 0;
+use i3test
+    i3_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+bindsym Print nop Print
+bindsym Mod4+Return nop Mod4+Return
+EOT
 use i3test::XTEST;
 use ExtUtils::PkgConfig;
 
@@ -29,16 +36,6 @@ SKIP: {
     skip "setxkbmap not found", 1 if
         system(q|setxkbmap -print >/dev/null|) != 0;
 
-my $config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-bindsym Print nop Print
-bindsym Mod4+Return nop Mod4+Return
-EOT
-
-my $pid = launch_with_config($config);
-
 start_binding_capture;
 
 system(q|setxkbmap us,ru -option grp:alt_shift_toggle|);
@@ -89,7 +86,8 @@ is(listen_for_binding(
 sync_with_i3;
 is(scalar @i3test::XTEST::binding_events, 4, 'Received exactly 4 binding events');
 
-exit_gracefully($pid);
+# Disable the grp:alt_shift_toggle option, as we use Alt+Shift in other testcases.
+system(q|setxkbmap us -option|);
 
 }
 
index b0d778587823df9dae1ec877e8060c8921e2fc77..efacdb5206f3f96c92eb4668d4ac261aaa7a3610 100644 (file)
 # Ticket: #2002
 # Bug still in: 4.11-103-gc8d51b4
 # Bug introduced with commit bf3cd41b5ddf1e757515ab5fbf811be56e5f69cc
-use i3test i3_autostart => 0;
-use i3test::XTEST;
-use ExtUtils::PkgConfig;
-
-SKIP: {
-    skip "libxcb-xkb too old (need >= 1.11)", 1 unless
-        ExtUtils::PkgConfig->atleast_version('xcb-xkb', '1.11');
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
@@ -38,8 +30,12 @@ bindsym --release Control+Print nop Control+Print
 bindsym Mod1+b nop Mod1+b
 bindsym --release Mod1+Shift+b nop Mod1+Shift+b release
 EOT
+use i3test::XTEST;
+use ExtUtils::PkgConfig;
 
-my $pid = launch_with_config($config);
+SKIP: {
+    skip "libxcb-xkb too old (need >= 1.11)", 1 unless
+        ExtUtils::PkgConfig->atleast_version('xcb-xkb', '1.11');
 
 start_binding_capture;
 
@@ -90,8 +86,6 @@ is(listen_for_binding(
 sync_with_i3;
 is(scalar @i3test::XTEST::binding_events, 4, 'Received exactly 4 binding events');
 
-exit_gracefully($pid);
-
 }
 
 done_testing;
diff --git a/testcases/t/262-root-window-mouse-binding.t b/testcases/t/262-root-window-mouse-binding.t
deleted file mode 100644 (file)
index c8fd89e..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#!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)
-#
-# Verifies that mouse bindings work on the root window if
-# --whole-window is set.
-# Ticket: #2115
-use i3test i3_autostart => 0;
-use i3test::XTEST;
-
-my $config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-workspace_auto_back_and_forth no
-bindsym --whole-window button4 workspace special
-EOT
-
-my $pid = launch_with_config($config);
-fresh_workspace;
-
-xtest_button_press(4, 50, 50);
-xtest_button_release(4, 50, 50);
-sync_with_i3;
-
-is(focused_ws(), 'special', 'the binding was triggered');
-
-exit_gracefully($pid);
-
-done_testing;
index d72d3d94d5732f260be076c51c781d3efae8f92d..549a862bc915b4e743357eba72c4196dbc84a8b5 100644 (file)
@@ -18,9 +18,7 @@
 # binding mode.
 # Ticket: #2228
 # Bug still in: 4.11-262-geb631ce
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
@@ -28,8 +26,6 @@ mode "othermode" {
 }
 EOT
 
-my $pid = launch_with_config($config);
-
 cmd 'mode othermode';
 
 my $i3 = i3(get_socket_path(0));
@@ -51,6 +47,4 @@ $t = AnyEvent->timer(after => 0.5, cb => sub { $cv->send(0); });
 
 ok($cv->recv, 'Mode event received');
 
-exit_gracefully($pid);
-
 done_testing;
diff --git a/testcases/t/263-edge-borders.t b/testcases/t/263-edge-borders.t
deleted file mode 100644 (file)
index 6da1dac..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-#!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 that the hide_edge_borders smart option works
-# Ticket: #2188
-
-use i3test i3_autostart => 0;
-
-####################################################################
-# 1: check that the borders are present on a floating windows
-#####################################################################
-
-my $config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-new_window pixel 2
-new_float pixel 2
-hide_edge_borders smart
-EOT
-
-my $pid = launch_with_config($config);
-
-my $tmp = fresh_workspace;
-
-ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
-
-my $floatwindow = open_floating_window;
-
-my $wscontent = get_ws($tmp);
-
-my @floating = @{$wscontent->{floating_nodes}};
-ok(@floating == 1, 'one floating container opened');
-is($floating[0]->{nodes}[0]->{current_border_width}, 2, 'floating current border width set to 2');
-is($floatwindow->rect->width, $floating[0]->{rect}->{width} - 2*2, 'floating border width 2');
-
-exit_gracefully($pid);
-
-#####################################################################
-# 2: check that the borders are present on a workspace with two tiled
-# windows visible
-#####################################################################
-
-$config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-new_window pixel 2
-new_float pixel 2
-hide_edge_borders smart
-EOT
-
-$pid = launch_with_config($config);
-
-$tmp = fresh_workspace;
-
-ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
-
-my $tilewindow = open_window;
-my $tilewindow2 = open_window;
-
-$wscontent = get_ws($tmp);
-
-my @tiled = @{$wscontent->{nodes}};
-ok(@tiled == 2, 'two tiled container opened');
-is($tiled[0]->{current_border_width}, 2, 'first tiled current border width set to 2');
-is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*2, 'first tiled border width 2');
-is($tiled[1]->{current_border_width}, 2, 'second tiled current border width set to 2');
-is($tilewindow2->rect->width, $tiled[1]->{rect}->{width} - 2*2, 'second tiled border width 2');
-
-exit_gracefully($pid);
-
-#####################################################################
-# 3: check that the borders are hidden on a workspace with one tiled
-# window visible
-#####################################################################
-
-$config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-new_window pixel 2
-new_float pixel 2
-hide_edge_borders smart
-EOT
-
-$pid = launch_with_config($config);
-
-$tmp = fresh_workspace;
-
-ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
-
-$tilewindow = open_window;
-
-$wscontent = get_ws($tmp);
-
-@tiled = @{$wscontent->{nodes}};
-ok(@tiled == 1, 'one tiled container opened');
-is($tiled[0]->{current_border_width}, 2, 'tiled current border width set to 2');
-is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*0, 'single tiled border width 0');
-
-exit_gracefully($pid);
-
-#####################################################################
-# 4: check that the borders are present on a workspace with two tiled
-# windows visible, recursively
-#####################################################################
-
-$config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-new_window pixel 2
-new_float pixel 2
-hide_edge_borders smart
-EOT
-
-$pid = launch_with_config($config);
-
-$tmp = fresh_workspace;
-
-ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
-
-$tilewindow = open_window;
-$tilewindow2 = open_window;
-ok(@{get_ws_content($tmp)} == 2, 'two containers opened');
-
-cmd 'layout tabbed';
-ok(@{get_ws_content($tmp)} == 1, 'layout tabbed -> back to one container');
-
-cmd 'focus parent';
-my $tilewindow3 = open_window;
-ok(@{get_ws_content($tmp)} == 2, 'after split & new window, two containers');
-
-$wscontent = get_ws($tmp);
-
-# Ensure i3’s X11 requests are processed before our inquiry via
-# $tilewindow->rect:
-sync_with_i3;
-
-@tiled = @{$wscontent->{nodes}};
-ok(@tiled == 2, 'two tiled container opened in another container');
-is($tiled[0]->{current_border_width}, -1, 'first tiled current border width set to -1');
-is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*2, 'first tiled border width 2');
-is($tiled[1]->{current_border_width}, 2, 'second tiled current border width set to 2');
-is($tilewindow2->rect->width, $tiled[1]->{rect}->{width} - 2*2, 'second tiled border width 2');
-
-exit_gracefully($pid);
-
-done_testing;
diff --git a/testcases/t/263-i3-floating-window-atom.t b/testcases/t/263-i3-floating-window-atom.t
deleted file mode 100644 (file)
index 43b69cc..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#!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 for our proprietary atom I3_FLOATING_WINDOW to allow
-# identifying floating windows.
-# Ticket: #2223
-use i3test;
-use X11::XCB qw(:all);
-
-my ($con);
-
-sub has_i3_floating_window {
-    sync_with_i3;
-
-    my ($con) = @_;
-    my $cookie = $x->get_property(
-        0,
-        $con->{id},
-        $x->atom(name => 'I3_FLOATING_WINDOW')->id,
-        $x->atom(name => 'CARDINAL')->id,
-        0,
-        1
-    );
-
-    my $reply = $x->get_property_reply($cookie->{sequence});
-    return 0 if $reply->{length} != 1;
-
-    return unpack("L", $reply->{value});
-}
-
-###############################################################################
-# Toggling floating on a container adds / removes I3_FLOATING_WINDOW.
-###############################################################################
-
-fresh_workspace;
-
-$con = open_window;
-is(has_i3_floating_window($con), 0, 'I3_FLOATING_WINDOW is not set');
-
-cmd 'floating enable';
-is(has_i3_floating_window($con), 1, 'I3_FLOATING_WINDOW is set');
-
-cmd 'floating disable';
-is(has_i3_floating_window($con), 0, 'I3_FLOATING_WINDOW is not set');
-
-###############################################################################
-# A window that is floated when managed has I3_FLOATING_WINDOW set.
-###############################################################################
-#
-fresh_workspace;
-
-$con = open_floating_window;
-is(has_i3_floating_window($con), 1, 'I3_FLOATING_WINDOW is set');
-
-###############################################################################
-
-done_testing;
index cbbdeb0f66edb13071c4cd21af3cd8d248f12a65..1eddba713b0e1ae3309206cc2d233fcdf653f87f 100644 (file)
 #
 # Verifies that command or config criteria does not match dock clients
 # Bug still in: 4.12-38-ge690e3d
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 for_window [class="dock"] move workspace current
-EOT
 
-my $pid = launch_with_config($config);
+bar {
+    # Disable i3bar, which is also a dock client.
+    i3bar_command :
+}
+EOT
 
 my $ws = fresh_workspace();
 
@@ -67,5 +68,4 @@ is(get_dock_clients, 2, "created second docked client");
 is_num_children($ws, 0, 'no container on the current workspace');
 
 
-exit_gracefully($pid);
 done_testing;
diff --git a/testcases/t/264-ipc-shutdown-event.t b/testcases/t/264-ipc-shutdown-event.t
deleted file mode 100644 (file)
index 379b9bf..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-#!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 the ipc shutdown event. This event is triggered when the connection to
-# the ipc is about to shutdown because of a user action such as with a
-# `restart` or `exit` command. The `change` field indicates why the ipc is
-# shutting down. It can be either "restart" or "exit".
-#
-# Ticket: #2318
-# Bug still in: 4.12-46-g2123888
-use i3test;
-
-SKIP: {
-    skip "AnyEvent::I3 too old (need >= 0.17)", 1 if $AnyEvent::I3::VERSION < 0.17;
-
-my $i3 = i3(get_socket_path());
-$i3->connect->recv;
-
-my $cv = AE::cv;
-my $timer = AE::timer 0.5, 0, sub { $cv->send(0); };
-
-$i3->subscribe({
-        shutdown => sub {
-            $cv->send(shift);
-        }
-    })->recv;
-
-cmd 'restart';
-
-my $e = $cv->recv;
-
-diag "Event:\n", Dumper($e);
-ok($e, 'the shutdown event should emit when the ipc is restarted by command');
-is($e->{change}, 'restart', 'the `change` field should tell the reason for the shutdown');
-
-# restarting kills the ipc client so we have to make a new one
-$i3 = i3(get_socket_path());
-$i3->connect->recv;
-
-$cv = AE::cv;
-$timer = AE::timer 0.5, 0, sub { $cv->send(0); };
-
-$i3->subscribe({
-        shutdown => sub {
-            $cv->send(shift);
-        }
-    })->recv;
-
-cmd 'exit';
-
-$e = $cv->recv;
-
-diag "Event:\n", Dumper($e);
-ok($e, 'the shutdown event should emit when the ipc is exited by command');
-is($e->{change}, 'exit', 'the `change` field should tell the reason for the shutdown');
-}
-
-done_testing;
diff --git a/testcases/t/264-keypress-numlock.t b/testcases/t/264-keypress-numlock.t
deleted file mode 100644 (file)
index fcc39ea..0000000
+++ /dev/null
@@ -1,390 +0,0 @@
-#!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)
-#
-# Verifies that one can bind on numpad keys in different numlock states.
-# Ticket: #2346
-# Bug still in: 4.12-78-g85bb324
-use i3test i3_autostart => 0;
-use i3test::XTEST;
-use ExtUtils::PkgConfig;
-
-SKIP: {
-    skip "libxcb-xkb too old (need >= 1.11)", 1 unless
-        ExtUtils::PkgConfig->atleast_version('xcb-xkb', '1.11');
-
-my $config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-# Same key, different numlock states.
-bindsym Mod2+KP_1 nop KP_1
-bindsym KP_End nop KP_End
-
-# Binding which should work with numlock and without.
-bindsym Mod4+a nop a
-
-# Binding which should work with numlock and without, see issue #2418.
-bindsym Escape nop Escape
-
-# Binding which should work with numlock and without, see issue #2418.
-bindsym Shift+Escape nop Shift+Escape
-
-# Binding which should work with numlock and without, see issue #2418.
-bindsym Mod1+Shift+q nop Mod1+Shift+q
-
-# Binding which should work with numlock and without, see issue #2559.
-bindcode 39 nop s
-EOT
-
-my $pid = launch_with_config($config);
-
-start_binding_capture;
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(87); # KP_End
-        xtest_key_release(87); # KP_End
-    },
-    ),
-   'KP_End',
-   'triggered the "KP_End" keybinding');
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(77); # enable Num_Lock
-        xtest_key_release(77); # enable Num_Lock
-        xtest_key_press(87); # KP_1
-        xtest_key_release(87); # KP_1
-        xtest_key_press(77); # disable Num_Lock
-        xtest_key_release(77); # disable Num_Lock
-    },
-    ),
-   'KP_1',
-   'triggered the "KP_1" keybinding');
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(133); # Super_L
-        xtest_key_press(38); # a
-        xtest_key_release(38); # a
-        xtest_key_release(133); # Super_L
-    },
-    ),
-   'a',
-   'triggered the "a" keybinding');
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(77); # enable Num_Lock
-        xtest_key_release(77); # enable Num_Lock
-        xtest_key_press(133); # Super_L
-        xtest_key_press(38); # a
-        xtest_key_release(38); # a
-        xtest_key_release(133); # Super_L
-        xtest_key_press(77); # disable Num_Lock
-        xtest_key_release(77); # disable Num_Lock
-    },
-    ),
-   'a',
-   'triggered the "a" keybinding');
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(9); # Escape
-        xtest_key_release(9); # Escape
-    },
-    ),
-   'Escape',
-   'triggered the "Escape" keybinding');
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(77); # enable Num_Lock
-        xtest_key_release(77); # enable Num_Lock
-        xtest_key_press(9); # Escape
-        xtest_key_release(9); # Escape
-        xtest_key_press(77); # disable Num_Lock
-        xtest_key_release(77); # disable Num_Lock
-    },
-    ),
-   'Escape',
-   'triggered the "Escape" keybinding');
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(50); # Shift_L
-        xtest_key_press(9); # Escape
-        xtest_key_release(9); # Escape
-        xtest_key_release(50); # Shift_L
-    },
-    ),
-   'Shift+Escape',
-   'triggered the "Escape" keybinding');
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(77); # enable Num_Lock
-        xtest_key_release(77); # enable Num_Lock
-        xtest_key_press(50); # Shift_L
-        xtest_key_press(9); # Escape
-        xtest_key_release(9); # Escape
-        xtest_key_release(50); # Shift_L
-        xtest_key_press(77); # disable Num_Lock
-        xtest_key_release(77); # disable Num_Lock
-    },
-    ),
-   'Shift+Escape',
-   'triggered the "Escape" keybinding');
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(50); # Shift_L
-        xtest_key_press(64); # Alt_L
-        xtest_key_press(24); # q
-        xtest_key_release(24); # q
-        xtest_key_release(64); # Alt_L
-        xtest_key_release(50); # Shift_L
-    },
-    ),
-   'Mod1+Shift+q',
-   'triggered the "Mod1+Shift+q" keybinding');
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(77); # enable Num_Lock
-        xtest_key_release(77); # enable Num_Lock
-        xtest_key_press(50); # Shift_L
-        xtest_key_press(64); # Alt_L
-        xtest_key_press(24); # q
-        xtest_key_release(24); # q
-        xtest_key_release(64); # Alt_L
-        xtest_key_release(50); # Shift_L
-        xtest_key_press(77); # disable Num_Lock
-        xtest_key_release(77); # disable Num_Lock
-    },
-    ),
-   'Mod1+Shift+q',
-   'triggered the "Mod1+Shift+q" keybinding');
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(39); # s
-        xtest_key_release(39); # s
-    },
-    ),
-   's',
-   'triggered the "s" keybinding without Num_Lock');
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(77); # enable Num_Lock
-        xtest_key_release(77); # enable Num_Lock
-        xtest_key_press(39); # s
-        xtest_key_release(39); # s
-        xtest_key_press(77); # disable Num_Lock
-        xtest_key_release(77); # disable Num_Lock
-    },
-    ),
-   's',
-   'triggered the "s" keybinding with Num_Lock');
-
-sync_with_i3;
-is(scalar @i3test::XTEST::binding_events, 12, 'Received exactly 12 binding events');
-
-exit_gracefully($pid);
-
-################################################################################
-# Verify bindings for modifiers work
-################################################################################
-
-$config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-bindsym Mod4+Return nop Return
-
-# Binding which should work with numlock and without, see issue #2559.
-bindcode --release 133 nop Super_L
-EOT
-
-$pid = launch_with_config($config);
-
-start_binding_capture;
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(133); # Super_L
-        xtest_key_release(133); # Super_L
-    },
-    ),
-   'Super_L',
-   'triggered the "Super_L" keybinding without Num_Lock');
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(77); # enable Num_Lock
-        xtest_key_release(77); # enable Num_Lock
-        xtest_key_press(133); # Super_L
-        xtest_key_release(133); # Super_L
-        xtest_key_press(77); # disable Num_Lock
-        xtest_key_release(77); # disable Num_Lock
-    },
-    ),
-   'Super_L',
-   'triggered the "Super_L" keybinding with Num_Lock');
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(133); # Super_L
-        xtest_key_press(36); # Return
-        xtest_key_release(36); # Return
-        xtest_key_release(133); # Super_L
-    },
-    ),
-   'Return',
-   'triggered the "Return" keybinding without Num_Lock');
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(77); # enable Num_Lock
-        xtest_key_release(77); # enable Num_Lock
-        xtest_key_press(133); # Super_L
-        xtest_key_press(36); # Return
-        xtest_key_release(36); # Return
-        xtest_key_release(133); # Super_L
-        xtest_key_press(77); # disable Num_Lock
-        xtest_key_release(77); # disable Num_Lock
-    },
-    ),
-   'Return',
-   'triggered the "Return" keybinding with Num_Lock');
-
-sync_with_i3;
-is(scalar @i3test::XTEST::binding_events, 16, 'Received exactly 16 binding events');
-
-exit_gracefully($pid);
-
-################################################################################
-# Verify the binding is only triggered for KP_End, not KP_1
-################################################################################
-
-$config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-bindsym KP_End nop KP_End
-bindcode 88 nop KP_Down
-EOT
-
-$pid = launch_with_config($config);
-
-start_binding_capture;
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(87); # KP_End
-        xtest_key_release(87); # KP_End
-    },
-    ),
-   'KP_End',
-   'triggered the "KP_End" keybinding');
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(88); # KP_Down
-        xtest_key_release(88); # KP_Down
-    },
-    ),
-   'KP_Down',
-   'triggered the "KP_Down" keybinding');
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(77); # enable Num_Lock
-        xtest_key_release(77); # enable Num_Lock
-        xtest_key_press(87); # KP_1
-        xtest_key_release(87); # KP_1
-        xtest_key_press(77); # disable Num_Lock
-        xtest_key_release(77); # disable Num_Lock
-    },
-    ),
-   'timeout',
-   'Did not trigger the KP_End keybinding with KP_1');
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(77); # enable Num_Lock
-        xtest_key_release(77); # enable Num_Lock
-        xtest_key_press(88); # KP_2
-        xtest_key_release(88); # KP_2
-        xtest_key_press(77); # disable Num_Lock
-        xtest_key_release(77); # disable Num_Lock
-    },
-    ),
-   'timeout',
-   'Did not trigger the KP_Down keybinding with KP_2');
-
-# TODO: This test does not verify that i3 does _NOT_ grab keycode 87 with Mod2.
-
-sync_with_i3;
-is(scalar @i3test::XTEST::binding_events, 18, 'Received exactly 18 binding events');
-
-exit_gracefully($pid);
-
-################################################################################
-# Verify mouse bindings are unaffected by NumLock
-################################################################################
-
-$config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-bindsym --whole-window button4 nop button4
-EOT
-
-$pid = launch_with_config($config);
-
-my $win = open_window;
-
-start_binding_capture;
-
-is(listen_for_binding(
-    sub {
-        xtest_key_press(77); # enable Num_Lock
-        xtest_key_release(77); # enable Num_Lock
-        xtest_button_press(4, 50, 50);
-        xtest_button_release(4, 50, 50);
-        xtest_key_press(77); # disable Num_Lock
-        xtest_key_release(77); # disable Num_Lock
-    },
-    ),
-   'button4',
-   'triggered the button4 keybinding with NumLock');
-
-is(listen_for_binding(
-    sub {
-       xtest_button_press(4, 50, 50);
-       xtest_button_release(4, 50, 50);
-    },
-    ),
-   'button4',
-   'triggered the button4 keybinding without NumLock');
-
-exit_gracefully($pid);
-
-}
-
-done_testing;
diff --git a/testcases/t/265-swap.t b/testcases/t/265-swap.t
deleted file mode 100644 (file)
index 810c39a..0000000
+++ /dev/null
@@ -1,430 +0,0 @@
-#!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 the swap command.
-# Ticket: #917
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
-# i3 config file (v4)
-font font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-for_window[class="mark_A"] mark A
-for_window[class="mark_B"] mark B
-EOT
-
-my ($pid);
-my ($ws, $ws1, $ws2, $ws3);
-my ($nodes, $expected_focus, $A, $B, $F);
-my ($result);
-my @urgent;
-
-$pid = launch_with_config($config);
-
-###############################################################################
-# Invalid con_id should not crash i3
-# See issue #2895.
-###############################################################################
-
-$ws = fresh_workspace;
-
-open_window;
-cmd "swap container with con_id 1";
-
-does_i3_live;
-kill_all_windows;
-
-###############################################################################
-# Swap 2 windows in different workspaces using con_id
-###############################################################################
-
-$ws = fresh_workspace;
-open_window;
-$A = get_focused($ws);
-
-$ws = fresh_workspace;
-open_window;
-
-cmd "swap container with con_id $A";
-is(get_focused($ws), $A, 'A is now focused');
-
-kill_all_windows;
-
-###############################################################################
-# Swap two containers next to each other.
-# Focus should stay on B because both windows are on the focused workspace.
-# The focused container is B.
-#
-# +---+---+    Layout: H1[ A B ]
-# | A | B |    Focus Stacks:
-# +---+---+        H1: B, A
-###############################################################################
-$ws = fresh_workspace;
-
-$A = open_window(wm_class => 'mark_A');
-$B = open_window(wm_class => 'mark_B');
-$expected_focus = get_focused($ws);
-
-cmd '[con_mark=B] swap container with mark A';
-
-$nodes = get_ws_content($ws);
-is($nodes->[0]->{window}, $B->{id}, 'B is on the left');
-is($nodes->[1]->{window}, $A->{id}, 'A is on the right');
-is(get_focused($ws), $expected_focus, 'B is still focused');
-
-kill_all_windows;
-
-###############################################################################
-# Swap two containers with different parents.
-# In this test, the focus head of the left v-split container is A.
-# The focused container is B.
-#
-# +---+---+    Layout: H1[ V1[ A Y ] V2[ X B ] ]
-# | A | X |    Focus Stacks:
-# +---+---+        H1: V2, V1
-# | Y | B |        V1: A, Y
-# +---+---+        V2: B, X
-###############################################################################
-$ws = fresh_workspace;
-
-$A = open_window(wm_class => 'mark_A');
-$B = open_window(wm_class => 'mark_B');
-cmd 'split v';
-open_window;
-cmd 'move up, focus left';
-cmd 'split v';
-open_window;
-cmd 'focus up, focus right, focus down';
-$expected_focus = get_focused($ws);
-
-cmd '[con_mark=B] swap container with mark A';
-
-$nodes = get_ws_content($ws);
-is($nodes->[0]->{nodes}->[0]->{window}, $B->{id}, 'B is on the top left');
-is($nodes->[1]->{nodes}->[1]->{window}, $A->{id}, 'A is on the bottom right');
-is(get_focused($ws), $expected_focus, 'B is still focused');
-
-kill_all_windows;
-
-###############################################################################
-# Swap two containers with different parents.
-# In this test, the focus head of the left v-split container is _not_ A.
-# The focused container is B.
-#
-# +---+---+    Layout: H1[ V1[ A Y ] V2[ X B ] ]
-# | A | X |    Focus Stacks:
-# +---+---+        H1: V2, V1
-# | Y | B |        V1: Y, A
-# +---+---+        V2: B, X
-###############################################################################
-$ws = fresh_workspace;
-
-$A = open_window(wm_class => 'mark_A');
-$B = open_window(wm_class => 'mark_B');
-cmd 'split v';
-open_window;
-cmd 'move up, focus left';
-cmd 'split v';
-open_window;
-cmd 'focus right, focus down';
-$expected_focus = get_focused($ws);
-
-cmd '[con_mark=B] swap container with mark A';
-
-$nodes = get_ws_content($ws);
-is($nodes->[0]->{nodes}->[0]->{window}, $B->{id}, 'B is on the top left');
-is($nodes->[1]->{nodes}->[1]->{window}, $A->{id}, 'A is on the bottom right');
-is(get_focused($ws), $expected_focus, 'B is still focused');
-
-kill_all_windows;
-
-###############################################################################
-# Swap two containers with one being on a different workspace.
-# The focused container is B.
-#
-# Layout: O1[ W1[ H1 ] W2[ H2 ] ]
-# Focus Stacks:
-#     O1: W2, W1
-#
-# +---+---+    Layout: H1[ A X ]
-# | A | X |    Focus Stacks:
-# +---+---+        H1: A, X
-#
-# +---+---+    Layout: H2[ Y, B ]
-# | Y | B |    Focus Stacks:
-# +---+---+        H2: B, Y
-###############################################################################
-$ws1 = fresh_workspace;
-$A = open_window(wm_class => 'mark_A');
-$expected_focus = get_focused($ws1);
-open_window;
-cmd 'focus left';
-
-$ws2 = fresh_workspace;
-open_window;
-$B = open_window(wm_class => 'mark_B');
-
-cmd '[con_mark=B] swap container with mark A';
-
-$nodes = get_ws_content($ws1);
-is($nodes->[0]->{window}, $B->{id}, 'B is on ws1:left');
-
-$nodes = get_ws_content($ws2);
-is($nodes->[1]->{window}, $A->{id}, 'A is on ws2:right');
-is(get_focused($ws2), $expected_focus, 'A is focused');
-
-kill_all_windows;
-
-###############################################################################
-# Swap two non-focused containers within the same workspace.
-#
-# +---+---+    Layout: H1[ V1[ A X ] V2[ F B ] ]
-# | A | F |    Focus Stacks:
-# +---+---+        H1: V2, V1
-# | X | B |        V1: A, X
-# +---+---+        V2: F, B
-###############################################################################
-$ws = fresh_workspace;
-
-$A = open_window(wm_class => 'mark_A');
-$B = open_window(wm_class => 'mark_B');
-cmd 'split v';
-open_window;
-cmd 'move up, focus left';
-cmd 'split v';
-open_window;
-cmd 'focus up, focus right';
-$expected_focus = get_focused($ws);
-
-cmd '[con_mark=B] swap container with mark A';
-
-$nodes = get_ws_content($ws);
-is($nodes->[0]->{nodes}->[0]->{window}, $B->{id}, 'B is on the top left');
-is($nodes->[1]->{nodes}->[1]->{window}, $A->{id}, 'A is on the bottom right');
-is(get_focused($ws), $expected_focus, 'F is still focused');
-
-kill_all_windows;
-
-###############################################################################
-# Swap two non-focused containers which are both on different workspaces.
-#
-# Layout: O1[ W1[ A ] W2[ B ] W3[ F ] ]
-# Focus Stacks:
-#     O1: W3, W2, W1
-#
-# +---+
-# | A |
-# +---+
-#
-# +---+
-# | B |
-# +---+
-#
-# +---+
-# | F |
-# +---+
-###############################################################################
-$ws1 = fresh_workspace;
-$A = open_window(wm_class => 'mark_A');
-
-$ws2 = fresh_workspace;
-$B = open_window(wm_class => 'mark_B');
-
-$ws3 = fresh_workspace;
-open_window;
-$expected_focus = get_focused($ws3);
-
-cmd '[con_mark=B] swap container with mark A';
-
-$nodes = get_ws_content($ws1);
-is($nodes->[0]->{window}, $B->{id}, 'B is on the first workspace');
-
-$nodes = get_ws_content($ws2);
-is($nodes->[0]->{window}, $A->{id}, 'A is on the second workspace');
-
-is(get_focused($ws3), $expected_focus, 'F is still focused');
-
-kill_all_windows;
-
-###############################################################################
-# Swap two non-focused containers with one being on a different workspace.
-#
-# Layout: O1[ W1[ A ] W2[ H2 ] ]
-# Focus Stacks:
-#     O1: W2, W1
-#
-# +---+
-# | A |
-# +---+
-#
-# +---+---+    Layout: H2[ B, F ]
-# | B | F |    Focus Stacks:
-# +---+---+        H2: F, B
-###############################################################################
-
-$ws1 = fresh_workspace;
-$A = open_window(wm_class => 'mark_A');
-
-$ws2 = fresh_workspace;
-$B = open_window(wm_class => 'mark_B');
-open_window;
-$expected_focus = get_focused($ws2);
-
-cmd '[con_mark=B] swap container with mark A';
-
-$nodes = get_ws_content($ws1);
-is($nodes->[0]->{window}, $B->{id}, 'B is on the first workspace');
-
-$nodes = get_ws_content($ws2);
-is($nodes->[0]->{window}, $A->{id}, 'A is on the left of the second workspace');
-is(get_focused($ws2), $expected_focus, 'F is still focused');
-
-kill_all_windows;
-
-###############################################################################
-# 1. A container cannot be swapped with its parent.
-# 2. A container cannot be swapped with one of its children.
-#
-#      ↓A↓
-# +---+---+    Layout: H1[ X V1[ Y B ] ]
-# |   | Y |        (with A := V1)
-# | X +---+
-# |   | B |
-# +---+---+
-###############################################################################
-$ws = fresh_workspace;
-open_window;
-open_window;
-cmd 'split v';
-$B = open_window(wm_class => 'mark_B');
-cmd 'focus parent, mark A, focus child';
-
-$result = cmd '[con_mark=B] swap container with mark A';
-is($result->[0]->{success}, 0, 'B cannot be swappd with its parent');
-
-$result = cmd '[con_mark=A] swap container with mark B';
-is($result->[0]->{success}, 0, 'A cannot be swappd with one of its children');
-
-kill_all_windows;
-
-###############################################################################
-# Swapping two containers preserves the geometry of the container they are
-# being swapped with.
-#
-# Before:
-# +---+-------+
-# | A |   B   |
-# +---+-------+
-#
-# After:
-# +---+-------+
-# | B |   A   |
-# +---+-------+
-###############################################################################
-$ws = fresh_workspace;
-$A = open_window(wm_class => 'mark_A');
-$B = open_window(wm_class => 'mark_B');
-cmd 'resize grow width 0 or 25 ppt';
-
-# sanity checks
-$nodes = get_ws_content($ws);
-cmp_float($nodes->[0]->{percent}, 0.25, 'A has 25% width');
-cmp_float($nodes->[1]->{percent}, 0.75, 'B has 75% width');
-
-cmd '[con_mark=B] swap container with mark A';
-
-$nodes = get_ws_content($ws);
-cmp_float($nodes->[0]->{percent}, 0.25, 'B has 25% width');
-cmp_float($nodes->[1]->{percent}, 0.75, 'A has 75% width');
-
-kill_all_windows;
-
-###############################################################################
-# Swapping containers not sharing the same parent preserves the geometry of
-# the container they are swapped with.
-#
-# Before:
-# +---+-----+
-# | A |     |
-# +---+  B  |
-# |   |     |
-# | Y +-----+
-# |   |  X  |
-# +---+-----+
-#
-# After:
-# +---+-----+
-# | B |     |
-# +---+  A  |
-# |   |     |
-# | Y +-----+
-# |   |  X  |
-# +---+-----+
-###############################################################################
-$ws = fresh_workspace;
-
-$A = open_window(wm_class => 'mark_A');
-$B = open_window(wm_class => 'mark_B');
-cmd 'split v';
-open_window;
-cmd 'focus up, resize grow height 0 or 25 ppt';
-cmd 'focus left, split v';
-open_window;
-cmd 'resize grow height 0 or 25 ppt';
-
-# sanity checks
-$nodes = get_ws_content($ws);
-cmp_float($nodes->[0]->{nodes}->[0]->{percent}, 0.25, 'A has 25% height');
-cmp_float($nodes->[1]->{nodes}->[0]->{percent}, 0.75, 'B has 75% height');
-
-cmd '[con_mark=B] swap container with mark A';
-
-$nodes = get_ws_content($ws);
-cmp_float($nodes->[0]->{nodes}->[0]->{percent}, 0.25, 'B has 25% height');
-cmp_float($nodes->[1]->{nodes}->[0]->{percent}, 0.75, 'A has 75% height');
-
-kill_all_windows;
-
-###############################################################################
-# Swapping containers moves the urgency hint correctly.
-###############################################################################
-
-$ws1 = fresh_workspace;
-$A = open_window(wm_class => 'mark_A');
-$ws2 = fresh_workspace;
-$B = open_window(wm_class => 'mark_B');
-open_window;
-
-$B->add_hint('urgency');
-sync_with_i3;
-
-cmd '[con_mark=B] swap container with mark A';
-
-@urgent = grep { $_->{urgent} } @{get_ws_content($ws1)};
-is(@urgent, 1, 'B is marked urgent');
-is(get_ws($ws1)->{urgent}, 1, 'the first workspace is marked urgent');
-
-@urgent = grep { $_->{urgent} } @{get_ws_content($ws2)};
-is(@urgent, 0, 'A is not marked urgent');
-is(get_ws($ws2)->{urgent}, 0, 'the second workspace is not marked urgent');
-
-kill_all_windows;
-
-###############################################################################
-
-exit_gracefully($pid);
-
-done_testing;
diff --git a/testcases/t/270-config-no-newline-end.t b/testcases/t/270-config-no-newline-end.t
new file mode 100644 (file)
index 0000000..4e7ccbf
--- /dev/null
@@ -0,0 +1,41 @@
+#!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)
+#
+# Make sure that configs that end without a newline don't crash i3.
+# Ticket: #2934
+use i3test i3_autostart => 0;
+
+my $first_lines = <<'EOT';
+set $workspace1 workspace number 1
+set $workspace0 workspace eggs
+
+bindsym Mod4+1 $workspace1
+EOT
+
+# Intentionally don't add a trailing newline for the last line since this is
+# what triggered the bug.
+my $last_line = 'bindsym Mod4+0 $workspace0';
+my $config = "${first_lines}${last_line}";
+
+my $pid = launch_with_config($config);
+does_i3_live;
+
+my $i3 = i3(get_socket_path());
+my $ws = $i3->get_workspaces->recv;
+is($ws->[0]->{name}, 'eggs', 'last line processed correctly');
+
+exit_gracefully($pid);
+done_testing;
diff --git a/testcases/t/271-for_window_tilingfloating.t b/testcases/t/271-for_window_tilingfloating.t
new file mode 100644 (file)
index 0000000..d480a85
--- /dev/null
@@ -0,0 +1,44 @@
+#!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)
+#
+use i3test i3_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+for_window [tiling] mark tiled
+for_window [floating] mark floated
+EOT
+use X11::XCB qw(PROP_MODE_REPLACE);
+
+##############################################################
+# 13: check that the tiling / floating criteria work.
+##############################################################
+
+my $tmp = fresh_workspace;
+
+open_window;
+open_floating_window;
+
+my @nodes = @{get_ws($tmp)->{nodes}};
+cmp_ok(@nodes, '==', 1, 'one tiling container on this workspace');
+is_deeply($nodes[0]->{marks}, [ 'tiled' ], "mark set for 'tiling' criterion");
+
+@nodes = @{get_ws($tmp)->{floating_nodes}};
+cmp_ok(@nodes, '==', 1, 'one floating container on this workspace');
+is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'floated' ], "mark set for 'floating' criterion");
+
+##############################################################
+
+done_testing;
diff --git a/testcases/t/272-regress-focus-assign.t b/testcases/t/272-regress-focus-assign.t
new file mode 100644 (file)
index 0000000..6b0aad0
--- /dev/null
@@ -0,0 +1,71 @@
+#!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)
+#
+# Regression: Checks if focus is stolen when a window is managed which is
+# assigned to an invisible workspace
+#
+use i3test i3_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+assign [class="special"] targetws
+EOT
+
+sub open_special {
+    my %args = @_;
+    $args{name} //= 'special window';
+
+    # We use dont_map because i3 will not map the window on the current
+    # workspace. Thus, open_window would time out in wait_for_map (2 seconds).
+    my $window = open_window(
+        %args,
+        wm_class => 'special',
+        dont_map => 1,
+    );
+    $window->map;
+    return $window;
+}
+
+#####################################################################
+# start a window and see that it does not get assigned with an empty config
+#####################################################################
+
+my $tmp = fresh_workspace;
+
+ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
+ok(get_ws($tmp)->{focused}, 'current workspace focused');
+
+my $window = open_special;
+sync_with_i3;
+
+ok(@{get_ws_content($tmp)} == 0, 'special window not on current workspace');
+ok(@{get_ws_content('targetws')} == 1, 'special window on targetws');
+ok(get_ws($tmp)->{focused}, 'current workspace still focused');
+
+#####################################################################
+# the same test, but with a floating window
+#####################################################################
+
+$window = open_special(
+    window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
+);
+
+ok(@{get_ws_content($tmp)} == 0, 'special window not on current workspace');
+ok(@{get_ws_content('targetws')} == 1, 'special window on targetws');
+ok(get_ws($tmp)->{focused}, 'current workspace still focused');
+
+$window->destroy;
+
+done_testing;
diff --git a/testcases/t/273-regress-focus-toggle.t b/testcases/t/273-regress-focus-toggle.t
new file mode 100644 (file)
index 0000000..192e975
--- /dev/null
@@ -0,0 +1,30 @@
+#!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)
+#
+# Regression: Checks if i3 still lives after using 'focus mode_toggle' on an
+# empty workspace. This regression was fixed in
+# 0848844f2d41055f6ffc69af1149d7a873460976.
+#
+use i3test;
+use v5.10;
+
+my $tmp = fresh_workspace;
+
+cmd 'focus mode_toggle';
+
+does_i3_live;
+
+done_testing;
diff --git a/testcases/t/274-move-branch-position.t b/testcases/t/274-move-branch-position.t
new file mode 100644 (file)
index 0000000..c2928c9
--- /dev/null
@@ -0,0 +1,376 @@
+#!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 movement of a con into a branch will place the moving con at the
+# correct position within the branch.
+#
+# If the direction of movement is the same as the orientation of the branch
+# container, append or prepend the container to the branch in the obvious way.
+# If the movement is to the right or downward, insert the moving container in
+# the first position (i.e., the leftmost or top position resp.) If the movement
+# is to the left or upward, insert the moving container in the last position
+# (i.e., the rightmost or bottom position resp.)
+#
+# If the direction of movement is different from the orientation of the branch
+# container, insert the container into the branch after the focused-inactive
+# container.
+#
+# For testing purposes, we will demonstrate the behavior for tabbed containers
+# to represent the case of split-horizontal branches and stacked containers to
+# represent the case of split-vertical branches.
+#
+# Ticket: #1060
+# Bug still in: 4.6-109-g18cfc36
+
+use i3test;
+
+# Opens tabs on the presently focused branch and adds several additional
+# windows. Shifts focus to somewhere in the middle of the tabs so the most
+# general case can be assumed.
+sub open_tabs {
+    cmd 'layout tabbed';
+    open_window;
+    open_window;
+    open_window;
+    open_window;
+    cmd 'focus left; focus left'
+}
+
+# Likewise for a stack
+sub open_stack {
+    cmd 'layout stacking';
+    open_window;
+    open_window;
+    open_window;
+    open_window;
+    cmd 'focus up; focus up'
+}
+
+# Gets the position of the given leaf within the given branch. The first
+# position is one (1). Returns negative one (-1) if the leaf cannot be found
+# within the branch.
+sub get_leaf_position {
+    my ($branch, $leaf) = @_;
+    my $position = -1;
+    for my $i (0 .. @{$branch->{nodes}}) {
+        if ($branch->{nodes}[$i]->{id} == $leaf) {
+            $position = $i + 1;
+            last;
+        };
+    }
+    return $position;
+}
+
+# convenience function to focus a con by id to avoid having to type an ugly
+# command each time
+sub focus_con {
+    my $con_id = shift @_;
+    cmd "[con_id=\"$con_id\"] focus";
+}
+
+# Places a leaf into a branch and focuses the leaf. The newly created branch
+# will have orientation specified by the second parameter.
+sub branchify {
+    my ($con_id, $orientation) = @_;
+    focus_con($con_id);
+    $orientation eq 'horizontal' ? cmd 'splith' : cmd 'splitv';
+    open_window;
+    focus_con($con_id);
+}
+
+##############################################################################
+# When moving a con right into tabs, the moving con should be placed as the
+# first tab in the branch
+##############################################################################
+my $ws = fresh_workspace;
+
+# create the target leaf
+open_window;
+my $target_leaf = get_focused($ws);
+
+# create the tabbed branch container
+open_window;
+cmd 'splith';
+open_tabs;
+
+# move the target leaf into the tabbed branch
+focus_con($target_leaf);
+cmd 'move right';
+
+# the target leaf should be the first in the branch
+my $branch = shift @{get_ws_content($ws)};
+is($branch->{nodes}[0]->{id}, $target_leaf, 'moving con right into tabs placed it as the first tab in the branch');
+
+# repeat the test when the target is in a branch
+cmd 'move up; move left';
+branchify($target_leaf, 'vertical');
+cmd 'move right';
+
+$branch = pop @{get_ws_content($ws)};
+is($branch->{nodes}[0]->{id}, $target_leaf, 'moving con right into tabs from a branch placed it as the first tab in the branch');
+
+##############################################################################
+# When moving a con right into a stack, the moving con should be placed
+# below the focused-inactive leaf
+##############################################################################
+$ws = fresh_workspace;
+
+# create the target leaf
+open_window;
+$target_leaf = get_focused($ws);
+
+# create the stacked branch container and find the focused leaf
+open_window;
+cmd 'splith';
+open_stack;
+my $secondary_leaf = get_focused($ws);
+
+# move the target leaf into the stacked branch
+focus_con($target_leaf);
+cmd 'move right';
+
+# the secondary focus leaf should be below the target
+$branch = shift @{get_ws_content($ws)};
+my $target_leaf_position = get_leaf_position($branch, $target_leaf);
+my $secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
+
+is($target_leaf_position, $secondary_leaf_position + 1, 'moving con right into a stack placed it below the focused-inactive leaf');
+
+# repeat the test when the target is in a branch
+cmd 'move up; move left';
+branchify($target_leaf, 'vertical');
+cmd 'move right';
+
+$branch = pop @{get_ws_content($ws)};
+$target_leaf_position = get_leaf_position($branch, $target_leaf);
+$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
+
+is($target_leaf_position, $secondary_leaf_position + 1, 'moving con right into a stack from a branch placed it below the focused-inactive leaf');
+
+##############################################################################
+# When moving a con down into a stack, the moving con should be placed at the
+# top of the stack
+##############################################################################
+$ws = fresh_workspace;
+cmd 'layout splitv';
+
+# create the target leaf
+open_window;
+$target_leaf = get_focused($ws);
+
+# create the stacked branch container
+open_window;
+cmd 'splitv';
+open_stack;
+
+# move the target leaf into the stacked branch
+focus_con($target_leaf);
+cmd 'move down';
+
+# the target leaf should be on the top of the stack
+$branch = shift @{get_ws_content($ws)};
+is($branch->{nodes}[0]->{id}, $target_leaf, 'moving con down into a stack placed it on the top of the stack');
+
+# repeat the test when the target is in a branch
+cmd 'move right; move up';
+branchify($target_leaf, 'horizontal');
+cmd 'move down';
+
+$branch = pop @{get_ws_content($ws)};
+is($branch->{nodes}[0]->{id}, $target_leaf, 'moving con down into a stack from a branch placed it on the top of the stack');
+
+##############################################################################
+# When moving a con down into tabs, the moving con should be placed after the
+# focused-inactive tab
+##############################################################################
+$ws = fresh_workspace;
+cmd 'layout splitv';
+
+# create the target leaf
+open_window;
+$target_leaf = get_focused($ws);
+
+# create the tabbed branch container and find the focused tab
+open_window;
+cmd 'splitv';
+open_tabs;
+$secondary_leaf = get_focused($ws);
+
+# move the target leaf into the tabbed branch
+focus_con($target_leaf);
+cmd 'move down';
+
+# the secondary focus tab should be to the right
+$branch = shift @{get_ws_content($ws)};
+$target_leaf_position = get_leaf_position($branch, $target_leaf);
+$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
+
+is($target_leaf_position, $secondary_leaf_position + 1, 'moving con down into tabs placed it after the focused-inactive tab');
+
+# repeat the test when the target is in a branch
+cmd 'move right; move up';
+branchify($target_leaf, 'horizontal');
+cmd 'move down';
+
+$branch = pop @{get_ws_content($ws)};
+$target_leaf_position = get_leaf_position($branch, $target_leaf);
+$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
+
+is($target_leaf_position, $secondary_leaf_position + 1, 'moving con down into tabs from a branch placed it after the focused-inactive tab');
+
+##############################################################################
+# When moving a con left into tabs, the moving con should be placed as the last
+# tab in the branch
+##############################################################################
+$ws = fresh_workspace;
+
+# create the tabbed branch container
+open_window;
+cmd 'splith';
+open_tabs;
+
+# create the target leaf
+cmd 'focus parent';
+open_window;
+$target_leaf = get_focused($ws);
+
+# move the target leaf into the tabbed branch
+cmd 'move left';
+
+# the target leaf should be last in the branch
+$branch = shift @{get_ws_content($ws)};
+
+is($branch->{nodes}->[-1]->{id}, $target_leaf, 'moving con left into tabs placed it as the last tab in the branch');
+
+# repeat the test when the target leaf is in a branch
+cmd 'move up; move right';
+branchify($target_leaf, 'vertical');
+cmd 'move left';
+
+$branch = shift @{get_ws_content($ws)};
+is($branch->{nodes}->[-1]->{id}, $target_leaf, 'moving con left into tabs from a branch placed it as the last tab in the branch');
+
+##############################################################################
+# When moving a con left into a stack, the moving con should be placed below
+# the focused-inactive leaf
+##############################################################################
+$ws = fresh_workspace;
+
+# create the stacked branch container and find the focused leaf
+open_window;
+open_stack;
+$secondary_leaf = get_focused($ws);
+
+# create the target leaf to the right
+cmd 'focus parent';
+open_window;
+$target_leaf = get_focused($ws);
+
+# move the target leaf into the stacked branch
+cmd 'move left';
+
+# the secondary focus leaf should be below
+$branch = shift @{get_ws_content($ws)};
+$target_leaf_position = get_leaf_position($branch, $target_leaf);
+$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
+
+is($target_leaf_position, $secondary_leaf_position + 1, 'moving con left into a stack placed it below the focused-inactive leaf');
+
+# repeat the test when the target leaf is in a branch
+cmd 'move up; move right';
+branchify($target_leaf, 'vertical');
+cmd 'move left';
+
+$branch = shift @{get_ws_content($ws)};
+$target_leaf_position = get_leaf_position($branch, $target_leaf);
+$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
+
+is($target_leaf_position, $secondary_leaf_position + 1, 'moving con left into a stack from a branch placed it below the focused-inactive leaf');
+
+##############################################################################
+# When moving a con up into a stack, the moving con should be placed last in
+# the stack
+##############################################################################
+$ws = fresh_workspace;
+cmd 'layout splitv';
+
+# create the stacked branch container
+open_window;
+cmd 'splitv';
+open_stack;
+
+# create the target leaf
+cmd 'focus parent';
+open_window;
+$target_leaf = get_focused($ws);
+
+# move the target leaf into the stacked branch
+cmd 'move up';
+
+# the target leaf should be on the bottom of the stack
+$branch = shift @{get_ws_content($ws)};
+
+is($branch->{nodes}->[-1]->{id}, $target_leaf, 'moving con up into stack placed it on the bottom of the stack');
+
+# repeat the test when the target leaf is in a branch
+cmd 'move right; move down';
+branchify($target_leaf, 'horizontal');
+cmd 'move up';
+
+$branch = shift @{get_ws_content($ws)};
+
+is($branch->{nodes}->[-1]->{id}, $target_leaf, 'moving con up into stack from a branch placed it on the bottom of the stack');
+
+##############################################################################
+# When moving a con up into tabs, the moving con should be placed after the
+# focused-inactive tab
+##############################################################################
+$ws = fresh_workspace;
+cmd 'layout splitv';
+
+# create the tabbed branch container and find the focused leaf
+open_window;
+cmd 'splitv';
+open_tabs;
+$secondary_leaf = get_focused($ws);
+
+# create the target leaf below
+cmd 'focus parent';
+open_window;
+$target_leaf = get_focused($ws);
+
+# move the target leaf into the tabbed branch
+cmd 'move up';
+
+# the secondary focus tab should be to the right
+$branch = shift @{get_ws_content($ws)};
+$target_leaf_position = get_leaf_position($branch, $target_leaf);
+$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
+
+is($target_leaf_position, $secondary_leaf_position + 1, 'moving con up into tabs placed it after the focused-inactive tab');
+
+# repeat the test when the target leaf is in a branch
+cmd 'move right; move down';
+branchify($target_leaf, 'horizontal');
+cmd 'move up';
+
+$branch = shift @{get_ws_content($ws)};
+$target_leaf_position = get_leaf_position($branch, $target_leaf);
+$secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
+
+is($target_leaf_position, $secondary_leaf_position + 1, 'moving con up into tabs from a branch placed it after the focused-inactive tab');
+
+done_testing;
diff --git a/testcases/t/275-ipc-window-close.t b/testcases/t/275-ipc-window-close.t
new file mode 100644 (file)
index 0000000..3483cf4
--- /dev/null
@@ -0,0 +1,52 @@
+#!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 that the ipc close event works properly
+#
+# Bug still in: 4.8-7-gf4a8253
+use i3test;
+
+my $i3 = i3(get_socket_path());
+$i3->connect()->recv;
+
+my $cv;
+my $t;
+
+sub reset_test {
+    $cv = AE::cv;
+    $t = AE::timer(0.5, 0, sub { $cv->send(0); });
+}
+
+reset_test;
+
+$i3->subscribe({
+        window => sub {
+            my ($e) = @_;
+            if ($e->{change} eq 'close') {
+                $cv->send($e->{container});
+            }
+        },
+    })->recv;
+
+my $window = open_window;
+
+cmd 'kill';
+my $con = $cv->recv;
+
+ok($con, 'closing a window should send the window::close event');
+is($con->{window}, $window->{id}, 'the event should contain information about the window');
+
+done_testing;
diff --git a/testcases/t/276-ipc-window-move.t b/testcases/t/276-ipc-window-move.t
new file mode 100644 (file)
index 0000000..117d27f
--- /dev/null
@@ -0,0 +1,61 @@
+#!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 that the ipc window::move event works properly
+#
+# Bug still in: 4.8-7-gf4a8253
+use i3test;
+
+my $i3 = i3(get_socket_path());
+$i3->connect()->recv;
+
+my $cv;
+my $t;
+
+sub reset_test {
+    $cv = AE::cv;
+    $t = AE::timer(0.5, 0, sub { $cv->send(0); });
+}
+
+reset_test;
+
+$i3->subscribe({
+        window => sub {
+            my ($e) = @_;
+            if ($e->{change} eq 'move') {
+                $cv->send($e->{container});
+            }
+        },
+    })->recv;
+
+my $dummy_window = open_window;
+my $window = open_window;
+
+cmd 'move right';
+my $con = $cv->recv;
+
+ok($con, 'moving a window should emit the window::move event');
+is($con->{window}, $window->{id}, 'the event should contain info about the window');
+
+reset_test;
+
+cmd 'move to workspace ws_new';
+$con = $cv->recv;
+
+ok($con, 'moving a window to a different workspace should emit the window::move event');
+is($con->{window}, $window->{id}, 'the event should contain info about the window');
+
+done_testing;
diff --git a/testcases/t/277-ipc-window-urgent.t b/testcases/t/277-ipc-window-urgent.t
new file mode 100644 (file)
index 0000000..09226ff
--- /dev/null
@@ -0,0 +1,68 @@
+#!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 the window::urgent event works correctly. The window::urgent event
+# should be emitted when a window becomes urgent or loses its urgent status.
+#
+use i3test;
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+force_display_urgency_hint 0ms
+EOT
+
+my $i3 = i3(get_socket_path());
+$i3->connect()->recv;
+
+my $cv;
+$i3->subscribe({
+    window => sub {
+        my ($event) = @_;
+        $cv->send($event) if $event->{change} eq 'urgent';
+    }
+})->recv;
+
+my $t;
+$t = AnyEvent->timer(
+    after => 0.5,
+    cb => sub {
+        $cv->send(0);
+    }
+);
+
+$cv = AnyEvent->condvar;
+fresh_workspace;
+my $win = open_window;
+my $dummy_win = open_window;
+
+$win->add_hint('urgency');
+my $event = $cv->recv;
+
+isnt($event, 0, 'an urgent con should emit the window::urgent event');
+is($event->{container}->{window}, $win->{id}, 'the event should contain information about the window');
+is($event->{container}->{urgent}, 1, 'the container should be urgent');
+
+$cv = AnyEvent->condvar;
+$win->delete_hint('urgency');
+$event = $cv->recv;
+
+isnt($event, 0, 'an urgent con should emit the window::urgent event');
+is($event->{container}->{window}, $win->{id}, 'the event should contain information about the window');
+is($event->{container}->{urgent}, 0, 'the container should not be urgent');
+
+done_testing;
diff --git a/testcases/t/278-layout-restore-output.t b/testcases/t/278-layout-restore-output.t
new file mode 100644 (file)
index 0000000..5a1f376
--- /dev/null
@@ -0,0 +1,234 @@
+#!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)
+#
+# Verifies that entire outputs can be saved and restored properly by i3.
+# Ticket: #1306
+# Bug still in: 4.8-26-gf96ec19
+use i3test;
+use File::Temp qw(tempfile);
+use IO::Handle;
+
+my $ws = fresh_workspace;
+
+################################################################################
+# Append a new workspace with a name.
+################################################################################
+
+ok(!workspace_exists('ws_new'), 'workspace "ws_new" does not exist yet');
+
+my ($fh, $filename) = tempfile(UNLINK => 1);
+print $fh <<'EOT';
+// vim:ts=4:sw=4:et
+{
+    // workspace with 1 children
+    "border": "pixel",
+    "floating": "auto_off",
+    "layout": "splith",
+    "percent": null,
+    "type": "workspace",
+    "name": "ws_new",
+    "nodes": [
+        {
+            "border": "pixel",
+            "floating": "auto_off",
+            "geometry": {
+               "height": 268,
+               "width": 484,
+               "x": 0,
+               "y": 0
+            },
+            "name": "vals@w00t: ~",
+            "percent": 1,
+            "swallows": [
+               {
+                  "class": "^URxvt$"
+               // "instance": "^urxvt$",
+               // "title": "^vals\\@w00t\\:\\ \\~$"
+               }
+            ],
+            "type": "con"
+        }
+    ]
+}
+EOT
+$fh->flush;
+cmd "append_layout $filename";
+
+ok(workspace_exists('ws_new'), 'workspace "ws_new" exists now');
+
+does_i3_live;
+
+close($fh);
+
+################################################################################
+# Append a new workspace with a name that clashes with an existing workspace.
+################################################################################
+
+my @old_workspaces = @{get_workspace_names()};
+
+cmd "append_layout $filename";
+
+my @new_workspaces = @{get_workspace_names()};
+cmp_ok(scalar @new_workspaces, '>', scalar @old_workspaces, 'more workspaces than before');
+
+my %created_workspaces = map { ($_, 1) } @new_workspaces;
+delete $created_workspaces{$_} for @old_workspaces;
+diag('created workspaces = ' . Dumper(keys %created_workspaces));
+cmp_ok(scalar keys %created_workspaces, '>', 0, 'new workspaces appeared');
+
+################################################################################
+# Append a new workspace without a name.
+################################################################################
+
+ok(!workspace_exists('unnamed'), 'workspace "unnamed" does not exist yet');
+
+($fh, $filename) = tempfile(UNLINK => 1);
+print $fh <<'EOT';
+// vim:ts=4:sw=4:et
+{
+    // workspace with 1 children
+    "border": "pixel",
+    "floating": "auto_off",
+    "layout": "splith",
+    "percent": null,
+    "type": "workspace",
+    "nodes": [
+        {
+            "border": "pixel",
+            "floating": "auto_off",
+            "geometry": {
+               "height": 268,
+               "width": 484,
+               "x": 0,
+               "y": 0
+            },
+            "name": "vals@w00t: ~",
+            "percent": 1,
+            "swallows": [
+               {
+                  "class": "^URxvt$"
+               // "instance": "^urxvt$",
+               // "title": "^vals\\@w00t\\:\\ \\~$"
+               }
+            ],
+            "type": "con"
+        }
+    ]
+}
+EOT
+$fh->flush;
+cmd "append_layout $filename";
+
+ok(workspace_exists('unnamed'), 'workspace "unnamed" exists now');
+
+################################################################################
+# Append a workspace with a numeric name, ensure it has ->num set.
+################################################################################
+
+ok(!workspace_exists('4'), 'workspace "4" does not exist yet');
+
+($fh, $filename) = tempfile(UNLINK => 1);
+print $fh <<'EOT';
+// vim:ts=4:sw=4:et
+{
+    // workspace with 1 children
+    "border": "pixel",
+    "floating": "auto_off",
+    "layout": "splith",
+    "percent": null,
+    "type": "workspace",
+    "name": "4",
+    "nodes": [
+        {
+            "border": "pixel",
+            "floating": "auto_off",
+            "geometry": {
+               "height": 268,
+               "width": 484,
+               "x": 0,
+               "y": 0
+            },
+            "name": "vals@w00t: ~",
+            "percent": 1,
+            "swallows": [
+               {
+                  "class": "^URxvt$"
+               // "instance": "^urxvt$",
+               // "title": "^vals\\@w00t\\:\\ \\~$"
+               }
+            ],
+            "type": "con"
+        }
+    ]
+}
+EOT
+$fh->flush;
+cmd "append_layout $filename";
+
+ok(workspace_exists('4'), 'workspace "4" exists now');
+$ws = get_ws("4");
+is($ws->{num}, 4, 'workspace number is 4');
+
+################################################################################
+# Append a workspace with a numeric name, with the “type” property at the end
+# of the JSON blurb (which is valid and sometimes happens).
+################################################################################
+
+ok(!workspace_exists('5'), 'workspace "5" does not exist yet');
+
+($fh, $filename) = tempfile(UNLINK => 1);
+print $fh <<'EOT';
+// vim:ts=4:sw=4:et
+{
+    // workspace with 1 children
+    "border": "pixel",
+    "floating": "auto_off",
+    "layout": "splith",
+    "percent": null,
+    "name": "5",
+    "nodes": [
+        {
+            "border": "pixel",
+            "floating": "auto_off",
+            "geometry": {
+               "height": 268,
+               "width": 484,
+               "x": 0,
+               "y": 0
+            },
+            "name": "vals@w00t: ~",
+            "percent": 1,
+            "swallows": [
+               {
+                  "class": "^URxvt$"
+               // "instance": "^urxvt$",
+               // "title": "^vals\\@w00t\\:\\ \\~$"
+               }
+            ],
+            "type": "con"
+        }
+    ],
+    "type": "workspace"
+}
+EOT
+$fh->flush;
+cmd "append_layout $filename";
+
+ok(workspace_exists('5'), 'workspace "5" exists now');
+$ws = get_ws("5");
+is($ws->{num}, 5, 'workspace number is 5');
+
+done_testing;
diff --git a/testcases/t/279-regress-default-floating-border.t b/testcases/t/279-regress-default-floating-border.t
new file mode 100644 (file)
index 0000000..5563ec3
--- /dev/null
@@ -0,0 +1,37 @@
+#!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)
+#
+# This is a regression test for a bug where a normal floating default border is
+# not applied when the default tiling border is set to a pixel value.
+# Ticket: #1305
+# Bug still in: 4.8-62-g7381b50
+use i3test i3_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+new_window pixel 5
+new_float normal
+EOT
+
+my $ws = fresh_workspace;
+
+my $float_window = open_floating_window;
+
+my @floating = @{get_ws($ws)->{floating_nodes}};
+
+is($floating[0]->{nodes}[0]->{border}, 'normal', 'default floating border is `normal`');
+
+done_testing;
diff --git a/testcases/t/280-wm-class-change-handler.t b/testcases/t/280-wm-class-change-handler.t
new file mode 100644 (file)
index 0000000..38b8351
--- /dev/null
@@ -0,0 +1,72 @@
+#!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_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+for_window [class="Special"] mark special_class_mark
+EOT
+use X11::XCB qw(PROP_MODE_REPLACE);
+
+sub change_window_class {
+    my ($window, $class, $length) = @_;
+    my $atomname = $x->atom(name => 'WM_CLASS');
+    my $atomtype = $x->atom(name => 'STRING');
+    $length ||= length($class) + 1;
+    $x->change_property(
+        PROP_MODE_REPLACE,
+        $window->id,
+        $atomname->id,
+        $atomtype->id,
+        8,
+        $length,
+        $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_deeply($con->{marks}, [ 'special_class_mark' ],
+    'A `for_window` assignment should run for a match when the window changes class');
+
+change_window_class($win, "abcdefghijklmnopqrstuv\0abcd", 24);
+
+$con = @{get_ws_content($ws)}[0];
+
+is($con->{window_properties}->{class}, 'a',
+    'Non-null-terminated strings should be handled correctly');
+
+done_testing;
diff --git a/testcases/t/281-regress-reload-bindsym.t b/testcases/t/281-regress-reload-bindsym.t
new file mode 100644 (file)
index 0000000..ad81244
--- /dev/null
@@ -0,0 +1,36 @@
+#!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 the binding event works properly
+# Ticket: #1210
+use i3test i3_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+bindsym r reload
+EOT
+
+SKIP: {
+    qx(which xdotool 2> /dev/null);
+
+    skip 'xdotool is required to test the binding event. `[apt-get install|pacman -S] xdotool`', 1 if $?;
+
+    qx(xdotool key r);
+
+    does_i3_live;
+
+}
+done_testing;
diff --git a/testcases/t/282-tabbed-floating-disable-crash.t b/testcases/t/282-tabbed-floating-disable-crash.t
new file mode 100644 (file)
index 0000000..b4f1a4c
--- /dev/null
@@ -0,0 +1,38 @@
+#!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)
+#
+# Verifies that i3 does not crash when floating and then unfloating an
+# unfocused window within a tabbed container.
+# Ticket: #1484
+# Bug still in: 4.9.1-124-g856e1f9
+use i3test i3_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+workspace_layout tabbed
+EOT
+
+open_window;
+open_window;
+
+# Mark the second window, then focus the workspace.
+cmd 'mark foo, focus parent, focus parent';
+
+# Float and unfloat the marked window (without it being focused).
+cmd '[con_mark=foo] floating enable, floating disable';
+
+does_i3_live;
+
+done_testing;
diff --git a/testcases/t/283-net-wm-state-hidden.t b/testcases/t/283-net-wm-state-hidden.t
new file mode 100644 (file)
index 0000000..3f2301c
--- /dev/null
@@ -0,0 +1,201 @@
+#!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 for setting and removing the _NET_WM_STATE_HIDDEN atom properly.
+# Ticket: #1648
+use i3test;
+use X11::XCB qw(:all);
+
+sub is_hidden {
+    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});
+    for (my $i = 0; $i < $len; $i++) {
+        return 1 if $atoms[$i] == $atom->id;
+    }
+
+    return 0;
+}
+
+my ($tabA, $tabB, $tabC, $subtabA, $subtabB, $windowA, $windowB);
+
+###############################################################################
+# Given two containers next to each other, when focusing one, then the other
+# one does not have _NET_WM_STATE_HIDDEN set.
+###############################################################################
+
+fresh_workspace;
+$windowA = open_window;
+$windowB = open_window;
+
+ok(!is_hidden($windowA), 'left window does not have _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($windowB), 'right window does not have _NET_WM_STATE_HIDDEN set');
+
+###############################################################################
+# Given two containers on different workspaces, when one is focused, then
+# the other one does not have _NET_WM_STATE_HIDDEN set.
+###############################################################################
+
+fresh_workspace;
+$windowA = open_window;
+fresh_workspace;
+$windowB = open_window;
+
+ok(!is_hidden($windowA), 'left window does not have _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($windowB), 'right window does not have _NET_WM_STATE_HIDDEN set');
+
+###############################################################################
+# Given two containers in the same tabbed container, when one is focused, then
+# (only) the other one has _NET_WM_STATE_HIDDEN set.
+# Given the other tab is focused, then the atom is transferred.
+###############################################################################
+
+fresh_workspace;
+$tabA = open_window;
+cmd 'layout tabbed';
+$tabB = open_window;
+
+ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($tabB), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
+
+cmd 'focus left';
+
+ok(!is_hidden($tabA), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
+ok(is_hidden($tabB), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
+
+###############################################################################
+# Given three containers in the same stacked container, when the focused tab
+# is moved to another workspace, then the now focused tab does not have
+# _NET_WM_STATE_HIDDEN set anymore.
+###############################################################################
+
+fresh_workspace;
+$tabA = open_window;
+cmd 'layout stacked';
+$tabB = open_window;
+$tabC = open_window;
+cmd 'move window to workspace unused';
+
+ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($tabB), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($tabC), 'moved window does not have _NET_WM_STATE_HIDDEN set');
+
+###############################################################################
+# Given three containers in the same stacked container, when a not focused
+# tab is moved to another workspace, then it does not have _NET_WM_STATE_HIDDEN
+# set anymore.
+###############################################################################
+
+fresh_workspace;
+$tabA = open_window;
+cmd 'layout stacked';
+$tabB = open_window;
+cmd 'mark moveme';
+$tabC = open_window;
+cmd '[con_mark="moveme"] move window to workspace unused';
+
+ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($tabB), 'moved window does not have _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($tabC), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
+
+###############################################################################
+# Given a tabbed container and some other container, when the latter is moved
+# into the tabbed container, then all other tabs have _NET_WM_STATE_HIDDEN
+# set.
+###############################################################################
+
+fresh_workspace;
+$tabA = open_window;
+cmd 'layout tabbed';
+$tabB = open_window;
+cmd 'focus parent';
+cmd 'split h';
+$tabC = open_window;
+cmd 'move left';
+
+ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
+ok(is_hidden($tabB), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($tabC), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
+
+###############################################################################
+# Given a stacked container nested inside another tabbed container with the
+# inner one being in the currently focused tab, then the focused tab of the
+# inner container does not have _NET_WM_STATE_HIDDEN set.
+###############################################################################
+
+fresh_workspace;
+$tabA = open_window;
+cmd 'layout tabbed';
+$tabB = open_window;
+cmd 'split h';
+open_window;
+cmd 'split v';
+cmd 'layout stacked';
+$subtabA = open_window;
+$subtabB = open_window;
+
+ok(is_hidden($tabA), 'unfocused outer tab has _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($tabB), 'focused outer tab does not have _NET_WM_STATE_HIDDEN set');
+ok(is_hidden($subtabA), 'unfocused inner tab has _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($subtabB), 'focused inner tab does not have _NET_WM_STATE_HIDDEN set');
+
+cmd 'focus left';
+
+ok(!is_hidden($subtabB), 'focused inner tab does not have _NET_WM_STATE_HIDDEN set');
+
+###############################################################################
+# Given a stacked container nested inside another tabbed container with the
+# inner one being in a currently not focused tab, then all tabs of the inner
+# container have _NET_WM_STATE_HIDDEN set.
+###############################################################################
+
+fresh_workspace;
+$tabA = open_window;
+cmd 'layout tabbed';
+$tabB = open_window;
+cmd 'split h';
+open_window;
+cmd 'split v';
+cmd 'layout stacked';
+$subtabA = open_window;
+$subtabB = open_window;
+cmd 'focus parent';
+cmd 'focus parent';
+cmd 'focus left';
+
+ok(!is_hidden($tabA), 'focused outer tab does not have _NET_WM_STATE_HIDDEN set');
+ok(is_hidden($tabB), 'unfocused outer tab has _NET_WM_STATE_HIDDEN set');
+ok(is_hidden($subtabA), 'unfocused inner tab has _NET_WM_STATE_HIDDEN set');
+ok(is_hidden($subtabB), 'unfocused inner tab has _NET_WM_STATE_HIDDEN set');
+
+###############################################################################
+
+done_testing;
diff --git a/testcases/t/284-ewmh-visible-name.t b/testcases/t/284-ewmh-visible-name.t
new file mode 100644 (file)
index 0000000..c201b39
--- /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)
+#
+# Tests that _NET_WM_VISIBLE_NAME is set correctly.
+# Ticket: #1872
+use i3test;
+use X11::XCB qw(:all);
+
+my ($con);
+
+sub get_visible_name {
+    sync_with_i3;
+    my ($con) = @_;
+
+    my $cookie = $x->get_property(
+        0,
+        $con->{id},
+        $x->atom(name => '_NET_WM_VISIBLE_NAME')->id,
+        $x->atom(name => 'UTF8_STRING')->id,
+        0,
+        4096
+    );
+
+    my $reply = $x->get_property_reply($cookie->{sequence});
+    return undef if $reply->{value_len} == 0;
+    return $reply->{value};
+}
+
+###############################################################################
+# 1: _NET_WM_VISIBLE_NAME is set when the title format of a window is changed.
+###############################################################################
+
+fresh_workspace;
+$con = open_window(name => 'boring title');
+is(get_visible_name($con), undef, 'sanity check: initially no visible name is set');
+
+cmd 'title_format custom';
+is(get_visible_name($con), 'custom', 'the visible name is updated');
+
+cmd 'title_format "<s>%title</s>"';
+is(get_visible_name($con), '<s>boring title</s>', 'markup is returned as is');
+
+###############################################################################
+# 2: _NET_WM_VISIBLE_NAME is removed if not needed.
+###############################################################################
+
+fresh_workspace;
+$con = open_window(name => 'boring title');
+cmd 'title_format custom';
+is(get_visible_name($con), 'custom', 'sanity check: a visible name is set');
+
+cmd 'title_format %title';
+is(get_visible_name($con), undef, 'the visible name is removed again');
+
+###############################################################################
+
+done_testing;
diff --git a/testcases/t/285-sticky.t b/testcases/t/285-sticky.t
new file mode 100644 (file)
index 0000000..85fc11f
--- /dev/null
@@ -0,0 +1,113 @@
+#!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 sticky windows.
+# Ticket: #1455
+use i3test;
+
+my ($ws, $tmp, $focused);
+
+###############################################################################
+# 1: Given a sticky tiling container, when the workspace is switched, then
+#    nothing happens.
+###############################################################################
+fresh_workspace;
+open_window(wm_class => 'findme');
+cmd 'sticky enable';
+$ws = fresh_workspace;
+
+is(@{get_ws($ws)->{nodes}}, 0, 'tiling sticky container did not move');
+is(@{get_ws($ws)->{floating_nodes}}, 0, 'tiling sticky container did not move');
+cmd '[class="findme"] kill';
+
+###############################################################################
+# 2: Given a sticky floating container, when the workspace is switched, then
+#    the container moves to the new workspace.
+###############################################################################
+$ws = fresh_workspace;
+open_floating_window(wm_class => 'findme');
+$focused = get_focused($ws);
+cmd 'sticky enable';
+$ws = fresh_workspace;
+
+is(@{get_ws($ws)->{floating_nodes}}, 1, 'floating sticky container moved to new workspace');
+is(get_focused($ws), $focused, 'sticky container has focus');
+cmd '[class="findme"] kill';
+
+###############################################################################
+# 3: Given two sticky floating containers, when the workspace is switched,
+#    then both containers move to the new workspace.
+###############################################################################
+fresh_workspace;
+open_floating_window(wm_class => 'findme');
+cmd 'sticky enable';
+open_floating_window(wm_class => 'findme');
+cmd 'sticky enable';
+$ws = fresh_workspace;
+
+is(@{get_ws($ws)->{floating_nodes}}, 2, 'multiple sticky windows can be used at the same time');
+cmd '[class="findme"] kill';
+
+###############################################################################
+# 4: Given an unfocused sticky floating container and a tiling container on the
+#    target workspace, when the workspace is switched, then the tiling container
+#    is focused.
+###############################################################################
+$ws = fresh_workspace;
+open_window;
+$focused = get_focused($ws);
+fresh_workspace;
+open_floating_window(wm_class => 'findme');
+cmd 'sticky enable';
+open_window;
+cmd 'workspace ' . $ws;
+
+is(get_focused($ws), $focused, 'the tiling container has focus');
+cmd '[class="findme"] kill';
+
+###############################################################################
+# 5: Given a focused sticky floating container and a tiling container on the
+#    target workspace, when the workspace is switched, then the tiling container
+#    is focused.
+###############################################################################
+$ws = fresh_workspace;
+open_window;
+$tmp = fresh_workspace;
+open_floating_window(wm_class => 'findme');
+$focused = get_focused($tmp);
+cmd 'sticky enable';
+cmd 'workspace ' . $ws;
+
+is(get_focused($ws), $focused, 'the sticky container has focus');
+cmd '[class="findme"] kill';
+
+###############################################################################
+# 6: Given a floating container on a non-visible workspace, when the window
+#    is made sticky, then the window immediately jumps to the currently
+#    visible workspace.
+###############################################################################
+fresh_workspace;
+open_floating_window(wm_class => 'findme');
+cmd 'mark sticky';
+$ws = fresh_workspace;
+cmd '[con_mark=sticky] sticky enable';
+
+is(@{get_ws($ws)->{floating_nodes}}, 1, 'the sticky window jumps to the front');
+cmd '[class="findme"] kill';
+
+###############################################################################
+
+done_testing;
diff --git a/testcases/t/286-root-window-mouse-binding.t b/testcases/t/286-root-window-mouse-binding.t
new file mode 100644 (file)
index 0000000..3500477
--- /dev/null
@@ -0,0 +1,37 @@
+#!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)
+#
+# Verifies that mouse bindings work on the root window if
+# --whole-window is set.
+# Ticket: #2115
+use i3test i3_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+workspace_auto_back_and_forth no
+bindsym --whole-window button4 workspace special
+EOT
+use i3test::XTEST;
+
+fresh_workspace;
+
+xtest_button_press(4, 50, 50);
+xtest_button_release(4, 50, 50);
+sync_with_i3;
+
+is(focused_ws(), 'special', 'the binding was triggered');
+
+done_testing;
diff --git a/testcases/t/287-edge-borders.t b/testcases/t/287-edge-borders.t
new file mode 100644 (file)
index 0000000..6da1dac
--- /dev/null
@@ -0,0 +1,163 @@
+#!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 that the hide_edge_borders smart option works
+# Ticket: #2188
+
+use i3test i3_autostart => 0;
+
+####################################################################
+# 1: check that the borders are present on a floating windows
+#####################################################################
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+new_window pixel 2
+new_float pixel 2
+hide_edge_borders smart
+EOT
+
+my $pid = launch_with_config($config);
+
+my $tmp = fresh_workspace;
+
+ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
+
+my $floatwindow = open_floating_window;
+
+my $wscontent = get_ws($tmp);
+
+my @floating = @{$wscontent->{floating_nodes}};
+ok(@floating == 1, 'one floating container opened');
+is($floating[0]->{nodes}[0]->{current_border_width}, 2, 'floating current border width set to 2');
+is($floatwindow->rect->width, $floating[0]->{rect}->{width} - 2*2, 'floating border width 2');
+
+exit_gracefully($pid);
+
+#####################################################################
+# 2: check that the borders are present on a workspace with two tiled
+# windows visible
+#####################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+new_window pixel 2
+new_float pixel 2
+hide_edge_borders smart
+EOT
+
+$pid = launch_with_config($config);
+
+$tmp = fresh_workspace;
+
+ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
+
+my $tilewindow = open_window;
+my $tilewindow2 = open_window;
+
+$wscontent = get_ws($tmp);
+
+my @tiled = @{$wscontent->{nodes}};
+ok(@tiled == 2, 'two tiled container opened');
+is($tiled[0]->{current_border_width}, 2, 'first tiled current border width set to 2');
+is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*2, 'first tiled border width 2');
+is($tiled[1]->{current_border_width}, 2, 'second tiled current border width set to 2');
+is($tilewindow2->rect->width, $tiled[1]->{rect}->{width} - 2*2, 'second tiled border width 2');
+
+exit_gracefully($pid);
+
+#####################################################################
+# 3: check that the borders are hidden on a workspace with one tiled
+# window visible
+#####################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+new_window pixel 2
+new_float pixel 2
+hide_edge_borders smart
+EOT
+
+$pid = launch_with_config($config);
+
+$tmp = fresh_workspace;
+
+ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
+
+$tilewindow = open_window;
+
+$wscontent = get_ws($tmp);
+
+@tiled = @{$wscontent->{nodes}};
+ok(@tiled == 1, 'one tiled container opened');
+is($tiled[0]->{current_border_width}, 2, 'tiled current border width set to 2');
+is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*0, 'single tiled border width 0');
+
+exit_gracefully($pid);
+
+#####################################################################
+# 4: check that the borders are present on a workspace with two tiled
+# windows visible, recursively
+#####################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+new_window pixel 2
+new_float pixel 2
+hide_edge_borders smart
+EOT
+
+$pid = launch_with_config($config);
+
+$tmp = fresh_workspace;
+
+ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
+
+$tilewindow = open_window;
+$tilewindow2 = open_window;
+ok(@{get_ws_content($tmp)} == 2, 'two containers opened');
+
+cmd 'layout tabbed';
+ok(@{get_ws_content($tmp)} == 1, 'layout tabbed -> back to one container');
+
+cmd 'focus parent';
+my $tilewindow3 = open_window;
+ok(@{get_ws_content($tmp)} == 2, 'after split & new window, two containers');
+
+$wscontent = get_ws($tmp);
+
+# Ensure i3’s X11 requests are processed before our inquiry via
+# $tilewindow->rect:
+sync_with_i3;
+
+@tiled = @{$wscontent->{nodes}};
+ok(@tiled == 2, 'two tiled container opened in another container');
+is($tiled[0]->{current_border_width}, -1, 'first tiled current border width set to -1');
+is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*2, 'first tiled border width 2');
+is($tiled[1]->{current_border_width}, 2, 'second tiled current border width set to 2');
+is($tilewindow2->rect->width, $tiled[1]->{rect}->{width} - 2*2, 'second tiled border width 2');
+
+exit_gracefully($pid);
+
+done_testing;
diff --git a/testcases/t/288-i3-floating-window-atom.t b/testcases/t/288-i3-floating-window-atom.t
new file mode 100644 (file)
index 0000000..43b69cc
--- /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)
+#
+# Tests for our proprietary atom I3_FLOATING_WINDOW to allow
+# identifying floating windows.
+# Ticket: #2223
+use i3test;
+use X11::XCB qw(:all);
+
+my ($con);
+
+sub has_i3_floating_window {
+    sync_with_i3;
+
+    my ($con) = @_;
+    my $cookie = $x->get_property(
+        0,
+        $con->{id},
+        $x->atom(name => 'I3_FLOATING_WINDOW')->id,
+        $x->atom(name => 'CARDINAL')->id,
+        0,
+        1
+    );
+
+    my $reply = $x->get_property_reply($cookie->{sequence});
+    return 0 if $reply->{length} != 1;
+
+    return unpack("L", $reply->{value});
+}
+
+###############################################################################
+# Toggling floating on a container adds / removes I3_FLOATING_WINDOW.
+###############################################################################
+
+fresh_workspace;
+
+$con = open_window;
+is(has_i3_floating_window($con), 0, 'I3_FLOATING_WINDOW is not set');
+
+cmd 'floating enable';
+is(has_i3_floating_window($con), 1, 'I3_FLOATING_WINDOW is set');
+
+cmd 'floating disable';
+is(has_i3_floating_window($con), 0, 'I3_FLOATING_WINDOW is not set');
+
+###############################################################################
+# A window that is floated when managed has I3_FLOATING_WINDOW set.
+###############################################################################
+#
+fresh_workspace;
+
+$con = open_floating_window;
+is(has_i3_floating_window($con), 1, 'I3_FLOATING_WINDOW is set');
+
+###############################################################################
+
+done_testing;
diff --git a/testcases/t/289-ipc-shutdown-event.t b/testcases/t/289-ipc-shutdown-event.t
new file mode 100644 (file)
index 0000000..379b9bf
--- /dev/null
@@ -0,0 +1,71 @@
+#!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 the ipc shutdown event. This event is triggered when the connection to
+# the ipc is about to shutdown because of a user action such as with a
+# `restart` or `exit` command. The `change` field indicates why the ipc is
+# shutting down. It can be either "restart" or "exit".
+#
+# Ticket: #2318
+# Bug still in: 4.12-46-g2123888
+use i3test;
+
+SKIP: {
+    skip "AnyEvent::I3 too old (need >= 0.17)", 1 if $AnyEvent::I3::VERSION < 0.17;
+
+my $i3 = i3(get_socket_path());
+$i3->connect->recv;
+
+my $cv = AE::cv;
+my $timer = AE::timer 0.5, 0, sub { $cv->send(0); };
+
+$i3->subscribe({
+        shutdown => sub {
+            $cv->send(shift);
+        }
+    })->recv;
+
+cmd 'restart';
+
+my $e = $cv->recv;
+
+diag "Event:\n", Dumper($e);
+ok($e, 'the shutdown event should emit when the ipc is restarted by command');
+is($e->{change}, 'restart', 'the `change` field should tell the reason for the shutdown');
+
+# restarting kills the ipc client so we have to make a new one
+$i3 = i3(get_socket_path());
+$i3->connect->recv;
+
+$cv = AE::cv;
+$timer = AE::timer 0.5, 0, sub { $cv->send(0); };
+
+$i3->subscribe({
+        shutdown => sub {
+            $cv->send(shift);
+        }
+    })->recv;
+
+cmd 'exit';
+
+$e = $cv->recv;
+
+diag "Event:\n", Dumper($e);
+ok($e, 'the shutdown event should emit when the ipc is exited by command');
+is($e->{change}, 'exit', 'the `change` field should tell the reason for the shutdown');
+}
+
+done_testing;
diff --git a/testcases/t/290-keypress-numlock.t b/testcases/t/290-keypress-numlock.t
new file mode 100644 (file)
index 0000000..fcc39ea
--- /dev/null
@@ -0,0 +1,390 @@
+#!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)
+#
+# Verifies that one can bind on numpad keys in different numlock states.
+# Ticket: #2346
+# Bug still in: 4.12-78-g85bb324
+use i3test i3_autostart => 0;
+use i3test::XTEST;
+use ExtUtils::PkgConfig;
+
+SKIP: {
+    skip "libxcb-xkb too old (need >= 1.11)", 1 unless
+        ExtUtils::PkgConfig->atleast_version('xcb-xkb', '1.11');
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+# Same key, different numlock states.
+bindsym Mod2+KP_1 nop KP_1
+bindsym KP_End nop KP_End
+
+# Binding which should work with numlock and without.
+bindsym Mod4+a nop a
+
+# Binding which should work with numlock and without, see issue #2418.
+bindsym Escape nop Escape
+
+# Binding which should work with numlock and without, see issue #2418.
+bindsym Shift+Escape nop Shift+Escape
+
+# Binding which should work with numlock and without, see issue #2418.
+bindsym Mod1+Shift+q nop Mod1+Shift+q
+
+# Binding which should work with numlock and without, see issue #2559.
+bindcode 39 nop s
+EOT
+
+my $pid = launch_with_config($config);
+
+start_binding_capture;
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(87); # KP_End
+        xtest_key_release(87); # KP_End
+    },
+    ),
+   'KP_End',
+   'triggered the "KP_End" keybinding');
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(77); # enable Num_Lock
+        xtest_key_release(77); # enable Num_Lock
+        xtest_key_press(87); # KP_1
+        xtest_key_release(87); # KP_1
+        xtest_key_press(77); # disable Num_Lock
+        xtest_key_release(77); # disable Num_Lock
+    },
+    ),
+   'KP_1',
+   'triggered the "KP_1" keybinding');
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(133); # Super_L
+        xtest_key_press(38); # a
+        xtest_key_release(38); # a
+        xtest_key_release(133); # Super_L
+    },
+    ),
+   'a',
+   'triggered the "a" keybinding');
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(77); # enable Num_Lock
+        xtest_key_release(77); # enable Num_Lock
+        xtest_key_press(133); # Super_L
+        xtest_key_press(38); # a
+        xtest_key_release(38); # a
+        xtest_key_release(133); # Super_L
+        xtest_key_press(77); # disable Num_Lock
+        xtest_key_release(77); # disable Num_Lock
+    },
+    ),
+   'a',
+   'triggered the "a" keybinding');
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(9); # Escape
+        xtest_key_release(9); # Escape
+    },
+    ),
+   'Escape',
+   'triggered the "Escape" keybinding');
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(77); # enable Num_Lock
+        xtest_key_release(77); # enable Num_Lock
+        xtest_key_press(9); # Escape
+        xtest_key_release(9); # Escape
+        xtest_key_press(77); # disable Num_Lock
+        xtest_key_release(77); # disable Num_Lock
+    },
+    ),
+   'Escape',
+   'triggered the "Escape" keybinding');
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(50); # Shift_L
+        xtest_key_press(9); # Escape
+        xtest_key_release(9); # Escape
+        xtest_key_release(50); # Shift_L
+    },
+    ),
+   'Shift+Escape',
+   'triggered the "Escape" keybinding');
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(77); # enable Num_Lock
+        xtest_key_release(77); # enable Num_Lock
+        xtest_key_press(50); # Shift_L
+        xtest_key_press(9); # Escape
+        xtest_key_release(9); # Escape
+        xtest_key_release(50); # Shift_L
+        xtest_key_press(77); # disable Num_Lock
+        xtest_key_release(77); # disable Num_Lock
+    },
+    ),
+   'Shift+Escape',
+   'triggered the "Escape" keybinding');
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(50); # Shift_L
+        xtest_key_press(64); # Alt_L
+        xtest_key_press(24); # q
+        xtest_key_release(24); # q
+        xtest_key_release(64); # Alt_L
+        xtest_key_release(50); # Shift_L
+    },
+    ),
+   'Mod1+Shift+q',
+   'triggered the "Mod1+Shift+q" keybinding');
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(77); # enable Num_Lock
+        xtest_key_release(77); # enable Num_Lock
+        xtest_key_press(50); # Shift_L
+        xtest_key_press(64); # Alt_L
+        xtest_key_press(24); # q
+        xtest_key_release(24); # q
+        xtest_key_release(64); # Alt_L
+        xtest_key_release(50); # Shift_L
+        xtest_key_press(77); # disable Num_Lock
+        xtest_key_release(77); # disable Num_Lock
+    },
+    ),
+   'Mod1+Shift+q',
+   'triggered the "Mod1+Shift+q" keybinding');
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(39); # s
+        xtest_key_release(39); # s
+    },
+    ),
+   's',
+   'triggered the "s" keybinding without Num_Lock');
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(77); # enable Num_Lock
+        xtest_key_release(77); # enable Num_Lock
+        xtest_key_press(39); # s
+        xtest_key_release(39); # s
+        xtest_key_press(77); # disable Num_Lock
+        xtest_key_release(77); # disable Num_Lock
+    },
+    ),
+   's',
+   'triggered the "s" keybinding with Num_Lock');
+
+sync_with_i3;
+is(scalar @i3test::XTEST::binding_events, 12, 'Received exactly 12 binding events');
+
+exit_gracefully($pid);
+
+################################################################################
+# Verify bindings for modifiers work
+################################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+bindsym Mod4+Return nop Return
+
+# Binding which should work with numlock and without, see issue #2559.
+bindcode --release 133 nop Super_L
+EOT
+
+$pid = launch_with_config($config);
+
+start_binding_capture;
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(133); # Super_L
+        xtest_key_release(133); # Super_L
+    },
+    ),
+   'Super_L',
+   'triggered the "Super_L" keybinding without Num_Lock');
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(77); # enable Num_Lock
+        xtest_key_release(77); # enable Num_Lock
+        xtest_key_press(133); # Super_L
+        xtest_key_release(133); # Super_L
+        xtest_key_press(77); # disable Num_Lock
+        xtest_key_release(77); # disable Num_Lock
+    },
+    ),
+   'Super_L',
+   'triggered the "Super_L" keybinding with Num_Lock');
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(133); # Super_L
+        xtest_key_press(36); # Return
+        xtest_key_release(36); # Return
+        xtest_key_release(133); # Super_L
+    },
+    ),
+   'Return',
+   'triggered the "Return" keybinding without Num_Lock');
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(77); # enable Num_Lock
+        xtest_key_release(77); # enable Num_Lock
+        xtest_key_press(133); # Super_L
+        xtest_key_press(36); # Return
+        xtest_key_release(36); # Return
+        xtest_key_release(133); # Super_L
+        xtest_key_press(77); # disable Num_Lock
+        xtest_key_release(77); # disable Num_Lock
+    },
+    ),
+   'Return',
+   'triggered the "Return" keybinding with Num_Lock');
+
+sync_with_i3;
+is(scalar @i3test::XTEST::binding_events, 16, 'Received exactly 16 binding events');
+
+exit_gracefully($pid);
+
+################################################################################
+# Verify the binding is only triggered for KP_End, not KP_1
+################################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+bindsym KP_End nop KP_End
+bindcode 88 nop KP_Down
+EOT
+
+$pid = launch_with_config($config);
+
+start_binding_capture;
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(87); # KP_End
+        xtest_key_release(87); # KP_End
+    },
+    ),
+   'KP_End',
+   'triggered the "KP_End" keybinding');
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(88); # KP_Down
+        xtest_key_release(88); # KP_Down
+    },
+    ),
+   'KP_Down',
+   'triggered the "KP_Down" keybinding');
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(77); # enable Num_Lock
+        xtest_key_release(77); # enable Num_Lock
+        xtest_key_press(87); # KP_1
+        xtest_key_release(87); # KP_1
+        xtest_key_press(77); # disable Num_Lock
+        xtest_key_release(77); # disable Num_Lock
+    },
+    ),
+   'timeout',
+   'Did not trigger the KP_End keybinding with KP_1');
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(77); # enable Num_Lock
+        xtest_key_release(77); # enable Num_Lock
+        xtest_key_press(88); # KP_2
+        xtest_key_release(88); # KP_2
+        xtest_key_press(77); # disable Num_Lock
+        xtest_key_release(77); # disable Num_Lock
+    },
+    ),
+   'timeout',
+   'Did not trigger the KP_Down keybinding with KP_2');
+
+# TODO: This test does not verify that i3 does _NOT_ grab keycode 87 with Mod2.
+
+sync_with_i3;
+is(scalar @i3test::XTEST::binding_events, 18, 'Received exactly 18 binding events');
+
+exit_gracefully($pid);
+
+################################################################################
+# Verify mouse bindings are unaffected by NumLock
+################################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+bindsym --whole-window button4 nop button4
+EOT
+
+$pid = launch_with_config($config);
+
+my $win = open_window;
+
+start_binding_capture;
+
+is(listen_for_binding(
+    sub {
+        xtest_key_press(77); # enable Num_Lock
+        xtest_key_release(77); # enable Num_Lock
+        xtest_button_press(4, 50, 50);
+        xtest_button_release(4, 50, 50);
+        xtest_key_press(77); # disable Num_Lock
+        xtest_key_release(77); # disable Num_Lock
+    },
+    ),
+   'button4',
+   'triggered the button4 keybinding with NumLock');
+
+is(listen_for_binding(
+    sub {
+       xtest_button_press(4, 50, 50);
+       xtest_button_release(4, 50, 50);
+    },
+    ),
+   'button4',
+   'triggered the button4 keybinding without NumLock');
+
+exit_gracefully($pid);
+
+}
+
+done_testing;
diff --git a/testcases/t/291-swap.t b/testcases/t/291-swap.t
new file mode 100644 (file)
index 0000000..5393259
--- /dev/null
@@ -0,0 +1,423 @@
+#!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 the swap command.
+# Ticket: #917
+use i3test i3_config => <<EOT;
+# i3 config file (v4)
+font font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+for_window[class="mark_A"] mark A
+for_window[class="mark_B"] mark B
+EOT
+
+my ($ws, $ws1, $ws2, $ws3);
+my ($nodes, $expected_focus, $A, $B, $F);
+my ($result);
+my @urgent;
+
+###############################################################################
+# Invalid con_id should not crash i3
+# See issue #2895.
+###############################################################################
+
+$ws = fresh_workspace;
+
+open_window;
+cmd "swap container with con_id 1";
+
+does_i3_live;
+kill_all_windows;
+
+###############################################################################
+# Swap 2 windows in different workspaces using con_id
+###############################################################################
+
+$ws = fresh_workspace;
+open_window;
+$A = get_focused($ws);
+
+$ws = fresh_workspace;
+open_window;
+
+cmd "swap container with con_id $A";
+is(get_focused($ws), $A, 'A is now focused');
+
+kill_all_windows;
+
+###############################################################################
+# Swap two containers next to each other.
+# Focus should stay on B because both windows are on the focused workspace.
+# The focused container is B.
+#
+# +---+---+    Layout: H1[ A B ]
+# | A | B |    Focus Stacks:
+# +---+---+        H1: B, A
+###############################################################################
+$ws = fresh_workspace;
+
+$A = open_window(wm_class => 'mark_A');
+$B = open_window(wm_class => 'mark_B');
+$expected_focus = get_focused($ws);
+
+cmd '[con_mark=B] swap container with mark A';
+
+$nodes = get_ws_content($ws);
+is($nodes->[0]->{window}, $B->{id}, 'B is on the left');
+is($nodes->[1]->{window}, $A->{id}, 'A is on the right');
+is(get_focused($ws), $expected_focus, 'B is still focused');
+
+kill_all_windows;
+
+###############################################################################
+# Swap two containers with different parents.
+# In this test, the focus head of the left v-split container is A.
+# The focused container is B.
+#
+# +---+---+    Layout: H1[ V1[ A Y ] V2[ X B ] ]
+# | A | X |    Focus Stacks:
+# +---+---+        H1: V2, V1
+# | Y | B |        V1: A, Y
+# +---+---+        V2: B, X
+###############################################################################
+$ws = fresh_workspace;
+
+$A = open_window(wm_class => 'mark_A');
+$B = open_window(wm_class => 'mark_B');
+cmd 'split v';
+open_window;
+cmd 'move up, focus left';
+cmd 'split v';
+open_window;
+cmd 'focus up, focus right, focus down';
+$expected_focus = get_focused($ws);
+
+cmd '[con_mark=B] swap container with mark A';
+
+$nodes = get_ws_content($ws);
+is($nodes->[0]->{nodes}->[0]->{window}, $B->{id}, 'B is on the top left');
+is($nodes->[1]->{nodes}->[1]->{window}, $A->{id}, 'A is on the bottom right');
+is(get_focused($ws), $expected_focus, 'B is still focused');
+
+kill_all_windows;
+
+###############################################################################
+# Swap two containers with different parents.
+# In this test, the focus head of the left v-split container is _not_ A.
+# The focused container is B.
+#
+# +---+---+    Layout: H1[ V1[ A Y ] V2[ X B ] ]
+# | A | X |    Focus Stacks:
+# +---+---+        H1: V2, V1
+# | Y | B |        V1: Y, A
+# +---+---+        V2: B, X
+###############################################################################
+$ws = fresh_workspace;
+
+$A = open_window(wm_class => 'mark_A');
+$B = open_window(wm_class => 'mark_B');
+cmd 'split v';
+open_window;
+cmd 'move up, focus left';
+cmd 'split v';
+open_window;
+cmd 'focus right, focus down';
+$expected_focus = get_focused($ws);
+
+cmd '[con_mark=B] swap container with mark A';
+
+$nodes = get_ws_content($ws);
+is($nodes->[0]->{nodes}->[0]->{window}, $B->{id}, 'B is on the top left');
+is($nodes->[1]->{nodes}->[1]->{window}, $A->{id}, 'A is on the bottom right');
+is(get_focused($ws), $expected_focus, 'B is still focused');
+
+kill_all_windows;
+
+###############################################################################
+# Swap two containers with one being on a different workspace.
+# The focused container is B.
+#
+# Layout: O1[ W1[ H1 ] W2[ H2 ] ]
+# Focus Stacks:
+#     O1: W2, W1
+#
+# +---+---+    Layout: H1[ A X ]
+# | A | X |    Focus Stacks:
+# +---+---+        H1: A, X
+#
+# +---+---+    Layout: H2[ Y, B ]
+# | Y | B |    Focus Stacks:
+# +---+---+        H2: B, Y
+###############################################################################
+$ws1 = fresh_workspace;
+$A = open_window(wm_class => 'mark_A');
+$expected_focus = get_focused($ws1);
+open_window;
+cmd 'focus left';
+
+$ws2 = fresh_workspace;
+open_window;
+$B = open_window(wm_class => 'mark_B');
+
+cmd '[con_mark=B] swap container with mark A';
+
+$nodes = get_ws_content($ws1);
+is($nodes->[0]->{window}, $B->{id}, 'B is on ws1:left');
+
+$nodes = get_ws_content($ws2);
+is($nodes->[1]->{window}, $A->{id}, 'A is on ws2:right');
+is(get_focused($ws2), $expected_focus, 'A is focused');
+
+kill_all_windows;
+
+###############################################################################
+# Swap two non-focused containers within the same workspace.
+#
+# +---+---+    Layout: H1[ V1[ A X ] V2[ F B ] ]
+# | A | F |    Focus Stacks:
+# +---+---+        H1: V2, V1
+# | X | B |        V1: A, X
+# +---+---+        V2: F, B
+###############################################################################
+$ws = fresh_workspace;
+
+$A = open_window(wm_class => 'mark_A');
+$B = open_window(wm_class => 'mark_B');
+cmd 'split v';
+open_window;
+cmd 'move up, focus left';
+cmd 'split v';
+open_window;
+cmd 'focus up, focus right';
+$expected_focus = get_focused($ws);
+
+cmd '[con_mark=B] swap container with mark A';
+
+$nodes = get_ws_content($ws);
+is($nodes->[0]->{nodes}->[0]->{window}, $B->{id}, 'B is on the top left');
+is($nodes->[1]->{nodes}->[1]->{window}, $A->{id}, 'A is on the bottom right');
+is(get_focused($ws), $expected_focus, 'F is still focused');
+
+kill_all_windows;
+
+###############################################################################
+# Swap two non-focused containers which are both on different workspaces.
+#
+# Layout: O1[ W1[ A ] W2[ B ] W3[ F ] ]
+# Focus Stacks:
+#     O1: W3, W2, W1
+#
+# +---+
+# | A |
+# +---+
+#
+# +---+
+# | B |
+# +---+
+#
+# +---+
+# | F |
+# +---+
+###############################################################################
+$ws1 = fresh_workspace;
+$A = open_window(wm_class => 'mark_A');
+
+$ws2 = fresh_workspace;
+$B = open_window(wm_class => 'mark_B');
+
+$ws3 = fresh_workspace;
+open_window;
+$expected_focus = get_focused($ws3);
+
+cmd '[con_mark=B] swap container with mark A';
+
+$nodes = get_ws_content($ws1);
+is($nodes->[0]->{window}, $B->{id}, 'B is on the first workspace');
+
+$nodes = get_ws_content($ws2);
+is($nodes->[0]->{window}, $A->{id}, 'A is on the second workspace');
+
+is(get_focused($ws3), $expected_focus, 'F is still focused');
+
+kill_all_windows;
+
+###############################################################################
+# Swap two non-focused containers with one being on a different workspace.
+#
+# Layout: O1[ W1[ A ] W2[ H2 ] ]
+# Focus Stacks:
+#     O1: W2, W1
+#
+# +---+
+# | A |
+# +---+
+#
+# +---+---+    Layout: H2[ B, F ]
+# | B | F |    Focus Stacks:
+# +---+---+        H2: F, B
+###############################################################################
+
+$ws1 = fresh_workspace;
+$A = open_window(wm_class => 'mark_A');
+
+$ws2 = fresh_workspace;
+$B = open_window(wm_class => 'mark_B');
+open_window;
+$expected_focus = get_focused($ws2);
+
+cmd '[con_mark=B] swap container with mark A';
+
+$nodes = get_ws_content($ws1);
+is($nodes->[0]->{window}, $B->{id}, 'B is on the first workspace');
+
+$nodes = get_ws_content($ws2);
+is($nodes->[0]->{window}, $A->{id}, 'A is on the left of the second workspace');
+is(get_focused($ws2), $expected_focus, 'F is still focused');
+
+kill_all_windows;
+
+###############################################################################
+# 1. A container cannot be swapped with its parent.
+# 2. A container cannot be swapped with one of its children.
+#
+#      ↓A↓
+# +---+---+    Layout: H1[ X V1[ Y B ] ]
+# |   | Y |        (with A := V1)
+# | X +---+
+# |   | B |
+# +---+---+
+###############################################################################
+$ws = fresh_workspace;
+open_window;
+open_window;
+cmd 'split v';
+$B = open_window(wm_class => 'mark_B');
+cmd 'focus parent, mark A, focus child';
+
+$result = cmd '[con_mark=B] swap container with mark A';
+is($result->[0]->{success}, 0, 'B cannot be swappd with its parent');
+
+$result = cmd '[con_mark=A] swap container with mark B';
+is($result->[0]->{success}, 0, 'A cannot be swappd with one of its children');
+
+kill_all_windows;
+
+###############################################################################
+# Swapping two containers preserves the geometry of the container they are
+# being swapped with.
+#
+# Before:
+# +---+-------+
+# | A |   B   |
+# +---+-------+
+#
+# After:
+# +---+-------+
+# | B |   A   |
+# +---+-------+
+###############################################################################
+$ws = fresh_workspace;
+$A = open_window(wm_class => 'mark_A');
+$B = open_window(wm_class => 'mark_B');
+cmd 'resize grow width 0 or 25 ppt';
+
+# sanity checks
+$nodes = get_ws_content($ws);
+cmp_float($nodes->[0]->{percent}, 0.25, 'A has 25% width');
+cmp_float($nodes->[1]->{percent}, 0.75, 'B has 75% width');
+
+cmd '[con_mark=B] swap container with mark A';
+
+$nodes = get_ws_content($ws);
+cmp_float($nodes->[0]->{percent}, 0.25, 'B has 25% width');
+cmp_float($nodes->[1]->{percent}, 0.75, 'A has 75% width');
+
+kill_all_windows;
+
+###############################################################################
+# Swapping containers not sharing the same parent preserves the geometry of
+# the container they are swapped with.
+#
+# Before:
+# +---+-----+
+# | A |     |
+# +---+  B  |
+# |   |     |
+# | Y +-----+
+# |   |  X  |
+# +---+-----+
+#
+# After:
+# +---+-----+
+# | B |     |
+# +---+  A  |
+# |   |     |
+# | Y +-----+
+# |   |  X  |
+# +---+-----+
+###############################################################################
+$ws = fresh_workspace;
+
+$A = open_window(wm_class => 'mark_A');
+$B = open_window(wm_class => 'mark_B');
+cmd 'split v';
+open_window;
+cmd 'focus up, resize grow height 0 or 25 ppt';
+cmd 'focus left, split v';
+open_window;
+cmd 'resize grow height 0 or 25 ppt';
+
+# sanity checks
+$nodes = get_ws_content($ws);
+cmp_float($nodes->[0]->{nodes}->[0]->{percent}, 0.25, 'A has 25% height');
+cmp_float($nodes->[1]->{nodes}->[0]->{percent}, 0.75, 'B has 75% height');
+
+cmd '[con_mark=B] swap container with mark A';
+
+$nodes = get_ws_content($ws);
+cmp_float($nodes->[0]->{nodes}->[0]->{percent}, 0.25, 'B has 25% height');
+cmp_float($nodes->[1]->{nodes}->[0]->{percent}, 0.75, 'A has 75% height');
+
+kill_all_windows;
+
+###############################################################################
+# Swapping containers moves the urgency hint correctly.
+###############################################################################
+
+$ws1 = fresh_workspace;
+$A = open_window(wm_class => 'mark_A');
+$ws2 = fresh_workspace;
+$B = open_window(wm_class => 'mark_B');
+open_window;
+
+$B->add_hint('urgency');
+sync_with_i3;
+
+cmd '[con_mark=B] swap container with mark A';
+
+@urgent = grep { $_->{urgent} } @{get_ws_content($ws1)};
+is(@urgent, 1, 'B is marked urgent');
+is(get_ws($ws1)->{urgent}, 1, 'the first workspace is marked urgent');
+
+@urgent = grep { $_->{urgent} } @{get_ws_content($ws2)};
+is(@urgent, 0, 'A is not marked urgent');
+is(get_ws($ws2)->{urgent}, 0, 'the second workspace is not marked urgent');
+
+kill_all_windows;
+
+###############################################################################
+
+done_testing;
index 3df2ba3f779303015731028bed1141d5a367bc29..6b62df3cab4f3d49e66c640ad2185b2e6e5cd20c 100644 (file)
 # Tests that the provided X-Server to the t/5??-*.t tests is actually providing
 # multiple monitors.
 #
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
 fake-outputs 1024x768+0+0,1024x768+1024+0
 EOT
-my $pid = launch_with_config($config);
 
 my $i3 = i3(get_socket_path());
 
@@ -39,6 +36,5 @@ my @outputs = map { $_->{name} } @{$tree->{nodes}};
 is_deeply(\@outputs, [ '__i3', 'fake-0', 'fake-1' ],
           'multi-monitor outputs ok');
 
-exit_gracefully($pid);
 
 done_testing;
index 0f9b0df012730f31f8293207c652a89068934e80..5aad2504fad80ff6e166b4ff56dbbe8d67abdf88 100644 (file)
 # ticket #596, bug present until up to commit
 # 89dded044b4fffe78f9d70778748fabb7ac533e9.
 #
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
 fake-outputs 1024x768+0+0,1024x768+1024+0
 EOT
-my $pid = launch_with_config($config);
-
-my $i3 = i3(get_socket_path());
 
 ################################################################################
 # Open a workspace on the second output, put a window to scratchpad, display
@@ -113,6 +108,4 @@ $second = fresh_workspace(output => 0);
 
 verify_scratchpad_switch($first, $second);
 
-exit_gracefully($pid);
-
 done_testing;
index cf297f0e2e72ee6ed8df94816deb3664de75156b..4b6fac401b03e290f619369d7175fb1547eb22dc 100644 (file)
 #
 # Verifies the 'focus output' command works properly.
 
-use i3test i3_autostart => 0;
-use List::Util qw(first);
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
 fake-outputs 1024x768+0+0,1024x768+1024+0
 EOT
-my $pid = launch_with_config($config);
+use List::Util qw(first);
 
 my $tmp = fresh_workspace;
 my $i3 = i3(get_socket_path());
@@ -89,6 +86,4 @@ is(focused_output, 'fake-1', 'focus on second output');
 cmd 'focus output fake-0';
 is(focused_output, 'fake-0', 'focus on first output');
 
-exit_gracefully($pid);
-
 done_testing;
index 7fe7f15b237953c7c6af3149260973bb3895af1b..62f15394343d8adbdc47f74ea21d360e14c2386b 100644 (file)
 # Tests whether 'workspace next_on_output' and the like work correctly.
 #
 use List::Util qw(first);
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
 fake-outputs 1024x768+0+0,1024x768+1024+0
 EOT
-my $pid = launch_with_config($config);
 
 ################################################################################
 # Setup workspaces so that they stay open (with an empty container).
@@ -100,6 +97,4 @@ sync_with_i3;
 cmd 'workspace prev_on_output';
 is(focused_ws, '2', 'workspace 2 focused');
 
-exit_gracefully($pid);
-
 done_testing;
index c43b8b407a39523336e0cacbb821e4508e699b75..c2d4bc9e9f9ac07cc2667e1dd83877828f8d77df 100644 (file)
 # Tests whether the 'move workspace <ws> to [output] <output>' command works
 #
 use List::Util qw(first);
-use i3test i3_autostart => 0;
-
-# Ensure the pointer is at (0, 0) so that we really start on the first
-# (the left) workspace.
-$x->root->warp_pointer(0, 0);
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
 fake-outputs 1024x768+0+0,1024x768+1024+0
+
+bar {
+    # Disable i3bar.
+    i3bar_command :
+}
 EOT
-my $pid = launch_with_config($config);
+
+# TODO: get rid of smartmatch in this test
 
 ################################################################################
 # Setup workspaces so that they stay open (with an empty container).
@@ -181,5 +181,4 @@ ok($ws1 ~~ @$x0, 'ws1 on fake-0');
 
 ################################################################################
 
-exit_gracefully($pid);
 done_testing;
index 2235e802fec5354c7e61d335ae8da188190d6c10..e2dff9dbff84957269caddb801b0ef827b466ec1 100644 (file)
 # Verifies that scratchpad windows don’t move due to floating point caulcation
 # errors when repeatedly hiding/showing, no matter what display resolution.
 #
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
 fake-outputs 683x768+0+0,1024x768+683+0
 EOT
-my $pid = launch_with_config($config);
-
-my $i3 = i3(get_socket_path());
 
 sync_with_i3;
 $x->root->warp_pointer(0, 0);
@@ -84,6 +79,4 @@ sync_with_i3;
 my $third = fresh_workspace(output => 1);
 verify_scratchpad_doesnt_move($third);
 
-exit_gracefully($pid);
-
 done_testing;
index 9e80553b2587bd83f51d06bc537df33c06148afd..ed5a90052d7bb63b46779efb306577715a67f6d5 100644 (file)
 # Bug still in: 4.3-78-g66b389c
 #
 use List::Util qw(first);
-use i3test i3_autostart => 0;
-
-# Ensure the pointer is at (0, 0) so that we really start on the first
-# (the left) workspace.
-$x->root->warp_pointer(0, 0);
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
 fake-outputs 1024x768+0+0,1024x768+1024+0
 EOT
-my $pid = launch_with_config($config);
 
 ################################################################################
 # Setup workspaces so that they stay open (with an empty container).
@@ -43,6 +36,4 @@ cmd 'move workspace to output fake-1';
 
 does_i3_live;
 
-exit_gracefully($pid);
-
 done_testing;
index d5cd3800a2ea80f1ee6493d1d1ce91f80b0eaa28..e8b8cbcd8f37a78b8bda6c52778c8858fa672072 100644 (file)
 # Tests whether workspace_layout is properly set after startup.
 #
 use List::Util qw(first);
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
 fake-outputs 1024x768+0+0
 workspace_layout tabbed
 EOT
-my $pid = launch_with_config($config);
 
 ################################################################################
 # Test that workspace_layout is properly set
 ################################################################################
 
-
 is(focused_ws, '1', 'starting on workspace 1');
 my $ws = get_ws(1);
 is($ws->{workspace_layout}, 'tabbed', 'workspace layout is "tabbed"');
 
-
-exit_gracefully($pid);
-
 done_testing;
index afa0ddef77f3a925eecab1aa70b4816f7288468b..8097f73f0fe7f1bd502a9dbce00f21d3ac8253a7 100644 (file)
 # Tests that switching workspaces via 'focus $dir' never leaves a floating
 # window focused.
 #
-use i3test i3_autostart => 0;
-
-# Ensure the pointer is at (0, 0) so that we really start on the first
-# (the left) workspace.
-$x->root->warp_pointer(0, 0);
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
 fake-outputs 1024x768+0+0,1024x768+1024+0,1024x768+0+768,1024x768+1024+768
 EOT
-my $pid = launch_with_config($config);
 
 my $s0_ws = fresh_workspace;
 my $first = open_window;
@@ -144,6 +137,4 @@ cmd "workspace $s2_ws";
 cmd 'focus up';
 is($x->input_focus, $second->id, 'second window focused');
 
-exit_gracefully($pid);
-
 done_testing;
index 561d24354d0ec396441e2589f466504a24db2b5b..851afb4afa6d1b790a852529d5934dac7f26b65e 100644 (file)
 # Tests that ConfigureRequests don’t make windows fall out of the scratchpad.
 # Ticket: #898
 # Bug still in: 4.4-15-g770ead6
-use i3test i3_autostart => 0;
-
-# Ensure the pointer is at (0, 0) so that we really start on the first
-# (the left) workspace.
-$x->root->warp_pointer(0, 0);
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
 fake-outputs 1024x768+0+0,1024x768+1024+0
 EOT
 
-my $pid = launch_with_config($config);
-
 my $left_ws = fresh_workspace(output => 0);
 my $right_ws = fresh_workspace(output => 1);
 
@@ -49,6 +41,4 @@ is(scalar @{$ws->{floating_nodes}}, 0, 'scratchpad window still in scratchpad af
 $ws = get_ws($right_ws);
 is(scalar @{$ws->{floating_nodes}}, 0, 'scratchpad window still in scratchpad after ConfigureRequest');
 
-exit_gracefully($pid);
-
 done_testing;
index 82d5a7ccf6355fec34a45d57fae0362c44e9cede..2c5a27af98c37af3f7d5735d34715b5e079c0d60 100644 (file)
 # E.g. when you have a container on the right output and you move it to the
 # right, it should appear on the left output.
 # Bug still in: 4.4-106-g3cd4b8c
-use i3test i3_autostart => 0;
-
-# Ensure the pointer is at (0, 0) so that we really start on the first
-# (the left) workspace.
-$x->root->warp_pointer(0, 0);
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
 fake-outputs 1024x768+0+0,1024x768+1024+0
 EOT
 
-my $pid = launch_with_config($config);
-
 my $right = fresh_workspace(output => 1);
 my $left = fresh_workspace(output => 0);
 
@@ -51,6 +43,4 @@ cmd 'move container to output right';
 is_num_children($left, 1, 'one container on left workspace');
 is_num_children($right, 0, 'no containers on right workspace');
 
-exit_gracefully($pid);
-
 done_testing;
index 3e27a6c09f070560a26a9902de592ee559f53cb7..fb9f0e256d522ce85256faaf42846cf590e42fce 100644 (file)
 #   (unless you are already familiar with Perl)
 #
 # Tests whether moving workspaces between outputs works correctly.
-use i3test i3_autostart => 0;
-use List::Util qw(first);
-
-# Ensure the pointer is at (0, 0) so that we really start on the first
-# (the left) workspace.
-$x->root->warp_pointer(0, 0);
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
 fake-outputs 1024x768+0+0,1024x768+1024+0
 EOT
-
-my $pid = launch_with_config($config);
+use List::Util qw(first);
 
 sub workspaces_per_screen {
     my $i3 = i3(get_socket_path());
@@ -92,6 +84,4 @@ cmd '[id="' . $win1->id . '"] focus';
 ($nodes, $focus) = get_ws_content('5');
 is($nodes->[1]->{window}, $win1->id, 'window 1 on workspace 5 after moving');
 
-exit_gracefully($pid);
-
 done_testing;
index 61622ab080f1b61415ec0b69a36f43b5e79aee1b..f34ee2330ec3de771c43d30664e8f85f80b7a964 100644 (file)
 # Ticket: #990
 # Bug still in: 4.5.1-23-g82b5978
 
-use i3test i3_autostart => 0;
-
-# Ensure the pointer is at (0, 0) so that we really start on the first
-# (the left) workspace.
-$x->root->warp_pointer(0, 0);
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 fake-outputs 1024x768+0+0,1024x768+1024+0
 EOT
 
-my $pid = launch_with_config($config);
-
 my $i3 = i3(get_socket_path());
 
 $i3->connect()->recv;
@@ -73,6 +65,4 @@ ok($event, 'Workspace "focus" event received');
 is($event->{current}->{id}, $current_ws->{id}, 'Event gave correct current workspace');
 is($event->{old}->{id}, $old_ws->{id}, 'Event gave correct old workspace');
 
-exit_gracefully($pid);
-
 done_testing;
index be790bf0ebaa708b7df3e7b40968321b8e4c7e4f..aad0693f04d18ff6109ba26f9490ad3d4ee6d205 100644 (file)
@@ -17,9 +17,7 @@
 # Tests that new workspace names are taken from the config,
 # then from the first free number starting with 1.
 #
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
@@ -27,7 +25,6 @@ fake-outputs 1024x768+0+0,1024x768+1024+0
 
 bindsym 1 workspace 1: eggs
 EOT
-my $pid = launch_with_config($config);
 
 my $i3 = i3(get_socket_path());
 my $ws = $i3->get_workspaces->recv;
@@ -35,6 +32,4 @@ my $ws = $i3->get_workspaces->recv;
 is($ws->[0]->{name}, '1: eggs', 'new workspace uses config name');
 is($ws->[1]->{name}, '2', 'naming continues with next free number');
 
-exit_gracefully($pid);
-
 done_testing;
index 0d21ca3140bfe520873a9bab36593797ca291505..3a4fa2ead3cdcfea08b67cced4e45a5ef4473594 100644 (file)
 #
 # Tests if a simple 'move <direction>' command will move containers across outputs.
 #
-use i3test i3_autostart => 0;
-
-# Ensure the pointer is at (0, 0) so that we really start on the first
-# (the left) workspace.
-$x->root->warp_pointer(0, 0);
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
@@ -34,7 +28,9 @@ workspace right-bottom output fake-2
 workspace left-bottom output fake-3
 EOT
 
-my $pid = launch_with_config($config);
+# Ensure the pointer is at (0, 0) so that we really start on the first
+# (the left) workspace.
+$x->root->warp_pointer(0, 0);
 
 #####################################################################
 # Try to move a single window across outputs in each direction
@@ -101,6 +97,4 @@ is(scalar @{get_ws_content('left-top')}, 1, 'moved some window to left-bottom wo
 $compare_window = shift @{get_ws_content('left-top')};
 is($social_window->name, $compare_window->{name}, 'moved correct window to left-bottom workspace');
 
-exit_gracefully($pid);
-
 done_testing;
index 383b27796fd381a67c62ab65d8194ef590229cff..1a325ae764b45695686bb5ffbb0fd41033757906 100644 (file)
@@ -18,9 +18,7 @@
 # ipc event required for i3bar to be properly updated and redrawn.
 #
 # Bug still in: 4.6-195-g34232b8
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
@@ -29,8 +27,6 @@ workspace ws-left output fake-0
 workspace ws-right output fake-1
 EOT
 
-my $pid = launch_with_config($config);
-
 my $i3 = i3(get_socket_path());
 $i3->connect()->recv;
 
@@ -78,6 +74,4 @@ ok($event, 'moving from workspace with one window triggered focus ipc event');
 is($event->{current}->{name}, 'ws-right', 'focus event gave the right workspace');
 is(@{$event->{current}->{nodes}}, 2, 'focus event gave the right number of windows on the workspace');
 
-exit_gracefully($pid);
-
 done_testing;
index 577881f06e813f91b88bf92c3c5c79b7fe1a1f34..aa2e2a2701b55a5553d149afd3e74b2a959bf251 100644 (file)
@@ -18,9 +18,7 @@
 # assign any workspace of that number to the specified output.
 # Ticket: #1238
 # Bug still in: 4.7.2-147-g3760a48
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
@@ -32,7 +30,6 @@ workspace 2:override output fake-1
 fake-outputs 1024x768+0+0,1024x768+1024+0
 EOT
 
-my $pid = launch_with_config($config);
 my $i3 = i3(get_socket_path());
 $i3->connect->recv;
 
@@ -72,6 +69,4 @@ is(get_output_for_workspace('1:override'), 'fake-0',
     'Assignment rules should not be affected by the order assignments are declared')
     or diag 'Since workspace "1:override" is assigned by name to fake-0, it should open on fake-0';
 
-exit_gracefully($pid);
-
 done_testing;
index 674f4cdbc8fcdf8610734dc2eb6d2187f6956c22..5b7e2c179c659c58f7c6a1d334f92b274747a2de 100644 (file)
 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
 #   (unless you are already familiar with Perl)
 
-use i3test i3_autostart => 0;
-
-# Ensure the pointer is at (0, 0) so that we really start on the first
-# (the left) workspace.
-$x->root->warp_pointer(0, 0);
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 fake-outputs 1024x768+0+0,1024x768+1024+0
 mouse_warping none
 EOT
-my $pid = launch_with_config($config);
 
-my $i3 = i3(get_socket_path());
+# Ensure the pointer is at (0, 0) so that we really start on the first
+# (the left) workspace.
+$x->root->warp_pointer(0, 0);
 
 ######################################################
 # Open one workspace with one window on both outputs #
@@ -47,6 +42,4 @@ $x->root->warp_pointer(0, 0);
 # Ensure focus is still on workspace 2
 is(focused_ws, '2', 'warped mouse cursor to (0, 0), focus still in workspace 2');
 
-# Exit gracefully
-exit_gracefully($pid);
 done_testing;
index ccef49e7e2d3d4806906bd761a1a0a58e6d9edb1..871f80450be581caa7d61460001f998bf8554259 100644 (file)
@@ -17,9 +17,7 @@
 # Ensure that `focus [direction]` will focus an existing floating con when no
 # tiling con exists on the output in [direction] when focusing across outputs
 # Bug still in: 4.7.2-204-g893dbae
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
@@ -31,8 +29,6 @@ mouse_warping none
 fake-outputs 1024x768+0+0,1024x768+1024+0
 EOT
 
-my $pid = launch_with_config($config);
-
 cmd 'workspace ws_left';
 my $win = open_window();
 
@@ -43,6 +39,4 @@ cmd 'focus left';
 is($x->input_focus, $win->id,
     'Focusing across outputs with `focus [direction]` should focus an existing floating con when no tiling con exists on the output in [direction].');
 
-exit_gracefully($pid);
-
 done_testing;
index 9e36090ca50d9b8b37c5e21e973ff75c9455dafc..273547693a388b28230663891cdaf07a3e71cdab 100644 (file)
@@ -18,9 +18,7 @@
 # properly on the root window. We interpret this as a list of x/y coordinate
 # pairs for the upper left corner of the respective outputs of the workspaces
 # Ticket: #1241
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
@@ -30,8 +28,6 @@ workspace 1 output fake-1
 fake-outputs 1024x768+0+0,1024x768+1024+0
 EOT
 
-my $pid = launch_with_config($config);
-
 sub get_desktop_viewport {
     # Make sure that i3 pushed its changes to X11 before querying.
     sync_with_i3;
@@ -90,6 +86,4 @@ cmd 'workspace 0';
 is_deeply(\@desktop_viewport, \@expected_viewport,
     'it should be updated when a workspace is emptied');
 
-exit_gracefully($pid);
-
 done_testing;
index aa69d66d8c8a5ea92396bb63493e6920757c34d7..a2e8ea5cb2284af2ed3826e7333ed098da7f5f75 100644 (file)
@@ -19,9 +19,7 @@
 # are renamed to an assigned name.
 # Ticket: #1473
 
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 fake-outputs 1024x768+0+0,1024x768+1024+0
@@ -32,7 +30,6 @@ workspace 3:foo output fake-1
 workspace baz output fake-1
 EOT
 
-my $pid = launch_with_config($config);
 my $i3 = i3(get_socket_path());
 $i3->connect->recv;
 
@@ -85,6 +82,4 @@ cmd 'rename workspace to baz';
 is(get_output_for_workspace('baz'), 'fake-1',
     'Renaming the workspace to a number and name should move it to the assigned output');
 
-
-exit_gracefully($pid);
 done_testing;
index 6b58424571b7e9f5454e9f8ced1fc64377e1f363..f0a8a8166547966759087a92249d1aa40306d865 100644 (file)
@@ -18,9 +18,7 @@
 # the appropriate output.
 # Ticket: #1211
 # Bug still in: 4.9.1-108-g037cb31
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
@@ -30,8 +28,6 @@ workspace left output fake-0
 workspace right output fake-1
 EOT
 
-my $pid = launch_with_config($config);
-
 #####################################################################
 # Verify that 'move position center' on a floating window does not
 # move it to another output.
@@ -58,6 +54,4 @@ sync_with_i3;
 is(scalar @{get_ws('left')->{floating_nodes}}, 0, 'no floating nodes on left ws');
 is(scalar @{get_ws('right')->{floating_nodes}}, 1, 'one floating node on right ws');
 
-exit_gracefully($pid);
-
 done_testing;
index 473bf235af12c310151f7a84ef66c736ffd69c0f..8cc20bc0d3911ffbca62e390986057e0288aebd6 100644 (file)
@@ -19,9 +19,7 @@
 # Ticket: #1603
 # Bug still in: 4.10.1-40-g0ad097e
 use List::Util qw(first);
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
@@ -35,8 +33,6 @@ workspace left-bottom output fake-3
 workspace_layout stacked
 EOT
 
-my $pid = launch_with_config($config);
-
 #####################################################################
 # Create two windows in the upper left workspace and move them
 # clockwise around the workspaces until the end up where they began.
@@ -94,6 +90,4 @@ cmd '[class="first"] move up';
 is_num_children('left-bottom', 0, 'no children on left-bottom');
 is_num_children('left-top', 2, 'two children on left-top');
 
-exit_gracefully($pid);
-
 done_testing;
index 39690291aedd0fbe1d02435c6ef6218ce3cdf94b..d5de2534194a88087190c92f51220ada87abbe12 100644 (file)
 #
 # Ensures that mouse bindings on the i3bar work correctly.
 # Ticket: #1695
-use i3test i3_autostart => 0;
-use i3test::XTEST;
-
-my ($cv, $timer);
-sub reset_test {
-    $cv = AE::cv;
-    $timer = AE::timer(1, 0, sub { $cv->send(0); });
-}
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 focus_follows_mouse no
@@ -41,8 +32,14 @@ bar {
     bindsym button5 focus left
 }
 EOT
+use i3test::XTEST;
+
+my ($cv, $timer);
+sub reset_test {
+    $cv = AE::cv;
+    $timer = AE::timer(1, 0, sub { $cv->send(0); });
+}
 
-my $pid = launch_with_config($config);
 my $i3 = i3(get_socket_path());
 $i3->connect()->recv;
 my $ws = fresh_workspace;
@@ -63,8 +60,32 @@ $i3->subscribe({
         },
     })->recv;
 
-my $con = $cv->recv;
-ok($con, 'i3bar appeared');
+my $con;
+
+sub i3bar_present {
+    my ($nodes) = @_;
+
+    for my $node (@{$nodes}) {
+       my $props = $node->{window_properties};
+       if (defined($props) && $props->{class} eq 'i3bar') {
+           return 1;
+       }
+    }
+
+    return 0 if !@{$nodes};
+
+    my @children = (map { @{$_->{nodes}} } @{$nodes},
+                    map { @{$_->{'floating_nodes'}} } @{$nodes});
+
+    return i3bar_present(\@children);
+}
+
+if (i3bar_present($i3->get_tree->recv->{nodes})) {
+    ok(1, 'i3bar present');
+} else {
+    $con = $cv->recv;
+    ok($con, 'i3bar appeared');
+}
 
 my $left = open_window;
 my $right = open_window;
@@ -108,6 +129,4 @@ $con = $cv->recv;
 is($con->{window}, $left->{id}, 'button 5 moves focus left');
 reset_test;
 
-exit_gracefully($pid);
-
 done_testing;
index 9e3ed3f280875fad876f2d54b85fee86b4ee483e..234fd1da7e014dc650340c0b1586d9fc7dfa2e06 100644 (file)
 #
 # Test reconfiguration of dock clients.
 # Ticket: #1883
-use i3test i3_autostart => 0;
+use i3test i3_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+fake-outputs 1024x768+0+0,1024x768+1024+0
 
-my ($config, $pid, $window, $rect);
+bar {
+    # Disable i3bar, which is also a dock client.
+    i3bar_command :
+}
+EOT
+
+my ($window, $rect);
 my (@docks);
 
 ###############################################################################
@@ -27,14 +37,6 @@ my (@docks);
 #    moved to the correct position.
 ###############################################################################
 
-$config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-fake-outputs 1024x768+0+0,1024x768+1024+0
-EOT
-$pid = launch_with_config($config);
-
 $window = open_window({
         window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK')
     });
@@ -50,8 +52,6 @@ is(@docks, 1, 'there is still exactly one dock');
 
 is($docks[0]->{rect}->{x}, 1024, 'dock client has moved to the other screen');
 
-exit_gracefully($pid);
-
 ###############################################################################
 
 done_testing;
index 00a9bbe4e77b498ceb3622835063ed626ef3694c..b10addecf67858e568ca16d7c1d58b7dbe04813a 100644 (file)
 # Tests whether 'workspace next' works correctly.
 #
 use List::Util qw(first);
-use i3test i3_autostart => 0;
+use i3test i3_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+fake-outputs 1024x768+0+0,1024x768+1024+0
+EOT
 
 sub assert_next {
     my ($expected) = @_;
@@ -43,14 +48,6 @@ sub assert_prev {
     is(focused_ws, $expected, "workspace $expected focused");
 }
 
-my $config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-fake-outputs 1024x768+0+0,1024x768+1024+0
-EOT
-my $pid = launch_with_config($config);
-
 sync_with_i3;
 $x->root->warp_pointer(0, 0);
 sync_with_i3;
@@ -124,6 +121,4 @@ assert_prev('2');
 assert_prev('1');
 
 
-exit_gracefully($pid);
-
 done_testing;
diff --git a/testcases/t/528-workspace-next-prev.t b/testcases/t/528-workspace-next-prev.t
deleted file mode 100644 (file)
index 1a83de2..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-#!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 whether 'workspace next' works correctly.
-#
-use List::Util qw(first);
-use i3test i3_autostart => 0;
-
-sub assert_next {
-    my ($expected) = @_;
-
-    cmd 'workspace next';
-    # We need to sync after changing focus to a different output to wait for the
-    # EnterNotify to be processed, otherwise it will be processed at some point
-    # later in time and mess up our subsequent tests.
-    sync_with_i3;
-
-    is(focused_ws, $expected, "workspace $expected focused");
-}
-
-sub assert_prev {
-    my ($expected) = @_;
-
-    cmd 'workspace prev';
-    # We need to sync after changing focus to a different output to wait for the
-    # EnterNotify to be processed, otherwise it will be processed at some point
-    # later in time and mess up our subsequent tests.
-    sync_with_i3;
-
-    is(focused_ws, $expected, "workspace $expected focused");
-}
-
-my $config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-fake-outputs 1024x768+0+0,1024x768+1024+0
-EOT
-my $pid = launch_with_config($config);
-
-sync_with_i3;
-$x->root->warp_pointer(0, 0);
-sync_with_i3;
-
-################################################################################
-# Setup workspaces so that they stay open (with an empty container).
-# open_window ensures, this
-#
-#                   numbered       named
-# output 1 (left) : 1, 2, 3, 6, 7, B, F, C
-# output 2 (right): 4, 5,          A, D, E
-#
-################################################################################
-
-cmd 'focus output right';
-cmd 'workspace A'; open_window;
-cmd 'workspace D'; open_window;
-cmd 'workspace 4'; open_window;
-cmd 'workspace 5'; open_window;
-cmd 'workspace E'; open_window;
-
-cmd 'focus output left';
-cmd 'workspace 1'; open_window;
-cmd 'workspace 2'; open_window;
-cmd 'workspace B'; open_window;
-cmd 'workspace 3'; open_window;
-cmd 'workspace F'; open_window;
-cmd 'workspace 6'; open_window;
-cmd 'workspace C'; open_window;
-cmd 'workspace 7'; open_window;
-
-################################################################################
-# Use workspace next and verify the correct order.
-# numbered -> numerical sort
-# named -> sort by creation time
-################################################################################
-cmd 'workspace 1';
-is(focused_ws, '1', 'back on workspace 1');
-
-assert_next('2');
-assert_next('3');
-assert_next('4');
-assert_next('5');
-assert_next('6');
-assert_next('7');
-
-assert_next('B');
-assert_next('F');
-assert_next('C');
-assert_next('A');
-assert_next('D');
-assert_next('E');
-assert_next('1');
-
-cmd 'workspace 1';
-is(focused_ws, '1', 'back on workspace 1');
-
-assert_prev('E');
-assert_prev('D');
-assert_prev('A');
-assert_prev('C');
-assert_prev('F');
-assert_prev('B');
-
-assert_prev('7');
-assert_prev('6');
-assert_prev('5');
-assert_prev('4');
-assert_prev('3');
-assert_prev('2');
-assert_prev('1');
-
-
-exit_gracefully($pid);
-
-done_testing;
index 5f85805194d7fd0baca94b93fe860e9472a46eb1..63c0b6a6d14f022c8bc4545e7384d5b68da02dc7 100644 (file)
 #
 # Tests for _NET_WM_DESKTOP.
 # Ticket: #2153
-use i3test i3_autostart => 0;
+use i3test i3_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+bar {
+    status_command i3status
+}
+EOT
 use X11::XCB qw(:all);
 
 ###############################################################################
@@ -78,19 +85,6 @@ sub open_window_with_net_wm_desktop {
     return $window;
 }
 
-###############################################################################
-
-my $config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-bar {
-    status_command i3status
-}
-EOT
-
-my $pid = launch_with_config($config);
-
 ###############################################################################
 # Upon managing a window which does not set _NET_WM_DESKTOP, the property is
 # set on the window.
@@ -296,6 +290,4 @@ kill_all_windows;
 
 ###############################################################################
 
-exit_gracefully($pid);
-
 done_testing;
diff --git a/testcases/t/529-net-wm-desktop_mm.t b/testcases/t/529-net-wm-desktop_mm.t
deleted file mode 100644 (file)
index 7723894..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-#!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 for _NET_WM_DESKTOP.
-# Ticket: #2153
-use i3test i3_autostart => 0;
-use X11::XCB qw(:all);
-
-sub get_net_wm_desktop {
-    sync_with_i3;
-
-    my ($con) = @_;
-    my $cookie = $x->get_property(
-        0,
-        $con->{id},
-        $x->atom(name => '_NET_WM_DESKTOP')->id,
-        $x->atom(name => 'CARDINAL')->id,
-        0,
-        1
-    );
-
-    my $reply = $x->get_property_reply($cookie->{sequence});
-    return undef if $reply->{length} != 1;
-
-    return unpack("L", $reply->{value});
-}
-
-my $config = <<EOT;
-# i3 config file (v4)
-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-
-workspace "0" output "fake-0"
-workspace "1" output "fake-0"
-workspace "2" output "fake-0"
-workspace "10" output "fake-1"
-workspace "11" output "fake-1"
-workspace "12" output "fake-1"
-
-fake-outputs 1024x768+0+0,1024x768+1024+0
-EOT
-
-###############################################################################
-# _NET_WM_DESKTOP is updated when the window is moved to another workspace
-# on another output.
-###############################################################################
-
-my $pid = launch_with_config($config);
-
-cmd 'workspace 0';
-open_window;
-cmd 'workspace 10';
-open_window;
-cmd 'workspace 0';
-my $con = open_window;
-
-cmd 'move window to workspace 10';
-
-is(get_net_wm_desktop($con), 1, '_NET_WM_DESKTOP is updated when moving the window');
-
-exit_gracefully($pid);
-
-done_testing;
index cfe61dec693d4fe4b9a4a4d8bd1b0f0055aff7f2..b975fadad452f4fd13d7fa67eccf411d541e3ec4 100644 (file)
@@ -16,9 +16,7 @@
 #
 # Ticket: #2229
 # Bug still in: 4.11-262-geb631ce
-use i3test i3_autostart => 0;
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
@@ -26,7 +24,6 @@ fake-outputs 400x400+0+0,400x400+400+0
 workspace_auto_back_and_forth no
 EOT
 
-my $pid = launch_with_config($config);
 my $i3 = i3(get_socket_path());
 
 # Set it up such that workspace 3 is on the left output and
@@ -43,8 +40,7 @@ cmd 'move workspace to output left';
 # ensure that workspace 3 has now vanished
 my $get_ws = $i3->get_workspaces->recv;
 my @ws_names = map { $_->{name} } @$get_ws;
+# TODO get rid of smartmatch
 ok(!('3' ~~ @ws_names), 'workspace 3 has been closed');
 
-exit_gracefully($pid);
-
 done_testing;
index fd32865339b67f100b0a377cb520a623fe1e5b1e..b2040554d8f35cc6edf432173eb88a339283fc58 100644 (file)
 #
 # Tests that fullscreen windows appear on the output indicated by
 # their geometry
-use i3test i3_autostart => 0;
-use List::Util qw(first);
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
 fake-outputs 1024x768+0+0,1024x768+1024+0
 EOT
-
-my $pid = launch_with_config($config);
+use List::Util qw(first);
 
 # Helper functions
 sub fullscreen($) {
@@ -70,6 +66,4 @@ my $node2 = find_window($tree->{nodes}, $win_on_second_output->{id});
 is($node1->{fullscreen_mode}, 1, "first window is fullscreen");
 is($node2->{fullscreen_mode}, 1, "second window is fullscreen");
 
-exit_gracefully($pid);
-
 done_testing;
index 08fa88cc20b72f09e39d9233888fb66ef7ae6b67..5b81194ac3e160c0af0b80650e495e97794c9114 100644 (file)
 use File::Temp qw(tempfile);
 use i3test i3_autostart => 0;
 
+my $monitor_name = 'i3-fake-monitor';
+my $output_name = 'i3-fake-output';
+
 my $config = <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
+bar {
+    output $output_name
+}
 EOT
 
 my ($outfh, $outname) = tempfile('i3-randr15reply-XXXXXX', UNLINK => 1);
@@ -33,50 +39,81 @@ my ($outfh, $outname) = tempfile('i3-randr15reply-XXXXXX', UNLINK => 1);
 my $reply = pack('cxSLLLLx[LLL]',
      1, # reply
      0, # sequence (will be filled in by inject_randr15)
-     # 56 = length($reply) + length($monitor1)
+     # 60 = length($reply) + length($monitor1)
      # 32 = minimum X11 reply length
-     (56-32) / 4, # length in words
+     (60-32) / 4, # length in words
      0, # timestamp TODO
      1, # nmonitors
-     0); # noutputs
+     1); # noutputs
 
 # Manually intern _NET_CURRENT_DESKTOP as $x->atom will not create atoms if
 # they are not yet interned.
-my $atom_cookie = $x->intern_atom(0, length("DP3"), "DP3");
-my $DP3 = $x->intern_atom_reply($atom_cookie->{sequence})->{atom};
+my $atom_cookie = $x->intern_atom(0, length($monitor_name), $monitor_name);
+my $monitor_name_atom = $x->intern_atom_reply($atom_cookie->{sequence})->{atom};
 
 # MONITORINFO is defined in A.1.1 in
 # https://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt
-my $monitor1 = pack('LccSssSSLL',
-        $DP3, # name (ATOM)
+my $monitor1 = pack('LccSssSSLLL',
+        $monitor_name_atom, # name (ATOM)
         1, # primary
         1, # automatic
-        0, # ncrtcs
+        1, # ncrtcs
         0, # x
         0, # y
         3840, # width in pixels
         2160, # height in pixels
         520, # width in millimeters
-        290); # height in millimeters
+        290, # height in millimeters
+        12345); # output ID #0
 
 print $outfh $reply;
 print $outfh $monitor1;
 
 close($outfh);
 
-my $pid = launch_with_config($config, inject_randr15 => $outname);
+# Prepare a RRGetOutputInfo reply as well; see RRGetOutputInfo in
+# https://www.x.org/releases/current/doc/randrproto/randrproto.txt
+($outfh, my $outname_moninfo) = tempfile('i3-randr15reply-XXXXXX', UNLINK => 1);
+my $moninfo = pack('cxSLLLx[LLccSSSS]S a* x!4',
+                   1, # reply
+                   0, # sequence (will be filled in by inject_randr15)
+                   # 36 = length($moninfo) (without name and padding)
+                   # 32 = minimum X11 reply length
+                   ((36 + length($output_name) - 32) + 3) / 4, # length in words
+                   0, # timestamp TODO
+                   12345, # CRTC
+                   length($output_name), # length of name
+                   $output_name); # name
+
+print $outfh $moninfo;
+close($outfh);
+
+my $pid = launch_with_config($config,
+                             inject_randr15 => $outname,
+                             inject_randr15_outputinfo => $outname_moninfo);
 
 my $tree = i3->get_tree->recv;
 my @outputs = map { $_->{name} } @{$tree->{nodes}};
-is_deeply(\@outputs, [ '__i3', 'DP3' ], 'outputs are __i3 and DP3');
+is_deeply(\@outputs, [ '__i3', $monitor_name ], 'outputs are __i3 and the fake monitor');
 
-my ($dp3) = grep { $_->{name} eq 'DP3' } @{$tree->{nodes}};
-is_deeply($dp3->{rect}, {
+my ($output_data) = grep { $_->{name} eq $monitor_name } @{$tree->{nodes}};
+is_deeply($output_data->{rect}, {
         width => 3840,
         height => 2160,
         x => 0,
         y => 0,
-    }, 'Output DP3 at 3840x2160+0+0');
+    }, "Fake output at 3840x2160+0+0");
+
+# Verify that i3 canonicalizes RandR output names to i3 output names
+# (RandR monitor names) for bar configs
+
+my $bars = i3->get_bar_config()->recv;
+is(@$bars, 1, 'one bar configured');
+
+my $bar_id = shift @$bars;
+
+my $bar_config = i3->get_bar_config($bar_id)->recv;
+is_deeply($bar_config->{outputs}, [ $monitor_name ], 'bar_config output name is normalized');
 
 exit_gracefully($pid);
 
@@ -86,7 +123,7 @@ exit_gracefully($pid);
 
 # When inject_randr15 is defined but false, fake-xinerama will be turned off,
 # but inject_randr15 will not actually be used.
-my $pid = launch_with_config($config, inject_randr15 => '');
+$pid = launch_with_config($config, inject_randr15 => '');
 
 $tree = i3->get_tree->recv;
 @outputs = map { $_->{name} } @{$tree->{nodes}};
index 8f84f9ae959b2e87cbbdd22acdd104b1224510e0..997340fa0546817fe671bd20aaa860e9732ae7db 100644 (file)
 # over an unfocused workspace.
 # Ticket: #2681
 # Bug still in: 4.13-210-g80c23afa
-use i3test i3_autostart => 0;
-
-# Ensure the pointer is at (0, 0) so that we really start on the first
-# (the left) workspace.
-$x->root->warp_pointer(0, 0);
-
-my $config = <<EOT;
+use i3test i3_config => <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
@@ -33,8 +27,6 @@ fake-outputs 1024x768+0+0,1024x768+1024+0
 focus_follows_mouse no
 EOT
 
-my $pid = launch_with_config($config);
-
 cmd 'focus output fake-0';
 my $s0_ws = fresh_workspace;
 
@@ -56,6 +48,4 @@ my $reply = $x->query_pointer_reply($cookie->{sequence});
 cmp_ok($reply->{root_x}, '<', 1024, 'pointer still on fake-0');
 cmp_ok($reply->{root_y}, '<', 768, 'pointer still on fake-0');
 
-exit_gracefully($pid);
-
 done_testing;
diff --git a/testcases/t/535-workspace-next-prev.t b/testcases/t/535-workspace-next-prev.t
new file mode 100644 (file)
index 0000000..ac80eab
--- /dev/null
@@ -0,0 +1,124 @@
+#!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 whether 'workspace next' works correctly.
+#
+use List::Util qw(first);
+use i3test i3_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+fake-outputs 1024x768+0+0,1024x768+1024+0
+EOT
+
+sub assert_next {
+    my ($expected) = @_;
+
+    cmd 'workspace next';
+    # We need to sync after changing focus to a different output to wait for the
+    # EnterNotify to be processed, otherwise it will be processed at some point
+    # later in time and mess up our subsequent tests.
+    sync_with_i3;
+
+    is(focused_ws, $expected, "workspace $expected focused");
+}
+
+sub assert_prev {
+    my ($expected) = @_;
+
+    cmd 'workspace prev';
+    # We need to sync after changing focus to a different output to wait for the
+    # EnterNotify to be processed, otherwise it will be processed at some point
+    # later in time and mess up our subsequent tests.
+    sync_with_i3;
+
+    is(focused_ws, $expected, "workspace $expected focused");
+}
+
+sync_with_i3;
+$x->root->warp_pointer(0, 0);
+sync_with_i3;
+
+################################################################################
+# Setup workspaces so that they stay open (with an empty container).
+# open_window ensures, this
+#
+#                   numbered       named
+# output 1 (left) : 1, 2, 3, 6, 7, B, F, C
+# output 2 (right): 4, 5,          A, D, E
+#
+################################################################################
+
+cmd 'focus output right';
+cmd 'workspace A'; open_window;
+cmd 'workspace D'; open_window;
+cmd 'workspace 4'; open_window;
+cmd 'workspace 5'; open_window;
+cmd 'workspace E'; open_window;
+
+cmd 'focus output left';
+cmd 'workspace 1'; open_window;
+cmd 'workspace 2'; open_window;
+cmd 'workspace B'; open_window;
+cmd 'workspace 3'; open_window;
+cmd 'workspace F'; open_window;
+cmd 'workspace 6'; open_window;
+cmd 'workspace C'; open_window;
+cmd 'workspace 7'; open_window;
+
+################################################################################
+# Use workspace next and verify the correct order.
+# numbered -> numerical sort
+# named -> sort by creation time
+################################################################################
+cmd 'workspace 1';
+is(focused_ws, '1', 'back on workspace 1');
+
+assert_next('2');
+assert_next('3');
+assert_next('4');
+assert_next('5');
+assert_next('6');
+assert_next('7');
+
+assert_next('B');
+assert_next('F');
+assert_next('C');
+assert_next('A');
+assert_next('D');
+assert_next('E');
+assert_next('1');
+
+cmd 'workspace 1';
+is(focused_ws, '1', 'back on workspace 1');
+
+assert_prev('E');
+assert_prev('D');
+assert_prev('A');
+assert_prev('C');
+assert_prev('F');
+assert_prev('B');
+
+assert_prev('7');
+assert_prev('6');
+assert_prev('5');
+assert_prev('4');
+assert_prev('3');
+assert_prev('2');
+assert_prev('1');
+
+
+done_testing;
diff --git a/testcases/t/536-net-wm-desktop_mm.t b/testcases/t/536-net-wm-desktop_mm.t
new file mode 100644 (file)
index 0000000..6346ebb
--- /dev/null
@@ -0,0 +1,69 @@
+#!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 for _NET_WM_DESKTOP.
+# Ticket: #2153
+use i3test i3_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+workspace "0" output "fake-0"
+workspace "1" output "fake-0"
+workspace "2" output "fake-0"
+workspace "10" output "fake-1"
+workspace "11" output "fake-1"
+workspace "12" output "fake-1"
+
+fake-outputs 1024x768+0+0,1024x768+1024+0
+EOT
+use X11::XCB qw(:all);
+
+sub get_net_wm_desktop {
+    sync_with_i3;
+
+    my ($con) = @_;
+    my $cookie = $x->get_property(
+        0,
+        $con->{id},
+        $x->atom(name => '_NET_WM_DESKTOP')->id,
+        $x->atom(name => 'CARDINAL')->id,
+        0,
+        1
+    );
+
+    my $reply = $x->get_property_reply($cookie->{sequence});
+    return undef if $reply->{length} != 1;
+
+    return unpack("L", $reply->{value});
+}
+
+###############################################################################
+# _NET_WM_DESKTOP is updated when the window is moved to another workspace
+# on another output.
+###############################################################################
+
+cmd 'workspace 0';
+open_window;
+cmd 'workspace 10';
+open_window;
+cmd 'workspace 0';
+my $con = open_window;
+
+cmd 'move window to workspace 10';
+
+is(get_net_wm_desktop($con), 1, '_NET_WM_DESKTOP is updated when moving the window');
+
+done_testing;
diff --git a/testcases/t/537-move-single-to-output.t b/testcases/t/537-move-single-to-output.t
new file mode 100644 (file)
index 0000000..db5b492
--- /dev/null
@@ -0,0 +1,63 @@
+#!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 that windows inside containers with a single child do not jump
+# over other containers and erratically move across outputs.
+# Ticket: #2466
+# Bug still in: 4.14-63-g75d11820
+use i3test i3_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+fake-outputs 640x480+0+0,640x480+640+0
+
+workspace left output fake-0
+workspace right output fake-1
+EOT
+
+cmd 'workspace right';
+
+# ┌───────────────────────────┐ ┌───────────────────────────┐
+# │         Output 1          │ │     Output 2 - splith     │
+# │                           │ │┌───────────┐┌────────────┐│
+# │                           │ ││           ││   splitv   ││
+# │                           │ ││           ││╔══════════╗││
+# │                           │ ││           ││║          ║││
+# │     (empty workspace)     │ ││   first   ││║  second  ║││
+# │                           │ ││           ││║          ║││
+# │                           │ ││           ││║          ║││
+# │                           │ ││           ││╚══════════╝││
+# │                           │ │└───────────┘└────────────┘│
+# └───────────────────────────┘ └───────────────────────────┘
+#
+# Moving "second" left shouldn't cause it to jump over to output 1.
+
+my $first = open_window;
+my $second = open_window;
+
+# Put the second window into its own splitv container
+cmd 'split v';
+
+is_num_children('left', 0, 'No children on left');
+is_num_children('right', 2, 'Two children on right');
+
+# Move the second window to the left
+cmd 'move left';
+
+is_num_children('left', 0, 'Still no children on left');
+is_num_children('right', 2, 'Still two children on right');
+
+done_testing;
index 5704d8e4636587fd0ee21dcbf7e477da6b27a293..7eafb9fb12dcf6f3750a5012a2af30e8541c0ea9 100644 (file)
@@ -19,7 +19,7 @@ RUN apt-get update && \
     dpkg-dev devscripts git equivs \
     clang clang-format-3.8 \
     lintian \
-    libmodule-install-perl libanyevent-perl libextutils-pkgconfig-perl xcb-proto cpanminus xvfb xserver-xephyr xauth libinline-perl libinline-c-perl libxml-simple-perl libmouse-perl libmousex-nativetraits-perl libextutils-depends-perl perl libtest-deep-perl libtest-exception-perl libxml-parser-perl libtest-simple-perl libtest-fatal-perl libdata-dump-perl libtest-differences-perl libxml-tokeparser-perl libipc-run-perl libxcb-xtest0-dev libx11-xcb-perl libjson-xs-perl && \
+    libmodule-install-perl libanyevent-perl libextutils-pkgconfig-perl xcb-proto cpanminus xvfb xserver-xephyr xauth libinline-perl libinline-c-perl libxml-simple-perl libmouse-perl libmousex-nativetraits-perl libextutils-depends-perl perl libtest-deep-perl libtest-exception-perl libxml-parser-perl libtest-simple-perl libtest-fatal-perl libdata-dump-perl libtest-differences-perl libxml-tokeparser-perl libipc-run-perl libxcb-xtest0-dev libx11-xcb-perl libjson-xs-perl x11-xserver-utils && \
     rm -rf /var/lib/apt/lists/*
 
 # Install i3 build dependencies.