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");
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');
###########################################################################
$swindow->fullscreen(0);
-sleep 0.25;
+sync_with_i3($x);
is(fullscreen_windows(), 0, 'amount of fullscreen windows after disabling');
# 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;
# 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;
+sync_with_i3($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");
my $top = open_standard_window($x);
my $mid = open_standard_window($x);
my $bottom = open_standard_window($x);
-sleep 0.25;
+##sleep 0.25;
diag("top id = " . $top->id);
diag("mid id = " . $mid->id);
# 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 $tiled_left = open_standard_window($x);
my $tiled_right = open_standard_window($x);
-sleep 0.25;
+sync_with_i3($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'),
-);
-
-isa_ok($window, 'X11::XCB::Window');
-
-$window->map;
+my $window = open_standard_window($x, undef, 1);
+sync_with_i3($x);
-sleep 1;
-sleep 0.25;
is($x->input_focus, $window->id, 'floating window focused');
$window->unmap;
-sleep 0.25;
+# TODO: wait for unmap
+sync_with_i3($x);
is($x->input_focus, $focus, 'Focus correctly restored');
#####################################################################
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);
#####################################################################
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);
my $msg = shift;
cmd $msg;
+ sync_with_i3($x);
return $x->input_focus;
}
#!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);
#####################################################################
# 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_standard_window($x, undef, 1);
# 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
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;
# 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;
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');
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');
# 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_standard_window($x);
my $content = get_ws_content($tmp);
ok(@{$content} == 1, 'window mapped');
my $win = $content->[0];
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');
$win->hints->aspect($aspect);
$x->flush;
-sleep 0.25;
+sync_with_i3($x);
my $rect = $win->rect;
my $ar = $rect->width / $rect->height;
my $first = open_standard_window($x);
my $second = open_standard_window($x);
+sync_with_i3($x);
+
is($x->input_focus, $second->id, 'second window focused');
cmd 'floating enable';
$second = open_standard_window($x); # window 3
my $third = open_standard_window($x); # window 4
+sync_with_i3($x);
+
is($x->input_focus, $third->id, 'last container focused');
cmd 'floating enable';
cmd '[id="' . $second->id . '"] focus';
+sync_with_i3($x);
+
is($x->input_focus, $second->id, 'second con focused');
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');
$second = open_standard_window($x, '#00ff00'); # window 6
my $third = open_standard_window($x, '#0000ff'); # window 7
+sync_with_i3($x);
+
is($x->input_focus, $third->id, 'last container focused');
cmd 'floating enable';
cmd '[id="' . $second->id . '"] focus';
+sync_with_i3($x);
+
is($x->input_focus, $second->id, 'second con focused');
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');
$second = open_standard_window($x, '#00ff00'); # window 6
$third = open_standard_window($x, '#0000ff'); # window 7
+sync_with_i3($x);
+
is($x->input_focus, $third->id, 'last container focused');
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');
$first = open_standard_window($x, '#ff0000'); # window 8
$second = open_standard_window($x, '#00ff00'); # window 9
+sync_with_i3($x);
+
is($x->input_focus, $second->id, 'second container focused');
cmd 'floating enable';
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');
$second = open_standard_window($x, '#00ff00', 1); # window 11
$third = open_standard_window($x, '#0000ff', 1); # 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');
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_standard_window($x, undef, 1);
ok($window->mapped, 'Window is mapped');
# switch to a different workspace, see if the window is still mapped?
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_standard_window($x, undef, 1);
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');
use i3test;
use X11::XCB qw(:all);
-use Time::HiRes qw(sleep);
BEGIN {
use_ok('X11::XCB::Window');
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_standard_window($x, undef, 1);
ok($window->mapped, 'Window is mapped');
my $ws = get_ws($tmp);
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_standard_window($x);
($nodes, $focus) = get_ws_content($tmp);
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_standard_window($x, undef, 1);
ok($window->mapped, 'Window is mapped');
$ws = get_ws($tmp);
# bug introduced by 77d0d42ed2d7ac8cafe267c92b35a81c1b9491eb
use i3test;
use X11::XCB qw(:all);
-use Time::HiRes qw(sleep);
BEGIN {
use_ok('X11::XCB::Window');
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;
+
+sync_with_i3($x);
diag("left = " . $left->id . ", mid = " . $mid->id . ", right = " . $right->id);
cmd 'split v';
my $top = open_standard_window($x);
-sleep 0.25;
my $bottom = open_standard_window($x);
-sleep 0.25;
+
+sync_with_i3($x);
diag("top = " . $top->id . ", bottom = " . $bottom->id);
cmd 'split v';
$top = open_standard_window($x);
-sleep 0.25;
$bottom = open_standard_window($x);
-sleep 0.25;
cmd 'split h';
cmd 'layout stacked';
$tmp = fresh_workspace;
$top = open_standard_window($x);
-sleep 0.25;
cmd 'floating enable';
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;
cmd 'move before v';
cmd 'move after h';
# vim:ts=4:sw=4:expandtab
#
use X11::XCB qw(:all);
-use Time::HiRes qw(sleep);
use i3test;
BEGIN {
my $tmp = fresh_workspace;
my $left = open_standard_window($x);
-sleep 0.25;
my $mid = open_standard_window($x);
-sleep 0.25;
cmd 'split v';
my $bottom = open_standard_window($x);
-sleep 0.25;
my ($nodes, $focus) = get_ws_content($tmp);
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_standard_window($x, undef, 1);
ok($window->mapped, 'Window is mapped');
($nodes, $focus) = get_ws_content($tmp);
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;
# go to workspace level
cmd 'level up';
# open a tiling window on the first workspace
open_standard_window($x);
-sleep 0.25;
+#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;
+#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');
cmp_ok($absolute->{height}, '>', 150, 'i3 raised the height');
cmd 'floating toggle';
-sleep 0.25;
+sync_with_i3($x);
($absolute, $top) = $window->rect;
# verify that the third window has the focus
-sleep 0.25;
+sync_with_i3($x);
is($x->input_focus, $third->id, 'third window focused');
$window->add_hint('urgency');
-sleep 0.25;
+sync_with_i3($x);
does_i3_live;
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_standard_window($x);
-$window->name('Window 1');
-$window->map;
+sync_with_i3($x);
diag('window mapped');
-sleep 0.5;
-
is($window->state, ICCCM_WM_STATE_NORMAL, 'WM_STATE normal');
$window->unmap;
-sleep 0.5;
+# TODO: wait for unmapnotify
+sync_with_i3($x);
is($window->state, ICCCM_WM_STATE_WITHDRAWN, 'WM_STATE withdrawn');
my $first = open_standard_window($x);
my $second = open_standard_window($x);
+sync_with_i3($x);
+
is($x->input_focus, $second->id, 'second window focused');
ok(@{get_ws_content($tmp)} == 2, 'two containers opened');
isnt($content[0]->{layout}, 'stacked', 'layout not stacked');
$first = open_standard_window($x);
$second = open_standard_window($x);
+sync_with_i3($x);
+
is($x->input_focus, $second->id, 'second window focused');
my @content = @{get_ws_content($tmp)};
ok(@content == 1, 'one con at workspace level');
cmd 'focus parent';
$third = open_standard_window($x);
+
+sync_with_i3($x);
+
is($x->input_focus, $third->id, 'third window focused');
cmd 'focus left';
my $tester = Test::Builder->new();
my $_cached_socket_path = undef;
+my $_sync_window = undef;
my $tmp_socket_path = undef;
BEGIN {
goto \&Exporter::import;
}
+#
+# 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) = @_;
+
+ 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 ($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 { say STDERR "timeout"; $cv->send(0) };
+
+ my $result = $cv->recv;
+ return $result;
+}
+
sub open_standard_window {
my ($x, $color, $floating) = @_;
$window->name('Window ' . counter_window());
$window->map;
- # wait for the mapped event with a timeout of 0.25s
- 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} == MAP_NOTIFY) {
- $cv->send(0)
- }
- }
- };
-
- 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 0.25s
- my $timeout = AE::timer 0.5, 0, sub { say STDERR "timeout"; $cv->send(1) };
-
- my $result = $cv->recv;
+ wait_for_event $x, 0.5, sub { $_[0]->{response_type} == MAP_NOTIFY };
return $window;
}
}
}
+#
+# 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->root->id;
+ # 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}};