use List::Util qw(first);
use Time::HiRes qw(sleep);
use Cwd qw(abs_path);
+use POSIX ':sys_wait_h';
use Scalar::Util qw(blessed);
use SocketActivation;
use i3test::Util qw(slurp);
use Exporter ();
our @EXPORT = qw(
get_workspace_names
+ get_output_for_workspace
get_unused_workspace
fresh_workspace
get_ws_content
cmd
sync_with_i3
exit_gracefully
+ exit_forcefully
workspace_exists
focused_ws
get_socket_path
kill_all_windows
events_for
listen_for_binding
+ is_net_wm_state_focused
);
=head1 NAME
} else {
kill(-9, $i3_pid)
- or $tester->BAIL_OUT("could not kill i3");
+ or $tester->BAIL_OUT("could not kill i3: $!");
waitpid $i3_pid, 0;
}
[ map { $_->{name} } @cons ]
}
+=head2 get_output_for_workspace()
+
+Returns the name of the output on which this workspace resides
+
+ cmd 'focus output fake-1';
+ cmd 'workspace 1';
+ is(get_output_for_workspace('1', 'fake-0', 'Workspace 1 in output fake-0');
+
+=cut
+sub get_output_for_workspace {
+ my $ws_name = shift @_;
+ my $i3 = i3(get_socket_path());
+ my $tree = $i3->get_tree->recv;
+ my @outputs = @{$tree->{nodes}};
+
+ foreach (grep { not $_->{name} =~ /^__/ } @outputs) {
+ my $output = $_->{name};
+ foreach (grep { $_->{name} =~ "content" } @{$_->{nodes}}) {
+ return $output if $_->{nodes}[0]->{name} =~ $ws_name;
+ }
+ }
+}
+
=head2 get_unused_workspace
Returns a workspace name which has not yet been used. See also
if (!$exited) {
kill(9, $pid)
- or $tester->BAIL_OUT("could not kill i3");
+ or $tester->BAIL_OUT("could not kill i3: $!");
}
if ($socketpath =~ m,^/tmp/i3-test-socket-,) {
undef $i3_pid;
}
+=head2 exit_forcefully($pid, [ $signal ])
+
+Tries to exit i3 forcefully by sending a signal (defaults to SIGTERM).
+
+You only need to use this function if you want to test signal handling
+(in which case you must have launched i3 on your own with
+C<launch_with_config>).
+
+ use i3test i3_autostart => 0;
+ my $pid = launch_with_config($config);
+ # …
+ exit_forcefully($pid);
+
+=cut
+sub exit_forcefully {
+ my ($pid, $signal) = @_;
+ $signal ||= 'TERM';
+
+ # Send the given signal to the i3 instance and wait for up to 10s
+ # for it to terminate.
+ kill($signal, $pid)
+ or $tester->BAIL_OUT("could not kill i3: $!");
+ my $status;
+ my $timeout = 10;
+ do {
+ $status = waitpid $pid, WNOHANG;
+
+ if ($status <= 0) {
+ sleep(1);
+ $timeout--;
+ }
+ } while ($status <= 0 && $timeout > 0);
+
+ if ($status <= 0) {
+ kill('KILL', $pid)
+ or $tester->BAIL_OUT("could not kill i3: $!");
+ waitpid $pid, 0;
+ }
+ undef $i3_pid;
+}
+
=head2 get_socket_path([ $cache ])
Gets the socket path from the C<I3_SOCKET_PATH> atom stored on the X11 root
return $command;
}
+=head2 is_net_wm_state_focused
+
+Returns true if the given window has the _NET_WM_STATE_FOCUSED atom.
+
+ ok(is_net_wm_state_focused($window), '_NET_WM_STATE_FOCUSED set');
+
+=cut
+sub is_net_wm_state_focused {
+ my ($window) = @_;
+
+ sync_with_i3;
+ my $atom = $x->atom(name => '_NET_WM_STATE_FOCUSED');
+ my $cookie = $x->get_property(
+ 0,
+ $window->{id},
+ $x->atom(name => '_NET_WM_STATE')->id,
+ GET_PROPERTY_TYPE_ANY,
+ 0,
+ 4096
+ );
+
+ my $reply = $x->get_property_reply($cookie->{sequence});
+ my $len = $reply->{length};
+ return 0 if $len == 0;
+
+ my @atoms = unpack("L$len", $reply->{value});
+ for (my $i = 0; $i < $len; $i++) {
+ return 1 if $atoms[$i] == $atom->id;
+ }
+
+ return 0;
+}
+
+
=head1 AUTHOR
Michael Stapelberg <michael@i3wm.org>