1package headlessinput
2
3import (
4 "context"
5 "github.com/bnema/neferwl/internal/adapters/xkb"
6 "github.com/bnema/neferwl/internal/logging"
7 "github.com/bnema/neferwl/internal/ports"
8 "strings"
9 "testing"
10)
11
12func TestScript(t *testing.T) {
13 for _, tt := range []struct {
14 line string
15 names []string
16 codes []uint32
17 }{
18 {"type Hi!", []string{"Shift_L", "H", "H", "Shift_L", "i", "i", "Shift_L", "exclam", "exclam", "Shift_L"}, nil},
19 {"key Shift+A", []string{"Shift_L", "A", "A", "Shift_L"}, []uint32{42, 30, 30, 42}},
20 {"key Super+Return", []string{"Super_L", "Return", "Return", "Super_L"}, []uint32{125, 28, 28, 125}},
21 } {
22 t.Run(tt.line, func(t *testing.T) {
23 km, err := xkb.New(xkb.RMLVO{Layout: "us"})
24 if err != nil {
25 if strings.Contains(err.Error(), "libxkbcommon.so.0") {
26 t.Skip(err)
27 }
28 t.Fatal(err)
29 }
30 script := make(chan string, 1)
31 script <- tt.line
32 close(script)
33 input := make(chan ports.InputEvent, 32)
34 if err := Run(context.Background(), km, nil, script, input, nil, nil, logging.For(context.Background(), "input")); err != nil {
35 t.Fatal(err)
36 }
37 if len(input) != len(tt.names) {
38 t.Fatalf("events=%d want %d", len(input), len(tt.names))
39 }
40 for i, name := range tt.names {
41 ev := (<-input).(ports.KeyEvent)
42 if ev.Keysym != name {
43 t.Errorf("event %d: %s want %s", i, ev.Keysym, name)
44 }
45 if tt.codes != nil && ev.Keycode != tt.codes[i] {
46 t.Errorf("code %d: %d", i, ev.Keycode)
47 }
48 if tt.line == "key Super+Return" && i == 1 && ev.Mods&ports.ModSuper == 0 {
49 t.Error("Return missing Super")
50 }
51 if tt.line == "key Super+Return" && i == 3 && ev.Mods&ports.ModSuper != 0 {
52 t.Error("Super not released")
53 }
54 }
55 })
56 }
57}
58
59func TestPointerScript(t *testing.T) {
60 km, err := xkb.New(xkb.RMLVO{Layout: "us"})
61 if err != nil {
62 t.Skip(err)
63 }
64 script := make(chan string, 8)
65 for _, line := range []string{"move 12.5 45", "click", "down right", "up right", "swipe sideways", "swipe left", "swipe4 up"} {
66 script <- line
67 }
68 close(script)
69 input := make(chan ports.InputEvent, 32)
70 if err := Run(context.Background(), km, nil, script, input, nil, nil, logging.For(context.Background(), "input")); err != nil {
71 t.Fatal(err)
72 }
73 if len(input) != 5+2*(swipeSteps+2) {
74 t.Fatalf("events: %d", len(input))
75 }
76 m := (<-input).(ports.PointerMotion)
77 if m.X != 12.5 || m.Y != 45 {
78 t.Fatal(m)
79 }
80 for _, want := range []ports.PointerButton{{Button: 0x110, Pressed: true}, {Button: 0x110}, {Button: 0x111, Pressed: true}, {Button: 0x111}} {
81 got := (<-input).(ports.PointerButton)
82 if got.Button != want.Button || got.Pressed != want.Pressed {
83 t.Fatalf("got %+v want %+v", got, want)
84 }
85 }
86 begin := (<-input).(ports.SwipeBegin)
87 if begin.Fingers != 3 {
88 t.Fatal(begin)
89 }
90 last := begin.Time
91 for range swipeSteps {
92 u := (<-input).(ports.SwipeUpdate)
93 if u.DX >= 0 || u.DY != 0 || u.Time <= last {
94 t.Fatalf("update %+v after %v", u, last)
95 }
96 last = u.Time
97 }
98 if e := (<-input).(ports.SwipeEnd); e.Cancelled || e.Time != last {
99 t.Fatal(e)
100 }
101 if b := (<-input).(ports.SwipeBegin); b.Fingers != 4 {
102 t.Fatal(b)
103 }
104 if u := (<-input).(ports.SwipeUpdate); u.DY >= 0 || u.DX != 0 {
105 t.Fatal(u)
106 }
107}
108
109// The cursor is placed in target pixels: after the output transform.
110func TestMoveCursorTarget(t *testing.T) {
111 km, err := xkb.New(xkb.RMLVO{Layout: "us"})
112 if err != nil {
113 t.Skip(err)
114 }
115 layouts := make(chan ports.Layout)
116 script := make(chan string)
117 input := make(chan ports.InputEvent, 4)
118 type pos struct {
119 out string
120 x, y float64
121 }
122 moved := make(chan pos, 1)
123 done := make(chan error, 1)
124 go func() {
125 done <- Run(context.Background(), km, nil, script, input, layouts, func(o string, x, y float64) { moved <- pos{o, x, y} }, logging.For(context.Background(), "input"))
126 }()
127 // Unbuffered: the layout is taken before the move is read.
128 layouts <- ports.Layout{{Info: ports.OutputInfo{Name: "A", Width: 200, Height: 100}, Scale: 1, Transform: 1, Width: 100, Height: 200}}
129 script <- "move 10 20"
130 if got, want := <-moved, (pos{"A", 20, 90}); got != want {
131 t.Fatalf("got %+v want %+v", got, want)
132 }
133 close(script)
134 if err := <-done; err != nil {
135 t.Fatal(err)
136 }
137}