--- /dev/null
+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
use AnyEvent;
use Encode;
use Scalar::Util qw(tainted);
+use Carp;
=head1 NAME
# 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;
}
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) {
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
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
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
$ sudo apt-get install cpanminus
$ sudo cpanm .
$ cd ~/i3/AnyEvent-I3
+$ sudo cpanm Module::Install
$ sudo cpanm .
--------------------------------------------------------------------------------
$ cd ~/i3/testcases
$ sudo cpan .
$ cd ~/i3/AnyEvent-I3
+$ sudo cpan Module::Install
$ sudo cpan .
--------------------------------------------------------------------------------
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*:
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
*/
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().
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.
*
*/
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
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;
* 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);
*/
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.
*
* 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);
/* 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;
}
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);
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);
}
/*
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;
}
/*
- * 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.
*
*/
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);
}
/* 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 */
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;
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);
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);
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);
}
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);
y(map_open);
ystr("name");
- ystr(output->name);
+ ystr(output_primary_name(output));
ystr("active");
y(bool, output->active);
/* 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;
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;
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) {
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,
.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,
.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;
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
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);
+ }
}
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
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;
}
? 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
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) {
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;
}
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;
}
}
}
- 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;
}
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;
}
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;
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);
/* 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 */
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
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));
}
}
/* 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",
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 {
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
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;
}
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);
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",
* 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;
}
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);
}
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));
}
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();
*
*/
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 */
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));
}
restore_open_placeholder_windows(croot);
+ result = true;
- return true;
+out:
+ free(globbed);
+ free(buf);
+ return result;
}
/*
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. */
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;
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;
*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;
+}
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;
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 */
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;
}
}
+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)
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 */
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 */
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
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) {
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;
}
}
-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);
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);
}
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 = "";
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);
}
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};
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 . '"';
}
# 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...";
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++;
$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;
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) {
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};
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;
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 = '';
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;
}
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: $!");
say $fh scalar <$conf_fh>;
}
- say $fh "ipc-socket $tmp_socket_path"
- unless $args{dont_add_socket_path};
-
close($fh);
my $cv = AnyEvent->condvar;
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
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
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>
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;
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|);
# 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;
# TODO: Tests for "move to output" and "move workspace to output".
-exit_gracefully($pid);
-
done_testing;
+++ /dev/null
-#!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;
+++ /dev/null
-#!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;
+++ /dev/null
-#!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;
# 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';
open_window(name => "testcase");
is_num_children('targetws', 3, 'new window opened next to last one');
-exit_gracefully($pid);
-
done_testing;
# (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
}
EOT
-my $pid = launch_with_config($config);
-
my $i3 = i3(get_socket_path(0));
$i3->connect->recv;
ok($cv->recv, 'Mode event received');
-exit_gracefully($pid);
-
done_testing;
#
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
sleep(0.6);
is(count_total_urgent(get_ws($tmp3)), 0, "no more urgent windows on workspace $tmp3");
-exit_gracefully($pid);
-
done_testing;
# • 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;
# 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;
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;
# 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
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',
does_i3_live;
-exit_gracefully($pid);
-
done_testing;
#
# 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 = @_;
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,
my @urgent = grep { $_->{urgent} } @{get_ws_content('nonvisible')};
isnt(@urgent, 0, 'urgent window(s) found on destination workspace');
-exit_gracefully($pid);
-
done_testing;
+++ /dev/null
-#!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;
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
################################################################################
# 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'),
does_i3_live;
-exit_gracefully($pid);
-
done_testing;
# 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');
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;
+++ /dev/null
-#!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;
+++ /dev/null
-#!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;
+++ /dev/null
-#!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;
# 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
for_window [class="^special_scratchpad$"] move scratchpad
EOT
-my $pid = launch_with_config($config);
-
my $win = open_window;
my $scratch_window = open_window(
is($x->input_focus, $win->{id},
'an assignment that kills a window should not disturb focus');
-exit_gracefully($pid);
-
done_testing;
+++ /dev/null
-#!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;
+++ /dev/null
-#!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;
+++ /dev/null
-#!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;
# 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
for_window [title=".*"] layout tabbed, focus
EOT
-my $pid = launch_with_config($config);
-
# Switch away from workspace 1
my $tmp = fresh_workspace;
does_i3_live;
-exit_gracefully($pid);
-
done_testing;
+++ /dev/null
-#!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;
+++ /dev/null
-#!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;
# 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
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
is($new->{x}, $child->{x}, 'x coordinates match');
is($new->{y}, $child->{y}, 'y coordinates match');
-exit_gracefully($pid);
-
done_testing;
+++ /dev/null
-#!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;
# 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
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.
is(int($pos->{y} + $pos->{height} / 2), int($x->root->rect->height / 2),
'y coordinates match');
-exit_gracefully($pid);
-
done_testing;
# 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) = @_;
$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;
sync_with_i3;
is($x->input_focus, $second->id, 'second window still focused');
-exit_gracefully($pid);
-
done_testing;
+++ /dev/null
-#!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;
+++ /dev/null
-#!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;
# 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);
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;
# 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;
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|);
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|);
}
# 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
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;
sync_with_i3;
is(scalar @i3test::XTEST::binding_events, 4, 'Received exactly 4 binding events');
-exit_gracefully($pid);
-
}
done_testing;
+++ /dev/null
-#!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;
# 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
}
EOT
-my $pid = launch_with_config($config);
-
cmd 'mode othermode';
my $i3 = i3(get_socket_path(0));
ok($cv->recv, 'Mode event received');
-exit_gracefully($pid);
-
done_testing;
+++ /dev/null
-#!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;
+++ /dev/null
-#!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;
#
# 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();
is_num_children($ws, 0, 'no container on the current workspace');
-exit_gracefully($pid);
done_testing;
+++ /dev/null
-#!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;
+++ /dev/null
-#!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;
+++ /dev/null
-#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
# 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());
is_deeply(\@outputs, [ '__i3', 'fake-0', 'fake-1' ],
'multi-monitor outputs ok');
-exit_gracefully($pid);
done_testing;
# 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
verify_scratchpad_switch($first, $second);
-exit_gracefully($pid);
-
done_testing;
#
# 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());
cmd 'focus output fake-0';
is(focused_output, 'fake-0', 'focus on first output');
-exit_gracefully($pid);
-
done_testing;
# 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).
cmd 'workspace prev_on_output';
is(focused_ws, '2', 'workspace 2 focused');
-exit_gracefully($pid);
-
done_testing;
# 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).
################################################################################
-exit_gracefully($pid);
done_testing;
# 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);
my $third = fresh_workspace(output => 1);
verify_scratchpad_doesnt_move($third);
-exit_gracefully($pid);
-
done_testing;
# 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).
does_i3_live;
-exit_gracefully($pid);
-
done_testing;
# 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;
# 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;
cmd 'focus up';
is($x->input_focus, $second->id, 'second window focused');
-exit_gracefully($pid);
-
done_testing;
# 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);
$ws = get_ws($right_ws);
is(scalar @{$ws->{floating_nodes}}, 0, 'scratchpad window still in scratchpad after ConfigureRequest');
-exit_gracefully($pid);
-
done_testing;
# 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);
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;
# (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());
($nodes, $focus) = get_ws_content('5');
is($nodes->[1]->{window}, $win1->id, 'window 1 on workspace 5 after moving');
-exit_gracefully($pid);
-
done_testing;
# 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;
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;
# 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
bindsym 1 workspace 1: eggs
EOT
-my $pid = launch_with_config($config);
my $i3 = i3(get_socket_path());
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;
#
# 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
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
$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;
# 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
workspace ws-right output fake-1
EOT
-my $pid = launch_with_config($config);
-
my $i3 = i3(get_socket_path());
$i3->connect()->recv;
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;
# 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
fake-outputs 1024x768+0+0,1024x768+1024+0
EOT
-my $pid = launch_with_config($config);
my $i3 = i3(get_socket_path());
$i3->connect->recv;
'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;
# • 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 #
# 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;
# 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
fake-outputs 1024x768+0+0,1024x768+1024+0
EOT
-my $pid = launch_with_config($config);
-
cmd 'workspace ws_left';
my $win = open_window();
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;
# 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
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;
is_deeply(\@desktop_viewport, \@expected_viewport,
'it should be updated when a workspace is emptied');
-exit_gracefully($pid);
-
done_testing;
# 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
workspace baz output fake-1
EOT
-my $pid = launch_with_config($config);
my $i3 = i3(get_socket_path());
$i3->connect->recv;
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;
# 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
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.
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;
# 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
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.
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;
#
# 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
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;
},
})->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;
is($con->{window}, $left->{id}, 'button 5 moves focus left');
reset_test;
-exit_gracefully($pid);
-
done_testing;
#
# 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);
###############################################################################
# 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')
});
is($docks[0]->{rect}->{x}, 1024, 'dock client has moved to the other screen');
-exit_gracefully($pid);
-
###############################################################################
done_testing;
# 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) = @_;
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;
assert_prev('1');
-exit_gracefully($pid);
-
done_testing;
+++ /dev/null
-#!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;
#
# 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);
###############################################################################
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.
###############################################################################
-exit_gracefully($pid);
-
done_testing;
+++ /dev/null
-#!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;
#
# 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
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
# 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;
#
# 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($) {
is($node1->{fullscreen_mode}, 1, "first window is fullscreen");
is($node2->{fullscreen_mode}, 1, "second window is fullscreen");
-exit_gracefully($pid);
-
done_testing;
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);
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);
# 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}};
# 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
focus_follows_mouse no
EOT
-my $pid = launch_with_config($config);
-
cmd 'focus output fake-0';
my $s0_ws = fresh_workspace;
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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
--- /dev/null
+#!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;
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.