aboutsummaryrefslogtreecommitdiff
path: root/testcases/t/158-wm_take_focus.t
blob: e9a32cd00ac24d35ffb6b9aa4e995f471f4fbc26 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!perl
# vim:ts=4:sw=4:expandtab
#
# Please read the following documents before working on tests:
# • https://build.i3wm.org/docs/testsuite.html
#   (or docs/testsuite)
#
# • https://build.i3wm.org/docs/lib-i3test.html
#   (alternatively: perldoc ./testcases/lib/i3test.pm)
#
# • https://build.i3wm.org/docs/ipc.html
#   (or docs/ipc)
#
# • https://i3wm.org/downloads/modern_perl_a4.pdf
#   (unless you are already familiar with Perl)
#
# Tests if the WM_TAKE_FOCUS protocol is correctly handled by i3
#
# For more information on the protocol and input handling, see:
# https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7
#
use i3test;

sub recv_take_focus {
    my ($window) = @_;

    # sync_with_i3 will send a ClientMessage to i3 and i3 will send the same
    # payload back to $window->id.
    my $myrnd = sync_with_i3(
        window_id => $window->id,
        dont_wait_for_event => 1,
    );

    # We check whether the first received message has the correct payload — if
    # not, the received message was a WM_TAKE_FOCUS message.
    my $first_event_is_clientmessage;
    wait_for_event 2, sub {
        my ($event) = @_;
        # TODO: const
        return 0 unless $event->{response_type} == 161;

        my ($win, $rnd) = unpack "LL", $event->{data};
        if (!defined($first_event_is_clientmessage)) {
            $first_event_is_clientmessage = ($rnd == $myrnd);
        }
        return ($rnd == $myrnd);
    };

    return !$first_event_is_clientmessage;
}

subtest 'Window without WM_TAKE_FOCUS', sub {
    my $ws = fresh_workspace;

    my $window = open_window;

    ok(!recv_take_focus($window), 'did not receive ClientMessage');
    ok(is_net_wm_state_focused($window), '_NET_WM_STATE_FOCUSED set');

    my ($nodes) = get_ws_content($ws);
    my $con = shift @$nodes;
    ok($con->{focused}, 'con is focused');

    done_testing;
};

# https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7
# > Clients using the Globally Active model can only use a SetInputFocus request
# > to acquire the input focus when they do not already have it on receipt of one
# > of the following events:
# > * ButtonPress
# > * ButtonRelease
# > * Passive-grabbed KeyPress
# > * Passive-grabbed KeyRelease
#
# Since managing a window happens on a MapNotify (which is absent from this
# list), the window cannot accept input focus, so we should not try to focus
# the window at all.
subtest 'Window with WM_TAKE_FOCUS and without InputHint', sub {
    my $ws = fresh_workspace;

    my $take_focus = $x->atom(name => 'WM_TAKE_FOCUS');

    my $window = open_window({
        dont_map => 1,
        protocols => [ $take_focus ],
    });

    # add an (empty) WM_HINTS property without the InputHint
    $window->delete_hint('input');

    $window->map;

    ok(!recv_take_focus($window), 'did not receive ClientMessage');
    ok(is_net_wm_state_focused($window), '_NET_WM_STATE_FOCUSED set');

    my ($nodes) = get_ws_content($ws);
    my $con = shift @$nodes;
    ok($con->{focused}, 'con is focused');

    done_testing;
};

# If the InputHint is unspecified, i3 should use the simpler method of focusing
# the window directly rather than using the WM_TAKE_FOCUS protocol.
# XXX: The code paths for an unspecified and set InputHint are
# nearly identical presently, so this is currently used also as a proxy test
# for the latter case.
subtest 'Window with WM_TAKE_FOCUS and unspecified InputHint', sub {
    my $ws = fresh_workspace;

    my $take_focus = $x->atom(name => 'WM_TAKE_FOCUS');

    my $window = open_window({ protocols => [ $take_focus ] });

    ok(!recv_take_focus($window), 'did not receive ClientMessage');
    ok(is_net_wm_state_focused($window), '_NET_WM_STATE_FOCUSED set');

    my ($nodes) = get_ws_content($ws);
    my $con = shift @$nodes;
    ok($con->{focused}, 'con is focused');

    done_testing;
};

done_testing;