Merge branch 'testsuite' into next

This commit is contained in:
Michael Stapelberg 2011-09-24 20:10:21 +01:00
commit efec744747
54 changed files with 1016 additions and 718 deletions

View File

@ -1,5 +1,5 @@
all: hacking-howto.html debugging.html userguide.html ipc.html multi-monitor.html wsbar.html refcard.pdf
all: hacking-howto.html debugging.html userguide.html ipc.html multi-monitor.html wsbar.html refcard.pdf testsuite.html
hacking-howto.html: hacking-howto
asciidoc -a toc -n $<
@ -10,6 +10,9 @@ debugging.html: debugging
userguide.html: userguide
asciidoc -a toc -n $<
testsuite.html: testsuite
asciidoc -a toc -n $<
ipc.html: ipc
asciidoc -a toc -n $<

BIN
docs/i3-sync-working.dia Normal file

Binary file not shown.

BIN
docs/i3-sync-working.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/i3-sync.dia Normal file

Binary file not shown.

BIN
docs/i3-sync.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

446
docs/testsuite Normal file
View File

@ -0,0 +1,446 @@
i3 testsuite
============
Michael Stapelberg <michael+i3@stapelberg.de>
September 2011
This document explains how the i3 testsuite works, how to use it and how to
extend it. It is targeted at developers who not necessarily have been doing
testing before or have not been testing in Perl before. In general, the
testsuite is not of interest for end users.
== Introduction
The i3 testsuite is a collection of files which contain testcases for various
i3 features. Some of them test if a certain workflow works correctly (moving
windows, focus behaviour, …). Others are regression tests and contain code
which previously made i3 crash or lead to unexpected behaviour. They then check
if i3 still runs (meaning it did not crash) and if it handled everything
correctly.
The goal of having these tests is to automatically find problems and to
automatically get a feel for whether a change in the source code breaks any
existing feature. After every modification of the i3 sourcecode, the developer
should run the full testsuite. If one of the tests does not pass (but fails),
the corresponding problem should be fixed (or, in some cases, the testcase has
to be modified). For every bugreport, a testcase should be written to test the
correct behaviour. Initially, it will fail, but after fixing the bug, it will
pass. This ensures (or increases the chance) that bugs which have been fixed
once will never be found again.
Also, when implementing a new feature, a testcase might be a good way to be
able to easily test if the feature is working correctly. Many developers will
test manually if everything works. Having a testcase not only helps you with
that, but it will also be useful for every future change.
== Implementation
For several reasons, the i3 testsuite has been implemented in Perl:
1. Perl has a long tradition of testing. Every popular/bigger Perl module which
you can find on CPAN will not only come with documentation, but also with
tests. Therefore, the available infrastructure for tests is comprehensive.
See for example the excellent http://search.cpan.org/perldoc?Test::More
and the referenced http://search.cpan.org/perldoc?Test::Tutorial.
2. Perl is widely available and has a well-working package infrastructure.
3. The author is familiar with Perl :).
Please do not start programming language flamewars at this point.
=== Mechanisms
==== Script: complete-run
The testcases are run by a script called +complete-run.pl+. It runs all
testcases by default, but you can be more specific and let it only run one or
more testcases. Also, it takes care of starting up a separate instance of i3
with an appropriate configuration file and creates a folder for each run
containing the appropriate i3 logfile for each testcase. The latest folder can
always be found under the symlink +latest/+. It is recommended that you run the
tests on one or more separate X server instances (you can only start one window
manager per X session), for example using the provided Xdummy script.
+complete-run.pl+ takes one or more X11 display specifications and parallelizes
the testcases appropriately:
.Example invocation of complete-run.pl+
---------------------------------------
$ cd ~/i3/testcases
# start two dummy X11 instances in the background
$ ./Xdummy :1 &
$ ./Xdummy :2 &
$ ./complete-run.pl -d :1,:2
# output omitted because it is very long
All tests successful.
Files=78, Tests=734, 27 wallclock secs ( 0.38 usr 0.48 sys + 17.65 cusr 3.21 csys = 21.72 CPU)
Result: PASS
$ ./complete-run.pl -d :1 t/04-floating.t
[:3] i3 startup: took 0.07s, status = 1
[:3] Running t/04-floating.t with logfile testsuite-2011-09-24-16-06-04-4.0.2-226-g1eb011a/i3-log-for-04-floating.t
[:3] t/04-floating.t finished
[:3] killing i3
output for t/04-floating.t:
ok 1 - use X11::XCB::Window;
ok 2 - The object isa X11::XCB::Window
ok 3 - Window is mapped
ok 4 - i3 raised the width to 75
ok 5 - i3 raised the height to 50
ok 6 - i3 did not map it to (0x0)
ok 7 - The object isa X11::XCB::Window
ok 8 - i3 let the width at 80
ok 9 - i3 let the height at 90
ok 10 - i3 mapped it to x=1
ok 11 - i3 mapped it to y=18
ok 12 - The object isa X11::XCB::Window
ok 13 - i3 let the width at 80
ok 14 - i3 let the height at 90
1..14
All tests successful.
Files=1, Tests=14, 0 wallclock secs ( 0.01 usr 0.00 sys + 0.19 cusr 0.03 csys = 0.23 CPU)
Result: PASS
$ less latest/i3-log-for-04-floating.t
----------------------------------------
==== IPC interface
The testsuite makes extensive use of the IPC (Inter-Process Communication)
interface which i3 provides. It is used for the startup process of i3, for
terminating it cleanly and (most importantly) for modifying and getting the
current state (layout tree).
See [http://i3wm.org/docs/ipc.html] for documentation on the IPC interface.
==== X11::XCB
In order to open new windows, change attributes, get events, etc., the
testsuite uses X11::XCB, a new (and quite specific to i3 at the moment) Perl
module which uses the XCB protocol description to generate Perl bindings to
X11. They work in a very similar way to libxcb (which i3 uses) and provide
relatively high-level interfaces (objects such as +X11::XCB::Window+) aswell as
access to the low-level interface, which is very useful when testing a window
manager.
=== Filesystem structure
In the git root of i3, the testcases live in the folder +testcases+. This
folder contains the +complete-run.pl+ and +Xdummy+ scripts and a base
configuration file which will be used for the tests. The different testcases
themselve can be found in the conventionally named subfolder +t+:
.Filesystem structure
--------------------------------------------
├── testcases
│   ├── complete-run.pl
│   ├── i3-test.config
│   ├── t
│   │   ├── 00-load.t
│   │   ├── 01-tile.t
│   │   ├── 02-fullscreen.t
│   │   ├── ...
│   │   ├── omitted for brevity
│   │   ├── ...
│   │   ├── 74-regress-focus-toggle.t
│   │   └── lib
│   │   └── i3test.pm
│   └── Xdummy
--------------------------------------------
== Anatomy of a testcase
Learning by example is definitely a good strategy when you are wondering how to
write a testcase. Let's take +t/11-goto.t+ as an easy example and go through it
step by step:
.t/11-goto.t: Boilerplate
----------------------
#!perl
# vim:ts=4:sw=4:expandtab
use i3test;
use File::Temp;
my $x = X11::XCB::Connection->new;
-----------------------
This is what we call boilerplate. It exists at the top of every test file (to
some extent). The first line is the shebang, which specifies that this file is
a Perl script. The second line contains VIM specific settings on how to
edit/format this file (use spaces instead of tabs, indent using 4 spaces).
Afterwards, the +i3test+ module is used. This module contains i3 testsuite
specific functions which you are strongly encouraged to use. They make writing
testcases a lot easier and will make it easier for other people to read your
tests.
The next line uses the +File::Temp+ module. This is specific to this testcase,
because it needs to generate a temporary name during the test. Many testcases
use only the +i3test+ module.
The last line opens a connection to X11. You might or might not need this in
your testcase, depending on whether you are going to open windows (etc.) or
only use i3 commands.
.t/11-goto.t: Setup
----------------------
my $tmp = fresh_workspace;
cmd 'split h';
----------------------
The first line calls i3test's +fresh_workspace+ function which looks for a
currently unused workspace, switches to it, and returns its name. The variable
+$tmp+ will end up having a value such as +"/tmp/87kBVcHbA9"+. Note that this
is not (necessarily) a valid path, it's just a random workspace name.
So, now that we are on a new workspace, we ensure that the workspace uses
horizontal orientation by issuing the +split h+ command (see the i3 User's
Guide for a list of commands). This is not strictly necessary, but good style.
In general, the +cmd+ function executes the specified i3 command by using the
IPC interface and returns once i3 acknowledged the command.
.t/11-goto.t: Setup
----------------------
#####################################################################
# Create two windows and make sure focus switching works
#####################################################################
my $top = open_window($x);
my $mid = open_window($x);
my $bottom = open_window($x);
----------------------
In every major section of a testcase, you should put a comment like the one
above. This makes it immediately clear how the file is structured.
The +open_window+ function opens a standard window, which will then be put into
tiling mode by i3. If you want a floating window, use the
+open_floating_window+ function. These functions accept the same parameters as
+X11::XCB::Window->new+, see the i3test documentation at TODO.
.t/11-goto.t: Helper function
----------------------
#
# Returns the input focus after sending the given command to i3 via IPC
# end sleeping for half a second to make sure i3 reacted
#
sub focus_after {
my $msg = shift;
cmd $msg;
sync_with_i3 $x;
return $x->input_focus;
}
----------------------
This section defines a helper function which will be used over and over in this
testcase. If you have code which gets executed more than once or twice
(depending on the length of your test, use your best judgement), please put it
in a function. Tests should be short, concise and clear.
The +focus_after+ function executes a command and returns the X11 focus after
the command was executed. The +sync_with_i3+ command makes sure that i3 could
push its state to X11. See <<i3_sync>> to learn how this works exactly.
.t/11-goto.t: Test assumptions
----------------------
$focus = $x->input_focus;
is($focus, $bottom->id, "Latest window focused");
$focus = focus_after('focus left');
is($focus, $mid->id, "Middle window focused");
----------------------
Now, we run the first two real tests. They use +Test::More+'s +is+ function,
which compares two values and prints the differences if they are not the same.
After the arguments, we supply a short comment to indicate what we are testing
here. This makes it vastly more easy for the developer to spot which testcase
is the problem in case one fails.
The first test checks that the most recently opened window is focused.
Afterwards, the command +focus left+ is issued and it is verified that the
middle window now has focus.
Note that this is not a comprehensive test of the +focus+ command -- we would
have to test wrapping, focus when using a more complex layout, focusing the
parent/child containers, etc. But that is not the point of this testcase.
Instead, we just want to know if +$x->input_focus+ corresponds with what we are
expecting. If not, something is completely wrong with the test environment and
this trivial test will fail.
.t/11-goto.t: Test that the feature does not work (yet)
----------------------
#####################################################################
# Now goto a mark which does not exist
#####################################################################
my $random_mark = mktemp('mark.XXXXXX');
$focus = focus_after(qq|[con_mark="$random_mark"] focus|);
is($focus, $mid->id, "focus unchanged");
----------------------
In this new major section, a random mark (mark is an identifier for a window,
see "VIM-like marks" in the i3 Users Guide) will be generated. Afterwards, we
test that trying to focus that mark will not do anything. This is important: Do
not only test that using a feature has the expected outcome, but also test that
using it without properly initializing it does no harm. This command could for
example have changed focus anyways (a bug) or crash i3 (obviously a bug).
.t/11-goto.t: Test that the feature does work
----------------------
cmd "mark $random_mark";
$focus = focus_after('focus left');
is($focus, $top->id, "Top window focused");
$focus = focus_after(qq|[con_mark="$random_mark"] focus|);
is($focus, $mid->id, "goto worked");
----------------------
Remember: Focus was on the middle window (we verified that earlier in "Test
assumptions"). We now mark the middle window with our randomly generated mark.
Afterwards, we switch focus away from the middle window to be able to tell if
focusing it via its mark will work. If it does work (next test), the goto
command works.
.t/11-goto.t: Test corner case
----------------------
# check that we can specify multiple criteria
$focus = focus_after('focus left');
is($focus, $top->id, "Top window focused");
$focus = focus_after(qq|[con_mark="$random_mark" con_mark="$random_mark"] focus|);
is($focus, $mid->id, "goto worked");
----------------------
Now we test the same feature, but specifying the mark twice in the command.
This should have no effect, but lets be sure: test it and see if things go
wrong.
.t/11-goto.t: Test second code path
----------------------
#####################################################################
# Check whether the focus command will switch to a different
# workspace if necessary
#####################################################################
my $tmp2 = fresh_workspace;
is(focused_ws(), $tmp2, 'tmp2 now focused');
cmd qq|[con_mark="$random_mark"] focus|;
is(focused_ws(), $tmp, 'tmp now focused');
----------------------
This part of the test checks that focusing windows by mark works across
workspaces. It uses i3test's +focused_ws+ function to get the current
workspace.
.t/11-goto.t: Test second code path
----------------------
done_testing;
----------------------
The end of every testcase has to contain the +done_testing+ line. This tells
+complete-run.pl+ that the test was finished successfully. If it does not
occur, the test might have crashed during execution -- some of the reasons why
that could happen are bugs in the used modules, bugs in the testcase itself or
an i3 crash resulting in the testcase being unable to communicate with i3 via
IPC anymore.
[[i3_sync]]
== Appendix A: The i3 sync protocol
Consider the following situation: You open two windows in your testcase, then
you use +focus left+ and want to verify that the X11 focus has been updated
properly. Sounds simple, right? Lets assume you use this straight-forward
implementation:
.Racey focus testcase
-----------
my $left = open_window($x);
my $right = open_window($x);
cmd 'focus left';
is($x->input_focus, $left->id, 'left window focused');
----------
However, the test fails. Sometimes. Apparantly, there is a race condition in
your test. If you think about it, this is because you are using two different
pieces of software: You tell i3 to update focus, i3 confirms that, and then you
ask X11 to give you the current focus. There is a certain time i3 needs to
update the X11 state. If the testcase gets CPU time before X11 processed i3's
requests, the test will fail.
image::i3-sync.png["Diagram of the race condition", title="Diagram of the race condition"]
One way to "solve" this would be to add +sleep 0.5;+ after the +cmd+ call.
After 0.5 seconds it should be safe to assume that focus has been updated,
right?
In practice, this usually works. However, it has several problems:
1. This is obviously not a clean solution, but a workaround. Ugly.
2. On very slow machines, this might not work. Unlikely, but in different
situations (a delay to wait for i3 to startup) the necessary time is much
harder to guess, even for fast machines.
3. This *wastes a lot of time*. Usually, your computer is much faster than 0.5s
to update the status. However, sometimes, it might take 0.4s, so we cant
make it +sleep 0.1+.
To illustrate how grave the problem with wasting time actually is: Before
removing all sleeps from the testsuite, a typical run using 4 separate X
servers took around 50 seconds on my machine. After removing all the sleeps,
we achieved times of about 25 seconds. This is very significant and influences
the way you think about tests -- the faster they are, the more likely you are
to check whether everything still works quite often (which you should).
What I am trying to say is: This adds up quickly and makes the test suite less
robust.
The real solution for this problem is a mechanism which I call "the i3 sync
protocol". The idea is to send a request (which does not modify state) via X11
to i3 which will then be answered. Due to the request's position in the event
queue (*after* all previous events), you can be sure that by the time you
receive the reply, all other events have been dealt with by i3 (and, more
importantly, X11).
image::i3-sync-working.png["Diagram of the i3 sync solution", title="Diagram of the i3 sync solution"]
=== Implementation details
The client which wants to sync with i3 initiates the protocol by sending a
ClientMessage to the X11 root window:
.Send ClientMessage
-------------------
# Generate a ClientMessage, see xcb_client_message_t
my $msg = pack "CCSLLLLLLL",
CLIENT_MESSAGE, # response_type
32, # format
0, # sequence
$root, # destination window
$x->atom(name => 'I3_SYNC')->id,
$_sync_window->id, # data[0]: our own window id
$myrnd, # data[1]: a random value to identify the request
0,
0,
0;
# Send it to the root window -- since i3 uses the SubstructureRedirect
# event mask, it will get the ClientMessage.
$x->send_event(0, $root, EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
-------------------
i3 will then reply with the same ClientMessage, sent to the window specified in
+data[0]+. In the reply, +data[0]+ and +data[1]+ are exactly the same as in the
request. You should use a random value in +data[1]+ and check that you received
the same one when getting the reply.
== Appendix B: Socket activation

View File

@ -24,3 +24,4 @@ xmacro(WM_TAKE_FOCUS)
xmacro(WM_WINDOW_ROLE)
xmacro(I3_SOCKET_PATH)
xmacro(I3_CONFIG_PATH)
xmacro(I3_SYNC)

View File

@ -648,6 +648,25 @@ static int handle_client_message(xcb_client_message_event_t *event) {
tree_render();
x_push_changes(croot);
} else if (event->type == A_I3_SYNC) {
DLOG("i3 sync, yay\n");
xcb_window_t window = event->data.data32[0];
uint32_t rnd = event->data.data32[1];
DLOG("Sending random value %d back to X11 window 0x%08x\n", rnd, window);
void *reply = scalloc(32);
xcb_client_message_event_t *ev = reply;
ev->response_type = XCB_CLIENT_MESSAGE;
ev->window = window;
ev->type = A_I3_SYNC;
ev->format = 32;
ev->data.data32[0] = window;
ev->data.data32[1] = rnd;
xcb_send_event(conn, false, window, XCB_EVENT_MASK_NO_EVENT, (char*)ev);
xcb_flush(conn);
free(reply);
} else {
ELOG("unhandled clientmessage\n");
return 0;

View File

@ -27,8 +27,8 @@ use File::Basename qw(basename);
use AnyEvent::I3 qw(:all);
use Try::Tiny;
use Getopt::Long;
use Time::HiRes qw(sleep);
use X11::XCB::Connection;
use Time::HiRes qw(sleep gettimeofday tv_interval);
use X11::XCB;
use IO::Socket::UNIX; # core
use POSIX; # core
use AnyEvent::Handle;
@ -75,13 +75,14 @@ my $result = GetOptions(
my @conns;
my @wdisplays;
for my $display (@displays) {
try {
my $x = X11::XCB::Connection->new(display => $display);
my $screen;
my $x = X11::XCB->new($display, $screen);
if ($x->has_error) {
say STDERR "WARNING: Not using X11 display $display, could not connect";
} else {
push @conns, $x;
push @wdisplays, $display;
} catch {
say STDERR "WARNING: Not using X11 display $display, could not connect";
};
}
}
my $config = slurp('i3-test.config');
@ -133,12 +134,13 @@ sub take_job {
my $dont_start = (slurp($test) =~ /# !NO_I3_INSTANCE!/);
my $logpath = "$outdir/i3-log-for-" . basename($test);
my ($fh, $tmpfile) = tempfile();
my ($fh, $tmpfile) = tempfile('i3-run-cfg.XXXXXX', UNLINK => 1);
say $fh $config;
say $fh "ipc-socket /tmp/nested-$display";
close($fh);
my $activate_cv = AnyEvent->condvar;
my $time_before_start = [gettimeofday];
my $start_i3 = sub {
# remove the old unix socket
unlink("/tmp/nested-$display-activation");
@ -157,15 +159,12 @@ sub take_job {
if (!defined($pid)) {
die "could not fork()";
}
say "pid = $pid";
if ($pid == 0) {
say "child!";
$ENV{LISTEN_PID} = $$;
$ENV{LISTEN_FDS} = 1;
$ENV{DISPLAY} = $display;
$^F = 3;
say "fileno is " . fileno($socket);
close($reserved);
POSIX::dup2(fileno($socket), 3);
@ -200,7 +199,6 @@ sub take_job {
# wait for the reply
$hdl->push_read(chunk => 1, => sub {
my ($h, $line) = @_;
say "read something from i3";
$activate_cv->send(1);
undef $hdl;
});
@ -234,9 +232,14 @@ sub take_job {
# This will be called as soon as i3 is running and answered to our
# IPC request
$activate_cv->cb(sub {
say "cb";
my $time_activating = [gettimeofday];
my $start_duration = tv_interval($time_before_start, $time_activating);
my ($status) = $activate_cv->recv;
say "complete-run: status = $status";
if ($dont_start) {
say "[$display] Not starting i3, testcase does that";
} else {
say "[$display] i3 startup: took " . sprintf("%.2f", $start_duration) . "s, status = $status";
}
say "[$display] Running $test with logfile $logpath";

View File

@ -42,6 +42,7 @@ my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => $original_rect,
background_color => '#C0C0C0',
event_mask => [ 'structure_notify' ],
);
isa_ok($window, 'X11::XCB::Window');
@ -50,7 +51,7 @@ is_deeply($window->rect, $original_rect, "rect unmodified before mapping");
$window->map;
sleep 0.25;
wait_for_map $x;
# open another container to make the window get only half of the screen
cmd 'open';
@ -59,11 +60,9 @@ my $new_rect = $window->rect;
ok(!eq_deeply($new_rect, $original_rect), "Window got repositioned");
$original_rect = $new_rect;
sleep 0.25;
$window->fullscreen(1);
sleep 0.25;
sync_with_i3($x);
$new_rect = $window->rect;
ok(!eq_deeply($new_rect, $original_rect), "Window got repositioned after fullscreen");
@ -94,6 +93,7 @@ $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => $original_rect,
background_color => 61440,
event_mask => [ 'structure_notify' ],
);
is_deeply($window->rect, $original_rect, "rect unmodified before mapping");
@ -101,7 +101,7 @@ is_deeply($window->rect, $original_rect, "rect unmodified before mapping");
$window->fullscreen(1);
$window->map;
sleep(0.25);
wait_for_map $x;
$new_rect = $window->rect;
ok(!eq_deeply($new_rect, $original_rect), "Window got repositioned after fullscreen");
@ -124,10 +124,12 @@ my $swindow = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => $original_rect,
background_color => '#C0C0C0',
event_mask => [ 'structure_notify' ],
);
$swindow->map;
sleep 0.25;
sync_with_i3($x);
ok(!$swindow->mapped, 'window not mapped while fullscreen window active');
@ -135,12 +137,12 @@ $new_rect = $swindow->rect;
ok(!eq_deeply($new_rect, $original_rect), "Window got repositioned");
$swindow->fullscreen(1);
sleep 0.25;
sync_with_i3($x);
is(fullscreen_windows(), 1, 'amount of fullscreen windows');
$window->fullscreen(0);
sleep 0.25;
sync_with_i3($x);
is(fullscreen_windows(), 0, 'amount of fullscreen windows');
ok($swindow->mapped, 'window mapped after other fullscreen ended');
@ -152,7 +154,7 @@ ok($swindow->mapped, 'window mapped after other fullscreen ended');
###########################################################################
$swindow->fullscreen(0);
sleep 0.25;
sync_with_i3($x);
is(fullscreen_windows(), 0, 'amount of fullscreen windows after disabling');

View File

@ -17,13 +17,14 @@ my $window = $x->root->create_child(
background_color => '#C0C0C0',
# replace the type with 'utility' as soon as the coercion works again in X11::XCB
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
event_mask => [ 'structure_notify' ],
);
isa_ok($window, 'X11::XCB::Window');
$window->map;
sleep 0.25;
wait_for_map $x;
my ($absolute, $top) = $window->rect;
@ -40,13 +41,14 @@ $window = $x->root->create_child(
rect => [ 1, 1, 80, 90],
background_color => '#C0C0C0',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
event_mask => [ 'structure_notify' ],
);
isa_ok($window, 'X11::XCB::Window');
$window->map;
sleep 0.25;
wait_for_map $x;
($absolute, $top) = $window->rect;
@ -70,13 +72,14 @@ $window = $x->root->create_child(
rect => [ 1, 1, 80, 90],
background_color => '#C0C0C0',
#window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
event_mask => [ 'structure_notify' ],
);
isa_ok($window, 'X11::XCB::Window');
$window->map;
sleep 0.25;
wait_for_map $x;
cmd 'floating enable';

View File

@ -2,11 +2,6 @@
# vim:ts=4:sw=4:expandtab
use i3test;
use X11::XCB qw(:all);
BEGIN {
use_ok('X11::XCB::Connection') or BAIL_OUT('Cannot load X11::XCB::Connection');
}
my $x = X11::XCB::Connection->new;
@ -17,17 +12,14 @@ fresh_workspace;
#####################################################################
# Create a window so we can get a focus different from NULL
my $window = open_standard_window($x);
diag("window->id = " . $window->id);
sleep 0.25;
my $window = open_window($x);
my $focus = $x->input_focus;
diag("old focus = $focus");
# Switch to another workspace
fresh_workspace;
sync_with_i3($x);
my $new_focus = $x->input_focus;
isnt($focus, $new_focus, "Focus changed");

View File

@ -2,15 +2,9 @@
# vim:ts=4:sw=4:expandtab
use i3test;
use X11::XCB qw(:all);
BEGIN {
use_ok('X11::XCB::Connection') or BAIL_OUT('Cannot load X11::XCB::Connection');
}
my $x = X11::XCB::Connection->new;
my $i3 = i3(get_socket_path());
my $tmp = fresh_workspace;
#####################################################################
@ -21,14 +15,9 @@ my $tmp = fresh_workspace;
cmd 'layout default';
cmd 'split v';
my $top = open_standard_window($x);
my $mid = open_standard_window($x);
my $bottom = open_standard_window($x);
sleep 0.25;
diag("top id = " . $top->id);
diag("mid id = " . $mid->id);
diag("bottom id = " . $bottom->id);
my $top = open_window($x);
my $mid = open_window($x);
my $bottom = open_window($x);
#
# Returns the input focus after sending the given command to i3 via IPC
@ -37,7 +26,8 @@ diag("bottom id = " . $bottom->id);
sub focus_after {
my $msg = shift;
$i3->command($msg)->recv;
cmd $msg;
sync_with_i3 $x;
return $x->input_focus;
}

View File

@ -4,45 +4,26 @@
# over an unfocused tiling client and destroying the floating one again.
use i3test;
use X11::XCB qw(:all);
BEGIN {
use_ok('X11::XCB::Window') or BAIL_OUT('Could not load X11::XCB::Window');
}
my $x = X11::XCB::Connection->new;
my $i3 = i3(get_socket_path());
fresh_workspace;
cmd 'split h';
my $tiled_left = open_standard_window($x);
my $tiled_right = open_standard_window($x);
sleep 0.25;
my $tiled_left = open_window($x);
my $tiled_right = open_window($x);
# Get input focus before creating the floating window
my $focus = $x->input_focus;
# Create a floating window which is smaller than the minimum enforced size of i3
my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 1, 1, 30, 30],
background_color => '#C0C0C0',
type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
);
my $window = open_floating_window($x);
isa_ok($window, 'X11::XCB::Window');
$window->map;
sleep 1;
sleep 0.25;
is($x->input_focus, $window->id, 'floating window focused');
$window->unmap;
sleep 0.25;
wait_for_unmap($x);
is($x->input_focus, $focus, 'Focus correctly restored');

View File

@ -27,9 +27,7 @@ $i3->command('9')->recv;
#####################################################################
my $top = i3test::open_standard_window($x);
sleep(0.25);
my $mid = i3test::open_standard_window($x);
sleep(0.25);
my $bottom = i3test::open_standard_window($x);
sleep(0.25);

View File

@ -10,7 +10,6 @@ BEGIN {
}
my $x = X11::XCB::Connection->new;
my $i3 = i3(get_socket_path());
#####################################################################
# verify that there is no dock window yet
@ -30,17 +29,9 @@ my $screens = $x->screens;
my $primary = first { $_->primary } @{$screens};
# TODO: focus the primary screen before
my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30],
background_color => '#FF0000',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
);
$window->map;
sleep 0.25;
my $window = open_window($x, {
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
});
my $rect = $window->rect;
is($rect->width, $primary->rect->width, 'dock client is as wide as the screen');
@ -67,7 +58,7 @@ is($docknode->{rect}->{height}, 30, 'dock node has unchanged height');
$window->rect(X11::XCB::Rect->new(x => 0, y => 0, width => 50, height => 40));
sleep 0.25;
sync_with_i3 $x;
@docked = get_dock_clients('top');
is(@docked, 1, 'one dock client found');
@ -82,7 +73,7 @@ is($docknode->{rect}->{height}, 40, 'dock height changed');
$window->destroy;
sleep 0.25;
wait_for_unmap $x;
@docked = get_dock_clients();
is(@docked, 0, 'no more dock clients');
@ -91,16 +82,11 @@ is(@docked, 0, 'no more dock clients');
# check if it gets placed on bottom (by coordinates)
#####################################################################
$window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 1000, 30, 30],
background_color => '#FF0000',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
);
$window->map;
sleep 0.25;
$window = open_window($x, {
rect => [ 0, 1000, 30, 30 ],
background_color => '#FF0000',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
});
my $rect = $window->rect;
is($rect->width, $primary->rect->width, 'dock client is as wide as the screen');
@ -111,7 +97,7 @@ is(@docked, 1, 'dock client on bottom');
$window->destroy;
sleep 0.25;
wait_for_unmap $x;
@docked = get_dock_clients();
is(@docked, 0, 'no more dock clients');
@ -120,12 +106,12 @@ is(@docked, 0, 'no more dock clients');
# check if it gets placed on bottom (by hint)
#####################################################################
$window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 1000, 30, 30],
background_color => '#FF0000',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
);
$window = open_window($x, {
dont_map => 1,
rect => [ 0, 1000, 30, 30 ],
background_color => '#FF0000',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
});
$window->_create();
@ -145,24 +131,24 @@ $x->change_property(
$window->map;
sleep 0.25;
wait_for_map $x;
@docked = get_dock_clients('top');
is(@docked, 1, 'dock client on top');
$window->destroy;
sleep 0.25;
wait_for_unmap $x;
@docked = get_dock_clients();
is(@docked, 0, 'no more dock clients');
$window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 1000, 30, 30],
background_color => '#FF0000',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
);
$window = open_window($x, {
dont_map => 1,
rect => [ 0, 1000, 30, 30 ],
background_color => '#FF0000',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
});
$window->_create();
@ -182,7 +168,7 @@ $x->change_property(
$window->map;
sleep 0.25;
wait_for_map $x;
@docked = get_dock_clients('bottom');
is(@docked, 1, 'dock client on bottom');
@ -194,17 +180,16 @@ $window->destroy;
# regression test: transient dock client
#####################################################################
my $fwindow = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30],
background_color => '#FF0000',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
);
$fwindow = open_window($x, {
dont_map => 1,
background_color => '#FF0000',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
});
$fwindow->transient_for($window);
$fwindow->map;
sleep 0.25;
wait_for_map $x;
does_i3_live;

View File

@ -2,16 +2,10 @@
# vim:ts=4:sw=4:expandtab
use i3test;
use X11::XCB qw(:all);
use Digest::SHA1 qw(sha1_base64);
BEGIN {
use_ok('X11::XCB::Connection') or BAIL_OUT('Cannot load X11::XCB::Connection');
}
use File::Temp;
my $x = X11::XCB::Connection->new;
my $i3 = i3(get_socket_path());
my $tmp = fresh_workspace;
cmd 'split h';
@ -20,16 +14,9 @@ cmd 'split h';
# Create two windows and make sure focus switching works
#####################################################################
my $top = open_standard_window($x);
sleep 0.25;
my $mid = open_standard_window($x);
sleep 0.25;
my $bottom = open_standard_window($x);
sleep 0.25;
diag("top id = " . $top->id);
diag("mid id = " . $mid->id);
diag("bottom id = " . $bottom->id);
my $top = open_window($x);
my $mid = open_window($x);
my $bottom = open_window($x);
#
# Returns the input focus after sending the given command to i3 via IPC
@ -39,6 +26,7 @@ sub focus_after {
my $msg = shift;
cmd $msg;
sync_with_i3($x);
return $x->input_focus;
}
@ -52,12 +40,12 @@ is($focus, $mid->id, "Middle window focused");
# Now goto a mark which does not exist
#####################################################################
my $random_mark = sha1_base64(rand());
my $random_mark = mktemp('mark.XXXXXX');
$focus = focus_after(qq|[con_mark="$random_mark"] focus|);
is($focus, $mid->id, "focus unchanged");
$i3->command("mark $random_mark")->recv;
cmd "mark $random_mark";
$focus = focus_after('focus left');
is($focus, $top->id, "Top window focused");

View File

@ -1,8 +1,5 @@
#!perl
# vim:ts=4:sw=4:expandtab
# Beware that this test uses workspace 9 to perform some tests (it expects
# the workspace to be empty).
# TODO: skip it by default?
use i3test;
use X11::XCB qw(:all);
@ -19,31 +16,22 @@ fresh_workspace;
# Create a floating window and see if resizing works
#####################################################################
# Create a floating window
my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30],
background_color => '#C0C0C0',
# replace the type with 'utility' as soon as the coercion works again in X11::XCB
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
);
isa_ok($window, 'X11::XCB::Window');
$window->map;
sleep 0.25;
my $window = open_floating_window($x);
# See if configurerequests cause window movements (they should not)
my ($a, $t) = $window->rect;
$window->rect(X11::XCB::Rect->new(x => $a->x, y => $a->y, width => $a->width, height => $a->height));
sleep 0.25;
sync_with_i3($x);
my ($na, $nt) = $window->rect;
is_deeply($na, $a, 'Rects are equal after configurerequest');
sub test_resize {
$window->rect(X11::XCB::Rect->new(x => 0, y => 0, width => 100, height => 100));
sync_with_i3($x);
my ($absolute, $top) = $window->rect;
# Make sure the width/height are different from what were gonna test, so
@ -52,7 +40,8 @@ sub test_resize {
isnt($absolute->height, 500, 'height != 500');
$window->rect(X11::XCB::Rect->new(x => 0, y => 0, width => 300, height => 500));
sleep 0.25;
sync_with_i3($x);
($absolute, $top) = $window->rect;

View File

@ -19,8 +19,8 @@ my $tmp = fresh_workspace;
cmd 'split v';
my $top = open_standard_window($x);
my $bottom = open_standard_window($x);
my $top = open_window($x);
my $bottom = open_window($x);
my @urgent = grep { $_->{urgent} } @{get_ws_content($tmp)};
is(@urgent, 0, 'no window got the urgent flag');
@ -31,7 +31,7 @@ is(@urgent, 0, 'no window got the urgent flag');
# Add the urgency hint, switch to a different workspace and back again
#####################################################################
$top->add_hint('urgency');
sleep 0.5;
sync_with_i3($x);
@content = @{get_ws_content($tmp)};
@urgent = grep { $_->{urgent} } @content;
@ -48,7 +48,7 @@ cmd '[id="' . $top->id . '"] focus';
is(@urgent, 0, 'no window got the urgent flag after focusing');
$top->add_hint('urgency');
sleep 0.5;
sync_with_i3($x);
@urgent = grep { $_->{urgent} } @{get_ws_content($tmp)};
is(@urgent, 0, 'no window got the urgent flag after re-setting urgency hint');
@ -62,7 +62,7 @@ ok(!$ws->{urgent}, 'urgent flag not set on workspace');
my $otmp = fresh_workspace;
$top->add_hint('urgency');
sleep 0.5;
sync_with_i3($x);
$ws = get_ws($tmp);
ok($ws->{urgent}, 'urgent flag set on workspace');

View File

@ -9,7 +9,6 @@ BEGIN {
}
my $x = X11::XCB::Connection->new;
my $i3 = i3(get_socket_path());
my $tmp = fresh_workspace;
@ -21,74 +20,42 @@ my $tmp = fresh_workspace;
# one of both (depending on your screen resolution) will be positioned wrong.
####################################################################################
my $left = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [0, 0, 30, 30],
background_color => '#FF0000',
);
$left->name('Left');
$left->map;
my $right = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [0, 0, 30, 30],
background_color => '#FF0000',
);
$right->name('Right');
$right->map;
sleep 0.25;
my $left = open_window($x, { name => 'Left' });
my $right = open_window($x, { name => 'Right' });
my ($abs, $rgeom) = $right->rect;
my $child = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#C0C0C0',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
);
$child->name('Child window');
my $child = open_floating_window($x, {
dont_map => 1,
name => 'Child window',
});
$child->client_leader($right);
$child->map;
sleep 0.25;
ok(wait_for_map($x), 'child window mapped');
my $cgeom;
($abs, $cgeom) = $child->rect;
cmp_ok($cgeom->x, '>=', $rgeom->x, 'Child X >= right container X');
my $child2 = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#C0C0C0',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
);
$child2->name('Child window 2');
my $child2 = open_floating_window($x, {
dont_map => 1,
name => 'Child window 2',
});
$child2->client_leader($left);
$child2->map;
sleep 0.25;
ok(wait_for_map($x), 'second child window mapped');
($abs, $cgeom) = $child2->rect;
cmp_ok(($cgeom->x + $cgeom->width), '<', $rgeom->x, 'child above left window');
# check wm_transient_for
my $fwindow = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30],
background_color => '#FF0000',
);
my $fwindow = open_window($x, { dont_map => 1 });
$fwindow->transient_for($right);
$fwindow->map;
sleep 0.25;
ok(wait_for_map($x), 'transient window mapped');
my ($absolute, $top) = $fwindow->rect;
ok($absolute->{x} != 0 && $absolute->{y} != 0, 'i3 did not map it to (0x0)');
@ -100,16 +67,10 @@ SKIP: {
# Create a parent window
#####################################################################
my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#C0C0C0',
);
$window->name('Parent window');
my $window = open_window($x, { dont_map => 1, name => 'Parent window' });
$window->map;
sleep 0.25;
ok(wait_for_map($x), 'parent window mapped');
#########################################################################
# Switch to a different workspace and open a child window. It should be opened
@ -117,17 +78,11 @@ sleep 0.25;
#########################################################################
fresh_workspace;
my $child = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#C0C0C0',
);
$child->name('Child window');
my $child = open_window($x, { dont_map => 1, name => 'Child window' });
$child->client_leader($window);
$child->map;
sleep 0.25;
ok(wait_for_map($x), 'child window mapped');
isnt($x->input_focus, $child->id, "Child window focused");

View File

@ -12,20 +12,7 @@ ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
# Open a new window
my $x = X11::XCB::Connection->new;
my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#C0C0C0',
);
$window->map;
# give it some time to be picked up by the window manager
# TODO: better check for $window->mapped or something like that?
# maybe we can even wait for getting mapped?
my $c = 0;
while (@{get_ws_content($tmp)} == 0 and $c++ < 5) {
sleep 0.25;
}
my $window = open_window($x);
my $content = get_ws_content($tmp);
ok(@{$content} == 1, 'window mapped');
my $win = $content->[0];
@ -48,8 +35,7 @@ cmd 'nop now killing the window';
my $id = $win->{id};
cmd qq|[con_id="$id"] kill|;
# give i3 some time to pick up the UnmapNotify event
sleep 0.25;
wait_for_unmap $x;
cmd 'nop checking if its gone';
$content = get_ws_content($tmp);
@ -88,25 +74,27 @@ my $left = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#0000ff',
event_mask => [ 'structure_notify' ],
);
$left->_create;
set_wm_class($left->id, 'special', 'special');
$left->name('left');
$left->map;
sleep 0.25;
ok(wait_for_map($x), 'left window mapped');
my $right = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#0000ff',
event_mask => [ 'structure_notify' ],
);
$right->_create;
set_wm_class($right->id, 'special', 'special');
$right->name('right');
$right->map;
sleep 0.25;
ok(wait_for_map($x), 'right window mapped');
# two windows should be here
$content = get_ws_content($tmp);
@ -114,7 +102,7 @@ ok(@{$content} == 2, 'two windows opened');
cmd '[class="special" title="left"] kill';
sleep 0.25;
sync_with_i3($x);
$content = get_ws_content($tmp);
is(@{$content}, 1, 'one window still there');
@ -129,13 +117,14 @@ $left = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#0000ff',
event_mask => [ 'structure_notify' ],
);
$left->_create;
set_wm_class($left->id, 'special7', 'special7');
$left->name('left');
$left->map;
sleep 0.25;
ok(wait_for_map($x), 'left window mapped');
# two windows should be here
$content = get_ws_content($tmp);
@ -143,7 +132,7 @@ ok(@{$content} == 1, 'window opened');
cmd '[class="^special[0-9]$"] kill';
sleep 0.25;
wait_for_unmap $x;
$content = get_ws_content($tmp);
is(@{$content}, 0, 'window killed');
@ -158,13 +147,14 @@ $left = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#0000ff',
event_mask => [ 'structure_notify' ],
);
$left->_create;
set_wm_class($left->id, 'special7', 'special7');
$left->name('ä 3');
$left->map;
sleep 0.25;
ok(wait_for_map($x), 'left window mapped');
# two windows should be here
$content = get_ws_content($tmp);
@ -172,7 +162,7 @@ ok(@{$content} == 1, 'window opened');
cmd '[title="^\w [3]$"] kill';
sleep 0.25;
wait_for_unmap $x;
$content = get_ws_content($tmp);
is(@{$content}, 0, 'window killed');

View File

@ -102,7 +102,7 @@ $first = open_empty_con($i3);
$middle = open_empty_con($i3);
# XXX: the $right empty con will be filled with the x11 window we are creating afterwards
$right = open_empty_con($i3);
my $win = open_standard_window($x, '#00ff00');
my $win = open_window($x, { background_color => '#00ff00' });
cmd qq|[con_id="$middle"] focus|;
$win->destroy;

View File

@ -5,20 +5,13 @@
#
use i3test;
my $i3 = i3(get_socket_path());
my $x = X11::XCB::Connection->new;
my $tmp = fresh_workspace;
ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
my $win = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => X11::XCB::Rect->new(x => 0, y => 0, width => 30, height => 30),
background_color => '#C0C0C0',
);
my $win = open_window($x, { dont_map => 1 });
# XXX: we should check screen size. in screens with an AR of 2.0,
# this is not a good idea.
my $aspect = X11::XCB::Sizehints::Aspect->new;
@ -28,11 +21,11 @@ $aspect->max_num(600);
$aspect->max_den(300);
$win->_create;
$win->map;
sleep 0.25;
wait_for_map $x;
$win->hints->aspect($aspect);
$x->flush;
sleep 0.25;
sync_with_i3($x);
my $rect = $win->rect;
my $ar = $rect->width / $rect->height;

View File

@ -13,8 +13,8 @@ my $tmp = fresh_workspace;
# 1: see if focus stays the same when toggling tiling/floating mode
#############################################################################
my $first = open_standard_window($x);
my $second = open_standard_window($x);
my $first = open_window($x);
my $second = open_window($x);
is($x->input_focus, $second->id, 'second window focused');
@ -30,9 +30,9 @@ is($x->input_focus, $second->id, 'second window still focused after mode toggle'
$tmp = fresh_workspace;
$first = open_standard_window($x); # window 2
$second = open_standard_window($x); # window 3
my $third = open_standard_window($x); # window 4
$first = open_window($x); # window 2
$second = open_window($x); # window 3
my $third = open_window($x); # window 4
is($x->input_focus, $third->id, 'last container focused');
@ -40,6 +40,8 @@ cmd 'floating enable';
cmd '[id="' . $second->id . '"] focus';
sync_with_i3($x);
is($x->input_focus, $second->id, 'second con focused');
cmd 'floating enable';
@ -47,7 +49,8 @@ cmd 'floating enable';
# now kill the third one (it's floating). focus should stay unchanged
cmd '[id="' . $third->id . '"] kill';
sleep 0.25;
# TODO: wait for unmapnotify
sync_with_i3($x);
is($x->input_focus, $second->id, 'second con still focused after killing third');
@ -59,9 +62,9 @@ is($x->input_focus, $second->id, 'second con still focused after killing third')
$tmp = fresh_workspace;
$first = open_standard_window($x, '#ff0000'); # window 5
$second = open_standard_window($x, '#00ff00'); # window 6
my $third = open_standard_window($x, '#0000ff'); # window 7
$first = open_window($x, '#ff0000'); # window 5
$second = open_window($x, '#00ff00'); # window 6
my $third = open_window($x, '#0000ff'); # window 7
is($x->input_focus, $third->id, 'last container focused');
@ -69,6 +72,8 @@ cmd 'floating enable';
cmd '[id="' . $second->id . '"] focus';
sync_with_i3($x);
is($x->input_focus, $second->id, 'second con focused');
cmd 'floating enable';
@ -77,13 +82,14 @@ cmd 'floating enable';
# also floating
cmd 'kill';
sleep 0.25;
# TODO: wait for unmapnotify
sync_with_i3($x);
is($x->input_focus, $third->id, 'third con focused');
cmd 'kill';
sleep 0.25;
# TODO: wait for unmapnotify
sync_with_i3($x);
is($x->input_focus, $first->id, 'first con focused after killing all floating cons');
@ -93,11 +99,11 @@ is($x->input_focus, $first->id, 'first con focused after killing all floating co
$tmp = fresh_workspace;
$first = open_standard_window($x, '#ff0000'); # window 5
$first = open_window($x, { background_color => '#ff0000' }); # window 5
cmd 'split v';
cmd 'layout stacked';
$second = open_standard_window($x, '#00ff00'); # window 6
$third = open_standard_window($x, '#0000ff'); # window 7
$second = open_window($x, { background_color => '#00ff00' }); # window 6
$third = open_window($x, { background_color => '#0000ff' }); # window 7
is($x->input_focus, $third->id, 'last container focused');
@ -105,23 +111,26 @@ cmd 'floating enable';
cmd '[id="' . $second->id . '"] focus';
sync_with_i3($x);
is($x->input_focus, $second->id, 'second con focused');
cmd 'floating enable';
sleep 0.5;
sync_with_i3($x);
# now kill the second one. focus should fall back to the third one, which is
# also floating
cmd 'kill';
sleep 0.25;
# TODO: wait for unmapnotify
sync_with_i3($x);
is($x->input_focus, $third->id, 'second con focused');
is($x->input_focus, $third->id, 'third con focused');
cmd 'kill';
sleep 0.25;
# TODO: wait for unmapnotify
sync_with_i3($x);
is($x->input_focus, $first->id, 'first con focused after killing all floating cons');
@ -131,8 +140,10 @@ is($x->input_focus, $first->id, 'first con focused after killing all floating co
$tmp = fresh_workspace;
$first = open_standard_window($x, '#ff0000'); # window 8
$second = open_standard_window($x, '#00ff00'); # window 9
$first = open_window($x, { background_color => '#ff0000' }); # window 8
$second = open_window($x, { background_color => '#00ff00' }); # window 9
sync_with_i3($x);
is($x->input_focus, $second->id, 'second container focused');
@ -142,31 +153,31 @@ is($x->input_focus, $second->id, 'second container focused');
cmd 'focus tiling';
sleep 0.25;
sync_with_i3($x);
is($x->input_focus, $first->id, 'first (tiling) container focused');
cmd 'focus floating';
sleep 0.25;
sync_with_i3($x);
is($x->input_focus, $second->id, 'second (floating) container focused');
cmd 'focus floating';
sleep 0.25;
sync_with_i3($x);
is($x->input_focus, $second->id, 'second (floating) container still focused');
cmd 'focus mode_toggle';
sleep 0.25;
sync_with_i3($x);
is($x->input_focus, $first->id, 'first (tiling) container focused');
cmd 'focus mode_toggle';
sleep 0.25;
sync_with_i3($x);
is($x->input_focus, $second->id, 'second (floating) container focused');
@ -176,39 +187,41 @@ is($x->input_focus, $second->id, 'second (floating) container focused');
$tmp = fresh_workspace;
$first = open_standard_window($x, '#ff0000', 1); # window 10
$second = open_standard_window($x, '#00ff00', 1); # window 11
$third = open_standard_window($x, '#0000ff', 1); # window 12
$first = open_floating_window($x, { background_color => '#ff0000' });# window 10
$second = open_floating_window($x, { background_color => '#00ff00' }); # window 11
$third = open_floating_window($x, { background_color => '#0000ff' }); # window 12
sync_with_i3($x);
is($x->input_focus, $third->id, 'third container focused');
cmd 'focus left';
sleep 0.25;
sync_with_i3($x);
is($x->input_focus, $second->id, 'second container focused');
cmd 'focus left';
sleep 0.25;
sync_with_i3($x);
is($x->input_focus, $first->id, 'first container focused');
cmd 'focus left';
sleep 0.25;
sync_with_i3($x);
is($x->input_focus, $third->id, 'focus wrapped to third container');
cmd 'focus right';
sleep 0.25;
sync_with_i3($x);
is($x->input_focus, $first->id, 'focus wrapped to first container');
cmd 'focus right';
sleep 0.25;
sync_with_i3($x);
is($x->input_focus, $second->id, 'focus on second container');

View File

@ -22,20 +22,7 @@ ok(workspace_exists($tmp), "workspace $tmp exists");
my $x = X11::XCB::Connection->new;
# Create a floating window which is smaller than the minimum enforced size of i3
my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30],
background_color => '#C0C0C0',
# replace the type with 'utility' as soon as the coercion works again in X11::XCB
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
);
isa_ok($window, 'X11::XCB::Window');
$window->map;
sleep 0.25;
my $window = open_floating_window($x);
ok($window->mapped, 'Window is mapped');
# switch to a different workspace, see if the window is still mapped?

View File

@ -21,27 +21,14 @@ my $tmp = fresh_workspace;
my $x = X11::XCB::Connection->new;
# Create a floating window which is smaller than the minimum enforced size of i3
my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30],
background_color => '#C0C0C0',
# replace the type with 'utility' as soon as the coercion works again in X11::XCB
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
);
isa_ok($window, 'X11::XCB::Window');
$window->map;
sleep 0.25;
my $window = open_floating_window($x);
ok($window->mapped, 'Window is mapped');
# switch to a different workspace, see if the window is still mapped?
my $otmp = fresh_workspace;
sleep 0.25;
sync_with_i3($x);
ok(!$window->mapped, 'Window is not mapped after switching ws');

View File

@ -5,7 +5,6 @@
use i3test;
use X11::XCB qw(:all);
use Time::HiRes qw(sleep);
BEGIN {
use_ok('X11::XCB::Window');
@ -22,20 +21,7 @@ my $tmp = fresh_workspace;
my $x = X11::XCB::Connection->new;
# Create a floating window
my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30],
background_color => '#C0C0C0',
# replace the type with 'utility' as soon as the coercion works again in X11::XCB
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
);
isa_ok($window, 'X11::XCB::Window');
$window->map;
sleep 0.25;
my $window = open_floating_window($x);
ok($window->mapped, 'Window is mapped');
my $ws = get_ws($tmp);
@ -45,17 +31,7 @@ is(@{$ws->{floating_nodes}}, 1, 'one floating node');
is(@{$nodes}, 0, 'no tiling nodes');
# Create a tiling window
my $twindow = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30],
background_color => '#C0C0C0',
);
isa_ok($twindow, 'X11::XCB::Window');
$twindow->map;
sleep 0.25;
my $twindow = open_window($x);
($nodes, $focus) = get_ws_content($tmp);
@ -68,8 +44,8 @@ is(@{$nodes}, 1, 'one tiling node');
$tmp = fresh_workspace;
my $first = open_standard_window($x);
my $second = open_standard_window($x);
my $first = open_window($x);
my $second = open_window($x);
cmd 'layout stacked';
@ -78,27 +54,14 @@ is(@{$ws->{floating_nodes}}, 0, 'no floating nodes so far');
is(@{$ws->{nodes}}, 1, 'one tiling node (stacked con)');
# Create a floating window
my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30],
background_color => '#C0C0C0',
# replace the type with 'utility' as soon as the coercion works again in X11::XCB
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
);
isa_ok($window, 'X11::XCB::Window');
$window->map;
sleep 0.25;
my $window = open_floating_window($x);
ok($window->mapped, 'Window is mapped');
$ws = get_ws($tmp);
is(@{$ws->{floating_nodes}}, 1, 'one floating nodes');
is(@{$ws->{nodes}}, 1, 'one tiling node (stacked con)');
my $third = open_standard_window($x);
my $third = open_window($x);
$ws = get_ws($tmp);

View File

@ -30,7 +30,7 @@ check_order('workspace order alright before testing');
cmd "workspace 93";
open_standard_window($x);
open_window($x);
my @ws = @{$i3->get_workspaces->recv};
my @f = grep { defined($_->{num}) && $_->{num} == 93 } @ws;
@ -38,23 +38,23 @@ is(@f, 1, 'ws 93 found by num');
check_order('workspace order alright after opening 93');
cmd "workspace 92";
open_standard_window($x);
open_window($x);
check_order('workspace order alright after opening 92');
cmd "workspace 94";
open_standard_window($x);
open_window($x);
check_order('workspace order alright after opening 94');
cmd "workspace 96";
open_standard_window($x);
open_window($x);
check_order('workspace order alright after opening 96');
cmd "workspace foo";
open_standard_window($x);
open_window($x);
check_order('workspace order alright after opening foo');
cmd "workspace 91";
open_standard_window($x);
open_window($x);
check_order('workspace order alright after opening 91');
done_testing;

View File

@ -4,7 +4,6 @@
# bug introduced by 77d0d42ed2d7ac8cafe267c92b35a81c1b9491eb
use i3test;
use X11::XCB qw(:all);
use Time::HiRes qw(sleep);
BEGIN {
use_ok('X11::XCB::Window');
@ -25,12 +24,11 @@ sub check_order {
my $tmp = fresh_workspace;
my $left = open_standard_window($x);
sleep 0.25;
my $mid = open_standard_window($x);
sleep 0.25;
my $right = open_standard_window($x);
sleep 0.25;
my $left = open_window($x);
my $mid = open_window($x);
my $right = open_window($x);
sync_with_i3($x);
diag("left = " . $left->id . ", mid = " . $mid->id . ", right = " . $right->id);

View File

@ -14,10 +14,10 @@ my $tmp = fresh_workspace;
cmd 'split v';
my $top = open_standard_window($x);
sleep 0.25;
my $bottom = open_standard_window($x);
sleep 0.25;
my $top = open_window($x);
my $bottom = open_window($x);
sync_with_i3($x);
diag("top = " . $top->id . ", bottom = " . $bottom->id);
@ -54,10 +54,8 @@ $tmp = fresh_workspace;
cmd 'split v';
$top = open_standard_window($x);
sleep 0.25;
$bottom = open_standard_window($x);
sleep 0.25;
$top = open_window($x);
$bottom = open_window($x);
cmd 'split h';
cmd 'layout stacked';
@ -78,8 +76,7 @@ is($nodes->[1]->{percent}, 0.75, 'bottom window got 75%');
$tmp = fresh_workspace;
$top = open_standard_window($x);
sleep 0.25;
$top = open_window($x);
cmd 'floating enable';

View File

@ -17,12 +17,9 @@ my $x = X11::XCB::Connection->new;
my $tmp = fresh_workspace;
my $left = open_standard_window($x);
sleep 0.25;
my $mid = open_standard_window($x);
sleep 0.25;
my $right = open_standard_window($x);
sleep 0.25;
my $left = open_window($x);
my $mid = open_window($x);
my $right = open_window($x);
cmd 'move before v';
cmd 'move after h';

View File

@ -2,7 +2,6 @@
# vim:ts=4:sw=4:expandtab
#
use X11::XCB qw(:all);
use Time::HiRes qw(sleep);
use i3test;
BEGIN {
@ -13,14 +12,11 @@ my $x = X11::XCB::Connection->new;
my $tmp = fresh_workspace;
my $left = open_standard_window($x);
sleep 0.25;
my $mid = open_standard_window($x);
sleep 0.25;
my $left = open_window($x);
my $mid = open_window($x);
cmd 'split v';
my $bottom = open_standard_window($x);
sleep 0.25;
my $bottom = open_window($x);
my ($nodes, $focus) = get_ws_content($tmp);
@ -28,23 +24,8 @@ my ($nodes, $focus) = get_ws_content($tmp);
# 1: open a floating window, get it mapped
#############################################################################
my $x = X11::XCB::Connection->new;
# Create a floating window
my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30],
background_color => '#C0C0C0',
# replace the type with 'utility' as soon as the coercion works again in X11::XCB
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
);
isa_ok($window, 'X11::XCB::Window');
$window->map;
sleep 0.25;
my $window = open_floating_window($x);
ok($window->mapped, 'Window is mapped');
($nodes, $focus) = get_ws_content($tmp);

View File

@ -16,12 +16,9 @@ my $x = X11::XCB::Connection->new;
my $tmp = fresh_workspace;
my $left = open_standard_window($x);
sleep 0.25;
my $mid = open_standard_window($x);
sleep 0.25;
my $right = open_standard_window($x);
sleep 0.25;
my $left = open_window($x);
my $mid = open_window($x);
my $right = open_window($x);
# go to workspace level
cmd 'level up';

View File

@ -16,21 +16,21 @@ my $x = X11::XCB::Connection->new;
my $tmp = fresh_workspace;
# open a tiling window on the first workspace
open_standard_window($x);
sleep 0.25;
open_window($x);
#sleep 0.25;
my $first = get_focused($tmp);
# on a different ws, open a floating window
my $otmp = fresh_workspace;
open_standard_window($x);
sleep 0.25;
open_window($x);
#sleep 0.25;
my $float = get_focused($otmp);
cmd 'mode toggle';
sleep 0.25;
#sleep 0.25;
# move the floating con to first workspace
cmd "move workspace $tmp";
sleep 0.25;
#sleep 0.25;
# switch to the first ws and check focus
is(get_focused($tmp), $float, 'floating client correctly focused');

View File

@ -11,7 +11,6 @@ BEGIN {
}
my $x = X11::XCB::Connection->new;
my $i3 = i3(get_socket_path());
my $tmp = fresh_workspace;
@ -26,16 +25,10 @@ is(@docked, 0, 'no dock clients yet');
# open a dock client
my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30],
background_color => '#FF0000',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
);
$window->map;
sleep 0.25;
my $window = open_window($x, {
background_color => '#FF0000',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
});
#####################################################################
# check that we can find it in the layout tree at the expected position
@ -69,7 +62,7 @@ is($docknode->{rect}->{height}, 30, 'dock node has unchanged height after restar
$window->destroy;
sleep 0.25;
wait_for_unmap $x;
@docked = get_dock_clients;
is(@docked, 0, 'no dock clients found');
@ -78,17 +71,12 @@ is(@docked, 0, 'no dock clients found');
# create a dock client with a 1px border
#####################################################################
$window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
border => 1,
rect => [ 0, 0, 30, 20],
background_color => '#00FF00',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
);
$window->map;
sleep 0.25;
$window = open_window($x, {
border => 1,
rect => [ 0, 0, 30, 20 ],
background_color => '#00FF00',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
});
@docked = get_dock_clients;
is(@docked, 1, 'one dock client found');

View File

@ -11,17 +11,7 @@ my $tmp = fresh_workspace;
my $x = X11::XCB::Connection->new;
# Create a floating window which is smaller than the minimum enforced size of i3
my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 400, 150],
background_color => '#C0C0C0',
);
isa_ok($window, 'X11::XCB::Window');
$window->map;
sleep 0.25;
my $window = open_window($x, { rect => [ 0, 0, 400, 150 ] });
my ($absolute, $top) = $window->rect;
@ -30,7 +20,7 @@ cmp_ok($absolute->{width}, '>', 400, 'i3 raised the width');
cmp_ok($absolute->{height}, '>', 150, 'i3 raised the height');
cmd 'floating toggle';
sleep 0.25;
sync_with_i3($x);
($absolute, $top) = $window->rect;

View File

@ -11,7 +11,6 @@ BEGIN {
}
my $x = X11::XCB::Connection->new;
my $i3 = i3(get_socket_path());
my $tmp = fresh_workspace;
@ -28,31 +27,19 @@ is(@docked, 0, 'no dock clients yet');
# open a dock client
#####################################################################
my $first = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30],
background_color => '#FF0000',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
);
$first->map;
sleep 0.25;
my $first = open_window($x, {
background_color => '#FF0000',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
});
#####################################################################
# Open a second dock client
#####################################################################
my $second = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30],
background_color => '#FF0000',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
);
$second->map;
sleep 0.25;
my $second = open_window($x, {
background_color => '#FF0000',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
});
#####################################################################
# Kill the second dock client

View File

@ -12,7 +12,6 @@ BEGIN {
}
my $x = X11::XCB::Connection->new;
my $i3 = i3(get_socket_path());
my $tmp = fresh_workspace;
@ -20,29 +19,19 @@ my $tmp = fresh_workspace;
# open a window with 200x80
#####################################################################
my $first = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 200, 80],
background_color => '#FF0000',
);
$first->map;
sleep 0.25;
my $first = open_window($x, {
rect => [ 0, 0, 200, 80],
background_color => '#FF0000',
});
#####################################################################
# Open a second window with 300x90
#####################################################################
my $second = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 300, 90],
background_color => '#00FF00',
);
$second->map;
sleep 0.25;
my $second = open_window($x, {
rect => [ 0, 0, 300, 90],
background_color => '#00FF00',
});
#####################################################################
# Set the parent to floating

View File

@ -20,7 +20,7 @@ my $tmp = fresh_workspace;
# open the left window
#####################################################################
my $left = open_standard_window($x, '#ff0000');
my $left = open_window($x, { background_color => '#ff0000' });
is($x->input_focus, $left->id, 'left window focused');
@ -30,7 +30,7 @@ diag("left = " . $left->id);
# Open the right window
#####################################################################
my $right = open_standard_window($x, '#00ff00');
my $right = open_window($x, { background_color => '#00ff00' });
diag("right = " . $right->id);
@ -44,7 +44,15 @@ cmd 'fullscreen';
# Open a third window
#####################################################################
my $third = open_standard_window($x, '#0000ff');
my $third = open_window($x, {
background_color => '#0000ff',
name => 'Third window',
dont_map => 1,
});
$third->map;
sync_with_i3 $x;
diag("third = " . $third->id);
@ -56,7 +64,7 @@ cmd "move workspace $tmp2";
# verify that the third window has the focus
sleep 0.25;
sync_with_i3($x);
is($x->input_focus, $third->id, 'third window focused');

View File

@ -11,7 +11,6 @@ BEGIN {
}
my $x = X11::XCB::Connection->new;
my $i3 = i3(get_socket_path());
my $tmp = fresh_workspace;
@ -19,7 +18,7 @@ my $tmp = fresh_workspace;
# open a window, verify its not in fullscreen mode
#####################################################################
my $win = open_standard_window($x);
my $win = open_window($x);
my $nodes = get_ws_content $tmp;
is(@$nodes, 1, 'exactly one client');

View File

@ -7,106 +7,35 @@ use X11::XCB qw(:all);
use i3test;
use v5.10;
BEGIN {
use_ok('EV');
use_ok('AnyEvent');
use_ok('X11::XCB::Window');
use_ok('X11::XCB::Event::Generic');
use_ok('X11::XCB::Event::MapNotify');
use_ok('X11::XCB::Event::ClientMessage');
}
my $x = X11::XCB::Connection->new;
my $i3 = i3(get_socket_path());
subtest 'Window without WM_TAKE_FOCUS', sub {
fresh_workspace;
my $tmp = fresh_workspace;
my $window = open_window($x);
my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#00ff00',
event_mask => [ 'structure_notify' ],
);
$window->name('Window 1');
$window->map;
my $cv = AE::cv;
my $prep = EV::prepare sub {
$x->flush;
};
my $check = EV::check sub {
while (defined(my $event = $x->poll_for_event)) {
if ($event->response_type == 161) {
# clientmessage
$cv->send(0);
}
}
};
my $w = EV::io $x->get_file_descriptor, EV::READ, sub {
# do nothing, we only need this watcher so that EV picks up the events
};
# Trigger timeout after 1 second
my $t = AE::timer 1, 0, sub {
$cv->send(1);
};
my $result = $cv->recv;
ok($result, 'cv result');
ok(!wait_for_event($x, 1, sub { $_[0]->{response_type} == 161 }), 'did not receive ClientMessage');
done_testing;
};
subtest 'Window with WM_TAKE_FOCUS', sub {
fresh_workspace;
my $tmp = fresh_workspace;
my $take_focus = $x->atom(name => 'WM_TAKE_FOCUS');
my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#00ff00',
event_mask => [ 'structure_notify' ],
protocols => [ $x->atom(name => 'WM_TAKE_FOCUS') ],
);
my $window = open_window($x, {
dont_map => 1,
protocols => [ $take_focus ],
});
$window->name('Window 1');
$window->map;
my $cv = AE::cv;
my $prep = EV::prepare sub {
$x->flush;
};
my $check = EV::check sub {
while (defined(my $event = $x->poll_for_event)) {
if ($event->response_type == 161) {
$cv->send($event->data);
}
}
};
my $w = EV::io $x->get_file_descriptor, EV::READ, sub {
# do nothing, we only need this watcher so that EV picks up the events
};
my $t = AE::timer 1, 0, sub {
say "timer!";
$cv->send(undef);
};
my $result = $cv->recv;
ok(defined($result), 'got a ClientMessage');
if (defined($result)) {
my ($data, $time) = unpack("L2", $result);
is($data, $x->atom(name => 'WM_TAKE_FOCUS')->id, 'first uint32_t contains WM_TAKE_FOCUS atom');
}
ok(wait_for_event($x, 1, sub {
return 0 unless $_[0]->{response_type} == 161;
my ($data, $time) = unpack("L2", $_[0]->{data});
return ($data == $take_focus->id);
}), 'got ClientMessage with WM_TAKE_FOCUS atom');
done_testing;
};

View File

@ -18,7 +18,7 @@ my $i3_path = abs_path("../i3");
# default case: socket will be created in /tmp/i3-<username>/ipc-socket.<pid>
#####################################################################
my ($fh, $tmpfile) = tempfile();
my ($fh, $tmpfile) = tempfile('/tmp/i3-test-config.XXXXXX', UNLINK => 1);
say $fh "# i3 config file (v4)";
say $fh "font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1";
close($fh);
@ -67,7 +67,7 @@ my $tmpdir = tempdir(CLEANUP => 1);
$socketpath = $tmpdir . "/config.sock";
ok(! -e $socketpath, "$socketpath does not exist yet");
($fh, $tmpfile) = tempfile();
($fh, $tmpfile) = tempfile('/tmp/i3-test-config.XXXXXX', UNLINK => 1);
say $fh "# i3 config file (v4)";
say $fh "font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1";
say $fh "ipc-socket $socketpath";

View File

@ -12,7 +12,7 @@ use i3test;
my $x = X11::XCB::Connection->new;
my $i3 = i3(get_socket_path());
my $tmp = fresh_workspace;
my $window = open_standard_window($x);
my $window = open_window($x);
sub get_border_style {
my @content = @{get_ws_content($tmp)};

View File

@ -52,7 +52,7 @@ is($docknode->{rect}->{height}, 30, 'dock node has unchanged height');
$window->add_hint('urgency');
sleep 0.25;
sync_with_i3($x);
does_i3_live;

View File

@ -7,34 +7,17 @@
use X11::XCB qw(:all);
use i3test;
BEGIN {
use_ok('X11::XCB::Window');
use_ok('X11::XCB::Event::Generic');
use_ok('X11::XCB::Event::MapNotify');
use_ok('X11::XCB::Event::ClientMessage');
}
my $x = X11::XCB::Connection->new;
my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#00ff00',
event_mask => [ 'structure_notify' ],
);
my $window = open_window($x);
$window->name('Window 1');
$window->map;
diag('window mapped');
sleep 0.5;
sync_with_i3($x);
is($window->state, ICCCM_WM_STATE_NORMAL, 'WM_STATE normal');
$window->unmap;
sleep 0.5;
wait_for_unmap $x;
is($window->state, ICCCM_WM_STATE_WITHDRAWN, 'WM_STATE withdrawn');

View File

@ -4,8 +4,6 @@
# Tests if WM_STATE is WM_STATE_NORMAL when mapped and WM_STATE_WITHDRAWN when
# unmapped.
#
use X11::XCB qw(:all);
use X11::XCB::Connection;
use i3test;
my $x = X11::XCB::Connection->new;
@ -15,8 +13,10 @@ sub two_windows {
ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
my $first = open_standard_window($x);
my $second = open_standard_window($x);
my $first = open_window($x);
my $second = open_window($x);
sync_with_i3 $x;
is($x->input_focus, $second->id, 'second window focused');
ok(@{get_ws_content($tmp)} == 2, 'two containers opened');

View File

@ -32,18 +32,19 @@ my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#00ff00',
event_mask => [ 'structure_notify' ],
);
$window->name('Border window');
$window->map;
sleep 0.25;
wait_for_map $x;
my @content = @{get_ws_content($tmp)};
cmp_ok(@content, '==', 1, 'one node on this workspace now');
is($content[0]->{border}, 'normal', 'normal border');
$window->unmap;
sleep 0.25;
wait_for_unmap $x;
my @content = @{get_ws_content($tmp)};
cmp_ok(@content, '==', 0, 'no more nodes');
@ -53,6 +54,7 @@ $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#00ff00',
event_mask => [ 'structure_notify' ],
);
$window->_create;
@ -79,14 +81,14 @@ sub set_wm_class {
set_wm_class($window->id, 'borderless', 'borderless');
$window->name('Borderless window');
$window->map;
sleep 0.25;
wait_for_map $x;
@content = @{get_ws_content($tmp)};
cmp_ok(@content, '==', 1, 'one node on this workspace now');
is($content[0]->{border}, 'none', 'no border');
$window->unmap;
sleep 0.25;
wait_for_unmap $x;
@content = @{get_ws_content($tmp)};
cmp_ok(@content, '==', 0, 'no more nodes');
@ -113,24 +115,25 @@ $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#00ff00',
event_mask => [ 'structure_notify' ],
);
$window->name('special title');
$window->map;
sleep 0.25;
wait_for_map $x;
@content = @{get_ws_content($tmp)};
cmp_ok(@content, '==', 1, 'one node on this workspace now');
is($content[0]->{border}, 'normal', 'normal border');
$window->name('special borderless title');
sleep 0.25;
sync_with_i3 $x;
@content = @{get_ws_content($tmp)};
is($content[0]->{border}, 'none', 'no border');
$window->name('special title');
sleep 0.25;
sync_with_i3 $x;
cmd 'border normal';
@ -138,13 +141,13 @@ cmd 'border normal';
is($content[0]->{border}, 'normal', 'border reset to normal');
$window->name('special borderless title');
sleep 0.25;
sync_with_i3 $x;
@content = @{get_ws_content($tmp)};
is($content[0]->{border}, 'normal', 'still normal border');
$window->unmap;
sleep 0.25;
wait_for_unmap $x;
@content = @{get_ws_content($tmp)};
cmp_ok(@content, '==', 0, 'no more nodes');
@ -172,17 +175,18 @@ $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#00ff00',
event_mask => [ 'structure_notify' ],
);
$window->name('special mark title');
$window->map;
sleep 0.25;
wait_for_map $x;
@content = @{get_ws_content($tmp)};
cmp_ok(@content, '==', 1, 'one node on this workspace now');
is($content[0]->{border}, 'none', 'no border');
my $other = open_standard_window($x);
my $other = open_window($x);
@content = @{get_ws_content($tmp)};
cmp_ok(@content, '==', 2, 'two nodes');
@ -215,6 +219,7 @@ $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#00ff00',
event_mask => [ 'structure_notify' ],
);
$window->_create;
@ -222,14 +227,14 @@ $window->_create;
set_wm_class($window->id, 'borderless', 'borderless');
$window->name('usethis');
$window->map;
sleep 0.25;
wait_for_map $x;
@content = @{get_ws_content($tmp)};
cmp_ok(@content, '==', 1, 'one node on this workspace now');
is($content[0]->{border}, 'none', 'no border');
$window->unmap;
sleep 0.25;
wait_for_unmap $x;
@content = @{get_ws_content($tmp)};
cmp_ok(@content, '==', 0, 'no nodes on this workspace now');
@ -237,7 +242,7 @@ cmp_ok(@content, '==', 0, 'no nodes on this workspace now');
set_wm_class($window->id, 'borderless', 'borderless');
$window->name('notthis');
$window->map;
sleep 0.25;
wait_for_map $x;
@content = @{get_ws_content($tmp)};
cmp_ok(@content, '==', 1, 'one node on this workspace now');
@ -264,6 +269,7 @@ $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#00ff00',
event_mask => [ 'structure_notify' ],
);
$window->_create;
@ -271,7 +277,7 @@ $window->_create;
set_wm_class($window->id, 'bar', 'foo');
$window->name('usethis');
$window->map;
sleep 0.25;
wait_for_map $x;
@content = @{get_ws_content($tmp)};
cmp_ok(@content, '==', 1, 'one node on this workspace now');
@ -298,6 +304,7 @@ $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#00ff00',
event_mask => [ 'structure_notify' ],
);
$window->_create;
@ -305,7 +312,7 @@ $window->_create;
set_wm_class($window->id, 'bar', 'foo');
$window->name('usethis');
$window->map;
sleep 0.25;
wait_for_map $x;
@content = @{get_ws_content($tmp)};
cmp_ok(@content, '==', 1, 'one node on this workspace now');
@ -334,6 +341,7 @@ $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#00ff00',
event_mask => [ 'structure_notify' ],
);
$window->_create;
@ -341,7 +349,7 @@ $window->_create;
set_wm_class($window->id, 'bar', 'foo');
$window->name('usethis');
$window->map;
sleep 0.25;
wait_for_map $x;
@content = @{get_ws_content($tmp)};
cmp_ok(@content, '==', 1, 'one node on this workspace now');
@ -370,6 +378,7 @@ $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#00ff00',
event_mask => [ 'structure_notify' ],
);
$window->_create;
@ -388,7 +397,7 @@ $x->change_property(
$window->name('usethis');
$window->map;
sleep 0.25;
wait_for_map $x;
@content = @{get_ws_content($tmp)};
cmp_ok(@content, '==', 1, 'one node on this workspace now');
@ -418,13 +427,14 @@ $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#00ff00',
event_mask => [ 'structure_notify' ],
);
$window->_create;
$window->name('usethis');
$window->map;
sleep 0.25;
wait_for_map $x;
@content = @{get_ws_content($tmp)};
cmp_ok(@content, '==', 1, 'one node on this workspace now');
@ -444,7 +454,7 @@ $x->change_property(
$x->flush;
sleep 0.25;
sync_with_i3 $x;
@content = @{get_ws_content($tmp)};
cmp_ok(@content, '==', 1, 'one node on this workspace now');

View File

@ -50,13 +50,14 @@ my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#0000ff',
event_mask => [ 'structure_notify' ],
);
$window->_create;
set_wm_class($window->id, 'special', 'special');
$window->name('special window');
$window->map;
sleep 0.25;
wait_for_map $x;
ok(@{get_ws_content($tmp)} == 1, 'special window got managed to current (random) workspace');
@ -64,8 +65,6 @@ exit_gracefully($process->pid);
$window->destroy;
sleep 0.25;
#####################################################################
# start a window and see that it gets assigned to a formerly unused
# workspace
@ -89,13 +88,14 @@ my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#0000ff',
event_mask => [ 'structure_notify' ],
);
$window->_create;
set_wm_class($window->id, 'special', 'special');
$window->name('special window');
$window->map;
sleep 0.25;
wait_for_map $x;
ok(@{get_ws_content($tmp)} == 0, 'still no containers');
ok("targetws" ~~ @{get_workspace_names()}, 'targetws exists');
@ -128,13 +128,18 @@ my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#0000ff',
event_mask => [ 'structure_notify' ],
);
$window->_create;
set_wm_class($window->id, 'special', 'special');
$window->name('special window');
$window->map;
sleep 0.25;
# We use sync_with_i3 instead of wait_for_map here because i3 will not actually
# map the window -- it will be assigned to a different workspace and will only
# be mapped once you switch to that workspace
sync_with_i3 $x;
ok(@{get_ws_content($tmp)} == 0, 'still no containers');
ok(@{get_ws_content('targetws')} == 2, 'two containers on targetws');
@ -164,13 +169,14 @@ my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#0000ff',
event_mask => [ 'structure_notify' ],
);
$window->_create;
set_wm_class($window->id, 'special', 'special');
$window->name('special window');
$window->map;
sleep 0.25;
wait_for_map $x;
my $content = get_ws($tmp);
ok(@{$content->{nodes}} == 0, 'no tiling cons');
@ -204,13 +210,14 @@ my $window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30 ],
background_color => '#0000ff',
event_mask => [ 'structure_notify' ],
);
$window->_create;
set_wm_class($window->id, 'SPEcial', 'SPEcial');
$window->name('special window');
$window->map;
sleep 0.25;
wait_for_map $x;
my $content = get_ws($tmp);
ok(@{$content->{nodes}} == 0, 'no tiling cons');
@ -249,13 +256,14 @@ my $window = $x->root->create_child(
rect => [ 0, 0, 30, 30 ],
background_color => '#0000ff',
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
event_mask => [ 'structure_notify' ],
);
$window->_create;
set_wm_class($window->id, 'special', 'special');
$window->name('special window');
$window->map;
sleep 0.25;
wait_for_map $x;
my $content = get_ws($tmp);
ok(@{$content->{nodes}} == 0, 'no tiling cons');

View File

@ -27,8 +27,10 @@ my $tmp = fresh_workspace;
ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
my $first = open_standard_window($x);
my $second = open_standard_window($x);
my $first = open_window($x);
my $second = open_window($x);
sync_with_i3($x);
is($x->input_focus, $second->id, 'second window focused');
ok(@{get_ws_content($tmp)} == 2, 'two containers opened');
@ -54,8 +56,10 @@ $tmp = fresh_workspace;
ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
$first = open_standard_window($x);
$second = open_standard_window($x);
$first = open_window($x);
$second = open_window($x);
sync_with_i3($x);
is($x->input_focus, $second->id, 'second window focused');
my @content = @{get_ws_content($tmp)};
@ -68,8 +72,8 @@ is($content[0]->{layout}, 'stacked', 'layout stacked');
#####################################################################
cmd 'focus parent';
my $right_top = open_standard_window($x);
my $right_bot = open_standard_window($x);
my $right_top = open_window($x);
my $right_bot = open_window($x);
@content = @{get_ws_content($tmp)};
is(@content, 2, 'two cons at workspace level after focus parent');

View File

@ -11,8 +11,8 @@ my $x = X11::XCB::Connection->new;
fresh_workspace;
open_standard_window($x);
open_standard_window($x);
open_window($x);
open_window($x);
cmd 'layout stacking';
sleep 1;

View File

@ -25,13 +25,13 @@ my $tmp = fresh_workspace;
ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
my $first = open_standard_window($x);
my $second = open_standard_window($x);
my $first = open_window($x);
my $second = open_window($x);
cmd 'layout tabbed';
cmd 'focus parent';
my $third = open_standard_window($x);
my $third = open_window($x);
is($x->input_focus, $third->id, 'third window focused');
cmd 'focus left';
@ -66,13 +66,16 @@ $tmp = fresh_workspace;
ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
$first = open_standard_window($x);
$second = open_standard_window($x);
$first = open_window($x);
$second = open_window($x);
cmd 'layout tabbed';
cmd 'focus parent';
$third = open_standard_window($x);
$third = open_window($x);
sync_with_i3($x);
is($x->input_focus, $third->id, 'third window focused');
cmd 'focus left';

View File

@ -23,7 +23,7 @@ sub slurp {
sub migrate_config {
my ($config) = @_;
my ($fh, $tmpfile) = tempfile();
my ($fh, $tmpfile) = tempfile('/tmp/i3-migrate-cfg.XXXXXX', UNLINK => 1);
print $fh $config;
close($fh);

View File

@ -6,8 +6,6 @@
#
use i3test;
use X11::XCB qw(:all);
use X11::XCB::Connection;
my $x = X11::XCB::Connection->new;
@ -27,7 +25,7 @@ my $tmp = fresh_workspace;
ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
my $first = open_standard_window($x);
my $first = open_window($x);
my @content = @{get_ws_content($tmp)};
ok(@content == 1, 'one container opened');
@ -53,7 +51,7 @@ $tmp = fresh_workspace;
ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
$first = open_standard_window($x);
$first = open_window($x);
@content = @{get_ws_content($tmp)};
ok(@content == 1, 'one container opened');
@ -77,18 +75,7 @@ $tmp = fresh_workspace;
ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
# Create a floating window which is smaller than the minimum enforced size of i3
$first = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30],
background_color => '#C0C0C0',
# replace the type with 'utility' as soon as the coercion works again in X11::XCB
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
);
$first->map;
sleep 0.25;
$first = open_floating_window($x);
my $wscontent = get_ws($tmp);
my @floating = @{$wscontent->{floating_nodes}};
@ -116,18 +103,7 @@ $tmp = fresh_workspace;
ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
# Create a floating window which is smaller than the minimum enforced size of i3
$first = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => [ 0, 0, 30, 30],
background_color => '#C0C0C0',
# replace the type with 'utility' as soon as the coercion works again in X11::XCB
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
);
$first->map;
sleep 0.25;
$first = open_floating_window($x);
$wscontent = get_ws($tmp);
@floating = @{$wscontent->{floating_nodes}};

View File

@ -7,6 +7,7 @@ use X11::XCB::Rect;
use X11::XCB::Window;
use X11::XCB qw(:all);
use AnyEvent::I3;
use EV;
use List::Util qw(first);
use List::MoreUtils qw(lastval);
use Time::HiRes qw(sleep);
@ -17,10 +18,33 @@ use Proc::Background;
use v5.10;
use Exporter ();
our @EXPORT = qw(get_workspace_names get_unused_workspace fresh_workspace get_ws_content get_ws get_focused open_empty_con open_standard_window get_dock_clients cmd does_i3_live exit_gracefully workspace_exists focused_ws get_socket_path launch_with_config);
our @EXPORT = qw(
get_workspace_names
get_unused_workspace
fresh_workspace
get_ws_content
get_ws
get_focused
open_empty_con
open_window
open_floating_window
get_dock_clients
cmd
sync_with_i3
does_i3_live
exit_gracefully
workspace_exists
focused_ws
get_socket_path
launch_with_config
wait_for_event
wait_for_map
wait_for_unmap
);
my $tester = Test::Builder->new();
my $_cached_socket_path = undef;
my $_sync_window = undef;
my $tmp_socket_path = undef;
BEGIN {
@ -47,32 +71,107 @@ use warnings;
goto \&Exporter::import;
}
sub open_standard_window {
my ($x, $color, $floating) = @_;
#
# Waits for the next event and calls the given callback for every event to
# determine if this is the event we are waiting for.
#
# Can be used to wait until a window is mapped, until a ClientMessage is
# received, etc.
#
# wait_for_event $x, 0.25, sub { $_[0]->{response_type} == MAP_NOTIFY };
#
sub wait_for_event {
my ($x, $timeout, $cb) = @_;
$color ||= '#c0c0c0';
my $cv = AE::cv;
# We cannot use a hashref here because create_child expands the arguments into an array
my @args = (
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => X11::XCB::Rect->new(x => 0, y => 0, width => 30, height => 30 ),
background_color => $color,
);
my $prep = EV::prepare sub {
$x->flush;
};
if (defined($floating) && $floating) {
@args = (@args, window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'));
}
my $check = EV::check sub {
while (defined(my $event = $x->poll_for_event)) {
if ($cb->($event)) {
$cv->send(1);
last;
}
}
};
my $window = $x->root->create_child(@args);
my $watcher = EV::io $x->get_file_descriptor, EV::READ, sub {
# do nothing, we only need this watcher so that EV picks up the events
};
# Trigger timeout after $timeout seconds (can be fractional)
my $timeout = AE::timer $timeout, 0, sub { warn "timeout"; $cv->send(0) };
my $result = $cv->recv;
return $result;
}
# thin wrapper around wait_for_event which waits for MAP_NOTIFY
# make sure to include 'structure_notify' in the windows event_mask attribute
sub wait_for_map {
my ($x) = @_;
wait_for_event $x, 1, sub { $_[0]->{response_type} == MAP_NOTIFY };
}
# Wrapper around wait_for_event which waits for UNMAP_NOTIFY. Also calls
# sync_with_i3 to make sure i3 also picked up and processed the UnmapNotify
# event.
sub wait_for_unmap {
my ($x) = @_;
wait_for_event $x, 1, sub { $_[0]->{response_type} == UNMAP_NOTIFY };
sync_with_i3($x);
}
#
# Opens a new window (see X11::XCB::Window), maps it, waits until it got mapped
# and synchronizes with i3.
#
# set dont_map to a true value to avoid mapping
#
# default values:
# class => WINDOW_CLASS_INPUT_OUTPUT
# rect => [ 0, 0, 30, 30 ]
# background_color => '#c0c0c0'
# event_mask => [ 'structure_notify' ]
# name => 'Window <n>'
#
sub open_window {
my ($x, $args) = @_;
my %args = ($args ? %$args : ());
my $dont_map = delete $args{dont_map};
$args{class} //= WINDOW_CLASS_INPUT_OUTPUT;
$args{rect} //= [ 0, 0, 30, 30 ];
$args{background_color} //= '#c0c0c0';
$args{event_mask} //= [ 'structure_notify' ];
$args{name} //= 'Window ' . counter_window();
my $window = $x->root->create_child(%args);
return $window if $dont_map;
$window->name('Window ' . counter_window());
$window->map;
sleep(0.25);
wait_for_map($x);
# We sync with i3 here to make sure $x->input_focus is updated.
sync_with_i3($x);
return $window;
}
# Thin wrapper around open_window which sets window_type to
# _NET_WM_WINDOW_TYPE_UTILITY to make the window floating.
sub open_floating_window {
my ($x, $args) = @_;
my %args = ($args ? %$args : ());
$args{window_type} = $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY');
return open_window($x, \%args);
}
sub open_empty_con {
my ($i3) = @_;
@ -197,6 +296,69 @@ sub focused_ws {
}
}
#
# Sends an I3_SYNC ClientMessage with a random value to the root window.
# i3 will reply with the same value, but, due to the order of events it
# processes, only after all other events are done.
#
# This can be used to ensure the results of a cmd 'focus left' are pushed to
# X11 and that $x->input_focus returns the correct value afterwards.
#
# See also docs/testsuite for a long explanation
#
sub sync_with_i3 {
my ($x) = @_;
# Since we need a (mapped) window for receiving a ClientMessage, we create
# one on the first call of sync_with_i3. It will be re-used in all
# subsequent calls.
if (!defined($_sync_window)) {
$_sync_window = $x->root->create_child(
class => WINDOW_CLASS_INPUT_OUTPUT,
rect => X11::XCB::Rect->new(x => -15, y => -15, width => 10, height => 10 ),
override_redirect => 1,
background_color => '#ff0000',
event_mask => [ 'structure_notify' ],
);
$_sync_window->map;
wait_for_event $x, 0.5, sub { $_[0]->{response_type} == MAP_NOTIFY };
}
my $root = $x->get_root_window();
# Generate a random number to identify this particular ClientMessage.
my $myrnd = int(rand(255)) + 1;
# Generate a ClientMessage, see xcb_client_message_t
my $msg = pack "CCSLLLLLLL",
CLIENT_MESSAGE, # response_type
32, # format
0, # sequence
$root, # destination window
$x->atom(name => 'I3_SYNC')->id,
$_sync_window->id, # data[0]: our own window id
$myrnd, # data[1]: a random value to identify the request
0,
0,
0;
# Send it to the root window -- since i3 uses the SubstructureRedirect
# event mask, it will get the ClientMessage.
$x->send_event(0, $root, EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
# now wait until the reply is here
return wait_for_event $x, 1, sub {
my ($event) = @_;
# TODO: const
return 0 unless $event->{response_type} == 161;
my ($win, $rnd) = unpack "LL", $event->{data};
return ($rnd == $myrnd);
};
}
sub does_i3_live {
my $tree = i3(get_socket_path())->get_tree->recv;
my @nodes = @{$tree->{nodes}};
@ -220,6 +382,10 @@ sub exit_gracefully {
if (!$exited) {
kill(9, $pid) or die "could not kill i3";
}
if ($socketpath =~ m,^/tmp/i3-test-socket-,) {
unlink($socketpath);
}
}
# Gets the socket path from the I3_SOCKET_PATH atom stored on the X11 root window
@ -264,7 +430,7 @@ sub launch_with_config {
# one test case.
my $i3cmd = "exec " . abs_path("../i3") . " -V -d all --disable-signalhandler -c $tmpfile >>$ENV{LOGPATH} 2>&1";
my $process = Proc::Background->new($i3cmd);
sleep 1;
sleep 1.25;
# force update of the cached socket path in lib/i3test
get_socket_path(0);