1package wayland
2
3import (
4 "testing"
5 "time"
6
7 "github.com/bnema/neferwl/internal/ports"
8
9 "github.com/bnema/go-wayland-bindings/server/wayland"
10 "github.com/bnema/go-wayland-bindings/server/xdgshell"
11 "github.com/bnema/wlturbo"
12 "golang.org/x/sys/unix"
13)
14
15// pressSerials records the serial of each pointer button event.
16type pressSerials struct {
17 wlturbo.BaseProxy
18 serials chan uint32
19}
20
21func (p *pressSerials) Dispatch(e *wlturbo.Event) {
22 if e.Opcode == uint16(wayland.PointerEventButton) {
23 p.serials <- e.Uint32()
24 }
25}
26
27func TestToplevelMoveRequest(t *testing.T) {
28 s, events, commands, dir := lifecycleServer(t)
29 c := protocolClient(t, s, dir)
30 seat := bindVersion(t, c, "wl_seat", 8)
31 registerProtocol(t, c, seat)
32 pointer := c.AllocateID()
33 p := &pressSerials{serials: make(chan uint32, 4)}
34 p.SetID(pointer)
35 registerWireProxy(c, p)
36 requestProtocol(t, c, seat, wayland.SeatRequestGetPointer, pointer)
37 comp := bindProtocol(t, c, "wl_compositor")
38 wm := bindProtocol(t, c, "xdg_wm_base")
39 shm := bindProtocol(t, c, "wl_shm")
40 registerProtocol(t, c, shm)
41 surf, xdg := c.AllocateID(), c.AllocateID()
42 requestProtocol(t, c, comp, wayland.CompositorRequestCreateSurface, surf)
43 registerProtocol(t, c, surf)
44 requestProtocol(t, c, wm, xdgshell.WmBaseRequestGetXdgSurface, xdg, surf)
45 configured := make(chan uint32, 1)
46 xp := &configureProxy{serial: configured}
47 xp.SetID(xdg)
48 registerWireProxy(c, xp)
49 top := c.AllocateID()
50 registerProtocol(t, c, top)
51 requestProtocol(t, c, xdg, xdgshell.SurfaceRequestGetToplevel, top)
52 // Before mapping, a move request is ignored.
53 requestProtocol(t, c, top, xdgshell.ToplevelRequestMove, seat, uint32(0))
54 requestProtocol(t, c, surf, wayland.SurfaceRequestCommit)
55 if err := c.Roundtrip(); err != nil {
56 t.Fatal(err)
57 }
58 requestProtocol(t, c, xdg, xdgshell.SurfaceRequestAckConfigure, <-configured)
59 fd, err := unix.MemfdCreate("move-buffer", 0)
60 if err != nil {
61 t.Fatal(err)
62 }
63 defer unix.Close(fd)
64 if err := unix.Ftruncate(fd, 4); err != nil {
65 t.Fatal(err)
66 }
67 pool, buffer := c.AllocateID(), c.AllocateID()
68 registerProtocol(t, c, pool)
69 registerProtocol(t, c, buffer)
70 if err := wireRequest(c, shm, uint16(wayland.ShmRequestCreatePool), []int{fd}, pool, int32(4)); err != nil {
71 t.Fatal(err)
72 }
73 requestProtocol(t, c, pool, wayland.ShmPoolRequestCreateBuffer, buffer, int32(0), int32(1), int32(1), int32(4), uint32(0))
74 requestProtocol(t, c, surf, wayland.SurfaceRequestAttach, buffer, int32(0), int32(0))
75 requestProtocol(t, c, surf, wayland.SurfaceRequestCommit)
76 if err := c.Roundtrip(); err != nil {
77 t.Fatal(err)
78 }
79 w := mapped(t, events, 2*time.Second)
80 commands <- ports.PointerFocus{ID: w.ID, X: 1, Y: 1}
81 commands <- ports.PointerButtonTo{ID: w.ID, Button: 0x110, Pressed: true, TimeMsec: 1}
82 var serial uint32
83 deadline := time.After(2 * time.Second)
84 for serial == 0 {
85 if err := c.Roundtrip(); err != nil {
86 t.Fatal(err)
87 }
88 select {
89 case serial = <-p.serials:
90 case <-deadline:
91 t.Fatal("no button")
92 default:
93 }
94 }
95 // A stale serial emits nothing.
96 requestProtocol(t, c, top, xdgshell.ToplevelRequestMove, seat, serial-1)
97 requestProtocol(t, c, top, xdgshell.ToplevelRequestResize, seat, serial+7, uint32(xdgshell.ToplevelResizeEdgeRight))
98 // The current press starts a move, then a resize.
99 requestProtocol(t, c, top, xdgshell.ToplevelRequestMove, seat, serial)
100 requestProtocol(t, c, top, xdgshell.ToplevelRequestResize, seat, serial, uint32(xdgshell.ToplevelResizeEdgeBottomLeft))
101 if err := c.Roundtrip(); err != nil {
102 t.Fatal(err)
103 }
104 var got []ports.WindowMoveRequest
105 for len(got) < 2 {
106 ev := waitEvent(t, events, 2*time.Second)
107 if v, ok := ev.(ports.WindowMoveRequest); ok {
108 got = append(got, v)
109 }
110 }
111 if got[0] != (ports.WindowMoveRequest{ID: w.ID}) || got[1] != (ports.WindowMoveRequest{ID: w.ID, Resize: true, Edges: ports.ResizeBottom | ports.ResizeLeft}) {
112 t.Fatalf("%+v", got)
113 }
114 select {
115 case ev := <-events:
116 if _, ok := ev.(ports.WindowMoveRequest); ok {
117 t.Fatalf("extra request %+v", ev)
118 }
119 default:
120 }
121}
122
123func TestToplevelResizeInvalidEdge(t *testing.T) {
124 s, _, _, dir := lifecycleServer(t)
125 c := protocolClient(t, s, dir)
126 seat := bindVersion(t, c, "wl_seat", 8)
127 registerProtocol(t, c, seat)
128 comp := bindProtocol(t, c, "wl_compositor")
129 wm := bindProtocol(t, c, "xdg_wm_base")
130 surf, xdg, top := c.AllocateID(), c.AllocateID(), c.AllocateID()
131 requestProtocol(t, c, comp, wayland.CompositorRequestCreateSurface, surf)
132 registerProtocol(t, c, surf)
133 requestProtocol(t, c, wm, xdgshell.WmBaseRequestGetXdgSurface, xdg, surf)
134 registerProtocol(t, c, xdg)
135 requestProtocol(t, c, xdg, xdgshell.SurfaceRequestGetToplevel, top)
136 registerProtocol(t, c, top)
137 requestProtocol(t, c, top, xdgshell.ToplevelRequestResize, seat, uint32(1), uint32(3))
138 expectProtocolError(t, c, top, uint32(xdgshell.ToplevelErrorInvalidResizeEdge))
139}