1package wayland
2
3import (
4 "testing"
5 "time"
6
7 "github.com/bnema/go-wayland-bindings/server/idleinhibit"
8 "github.com/bnema/go-wayland-bindings/server/keyboardshortcutsinhibit"
9 "github.com/bnema/neferwl/internal/ports"
10 "github.com/bnema/wlturbo"
11)
12
13type inhibitorProxy struct {
14 wlturbo.BaseProxy
15 states chan bool
16}
17
18func (p *inhibitorProxy) Dispatch(e *wlturbo.Event) {
19 p.states <- uint32(e.Opcode) == keyboardshortcutsinhibit.ZwpKeyboardShortcutsInhibitorV1EventActive
20}
21
22// clientEvent waits for the next client event of type T.
23func clientEvent[T ports.ClientEvent](t *testing.T, events <-chan ports.ClientEvent) T {
24 t.Helper()
25 deadline := time.After(2 * time.Second)
26 for {
27 select {
28 case ev := <-events:
29 if v, ok := ev.(T); ok {
30 return v
31 }
32 case <-deadline:
33 var zero T
34 t.Fatalf("no %T", zero)
35 return zero
36 }
37 }
38}
39
40func TestShortcutsInhibitor(t *testing.T) {
41 s, events, commands, _, dir := contentServer(t)
42 c := protocolClient(t, s, dir)
43 w, surf, _ := surfaceMapper(t, c, events)()
44 seat := bindProtocol(t, c, "wl_seat")
45 registerProtocol(t, c, seat)
46 mgr := bindProtocol(t, c, "zwp_keyboard_shortcuts_inhibit_manager_v1")
47 in := c.AllocateID()
48 p := &inhibitorProxy{states: make(chan bool, 4)}
49 p.SetID(in)
50 registerWireProxy(c, p)
51 requestProtocol(t, c, mgr, keyboardshortcutsinhibit.ZwpKeyboardShortcutsInhibitManagerV1RequestInhibitShortcuts, in, surf, seat)
52 if v := clientEvent[ports.ShortcutsInhibit](t, events); v != (ports.ShortcutsInhibit{Window: w.ID, Active: true}) {
53 t.Fatal(v)
54 }
55 // Core decides: active, then inactive.
56 state := func(active bool) {
57 t.Helper()
58 commands <- ports.ShortcutsInhibitState{Window: w.ID, Active: active}
59 deadline := time.Now().Add(2 * time.Second)
60 for time.Now().Before(deadline) {
61 if err := c.Roundtrip(); err != nil {
62 t.Fatal(err)
63 }
64 select {
65 case got := <-p.states:
66 if got != active {
67 t.Fatalf("state %v", got)
68 }
69 return
70 case <-time.After(5 * time.Millisecond):
71 }
72 }
73 t.Fatal("no active/inactive event")
74 }
75 state(true)
76 state(false)
77 // Destroying it tells core.
78 requestProtocol(t, c, in, keyboardshortcutsinhibit.ZwpKeyboardShortcutsInhibitorV1RequestDestroy)
79 if v := clientEvent[ports.ShortcutsInhibit](t, events); v.Active {
80 t.Fatal(v)
81 }
82 // A second inhibitor on the same surface is a protocol error.
83 requestProtocol(t, c, mgr, keyboardshortcutsinhibit.ZwpKeyboardShortcutsInhibitManagerV1RequestInhibitShortcuts, c.AllocateID(), surf, seat)
84 requestProtocol(t, c, mgr, keyboardshortcutsinhibit.ZwpKeyboardShortcutsInhibitManagerV1RequestInhibitShortcuts, c.AllocateID(), surf, seat)
85 expectProtocolError(t, c, mgr, uint32(keyboardshortcutsinhibit.ZwpKeyboardShortcutsInhibitManagerV1ErrorAlreadyInhibited))
86}
87
88// Idle inhibitors: several per surface, the window is reported once and
89// released when the last one goes.
90func TestIdleInhibitors(t *testing.T) {
91 s, events, _, _, dir := contentServer(t)
92 c := protocolClient(t, s, dir)
93 w, surf, _ := surfaceMapper(t, c, events)()
94 mgr := bindProtocol(t, c, "zwp_idle_inhibit_manager_v1")
95 a, b := c.AllocateID(), c.AllocateID()
96 registerProtocol(t, c, a)
97 registerProtocol(t, c, b)
98 requestProtocol(t, c, mgr, idleinhibit.ZwpIdleInhibitManagerV1RequestCreateInhibitor, a, surf)
99 requestProtocol(t, c, mgr, idleinhibit.ZwpIdleInhibitManagerV1RequestCreateInhibitor, b, surf)
100 if v := clientEvent[ports.IdleInhibit](t, events); v != (ports.IdleInhibit{Window: w.ID, Active: true}) {
101 t.Fatal(v)
102 }
103 requestProtocol(t, c, a, idleinhibit.ZwpIdleInhibitorV1RequestDestroy)
104 requestProtocol(t, c, b, idleinhibit.ZwpIdleInhibitorV1RequestDestroy)
105 if v := clientEvent[ports.IdleInhibit](t, events); v.Active {
106 t.Fatal(v)
107 }
108 if err := c.Roundtrip(); err != nil {
109 t.Fatal(err)
110 }
111 select {
112 case ev := <-events:
113 if _, ok := ev.(ports.IdleInhibit); ok {
114 t.Fatalf("extra %+v", ev)
115 }
116 default:
117 }
118}