Merge branch 'testsuite' into next
This commit is contained in:
commit
efec744747
|
@ -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 $<
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
|
@ -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 User’s 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 let’s 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? Let’s 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 can’t
|
||||
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
|
|
@ -24,3 +24,4 @@ xmacro(WM_TAKE_FOCUS)
|
|||
xmacro(WM_WINDOW_ROLE)
|
||||
xmacro(I3_SOCKET_PATH)
|
||||
xmacro(I3_CONFIG_PATH)
|
||||
xmacro(I3_SYNC)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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',
|
||||
my $window = open_window($x, {
|
||||
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
|
||||
);
|
||||
|
||||
$window->map;
|
||||
|
||||
sleep 0.25;
|
||||
});
|
||||
|
||||
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,
|
||||
$window = open_window($x, {
|
||||
rect => [ 0, 1000, 30, 30 ],
|
||||
background_color => '#FF0000',
|
||||
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
|
||||
);
|
||||
|
||||
$window->map;
|
||||
|
||||
sleep 0.25;
|
||||
});
|
||||
|
||||
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,
|
||||
$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,
|
||||
$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],
|
||||
$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;
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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 we’re 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;
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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],
|
||||
my $window = open_window($x, {
|
||||
background_color => '#FF0000',
|
||||
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
|
||||
);
|
||||
|
||||
$window->map;
|
||||
|
||||
sleep 0.25;
|
||||
});
|
||||
|
||||
#####################################################################
|
||||
# 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,
|
||||
$window = open_window($x, {
|
||||
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;
|
||||
});
|
||||
|
||||
@docked = get_dock_clients;
|
||||
is(@docked, 1, 'one dock client found');
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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],
|
||||
my $first = open_window($x, {
|
||||
background_color => '#FF0000',
|
||||
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
|
||||
);
|
||||
|
||||
$first->map;
|
||||
|
||||
sleep 0.25;
|
||||
});
|
||||
|
||||
#####################################################################
|
||||
# Open a second dock client
|
||||
#####################################################################
|
||||
|
||||
my $second = $x->root->create_child(
|
||||
class => WINDOW_CLASS_INPUT_OUTPUT,
|
||||
rect => [ 0, 0, 30, 30],
|
||||
my $second = open_window($x, {
|
||||
background_color => '#FF0000',
|
||||
window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
|
||||
);
|
||||
|
||||
$second->map;
|
||||
|
||||
sleep 0.25;
|
||||
});
|
||||
|
||||
#####################################################################
|
||||
# Kill the second dock client
|
||||
|
|
|
@ -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,
|
||||
my $first = open_window($x, {
|
||||
rect => [ 0, 0, 200, 80],
|
||||
background_color => '#FF0000',
|
||||
);
|
||||
|
||||
$first->map;
|
||||
|
||||
sleep 0.25;
|
||||
});
|
||||
|
||||
#####################################################################
|
||||
# Open a second window with 300x90
|
||||
#####################################################################
|
||||
|
||||
my $second = $x->root->create_child(
|
||||
class => WINDOW_CLASS_INPUT_OUTPUT,
|
||||
my $second = open_window($x, {
|
||||
rect => [ 0, 0, 300, 90],
|
||||
background_color => '#00FF00',
|
||||
);
|
||||
|
||||
$second->map;
|
||||
|
||||
sleep 0.25;
|
||||
});
|
||||
|
||||
#####################################################################
|
||||
# Set the parent to floating
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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 it’s 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');
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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)};
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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}};
|
||||
|
|
|
@ -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 $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;
|
||||
}
|
||||
|
||||
my $window = $x->root->create_child(@args);
|
||||
# thin wrapper around wait_for_event which waits for MAP_NOTIFY
|
||||
# make sure to include 'structure_notify' in the window’s 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);
|
||||
|
|
Loading…
Reference in New Issue