1package wayland
2
3import (
4 "fmt"
5 "testing"
6 "time"
7
8 "github.com/bnema/go-wayland-bindings/server/viewporter"
9 "github.com/bnema/go-wayland-bindings/server/wayland"
10)
11
12// A queued attachment that was destroyed cannot apply either its new crop or
13// its scale to the buffer retained by a synchronized child.
14func TestQueuedDestroyedAttachmentRetainsScaleAndCrop(t *testing.T) {
15 s, events, _, contents, dir := contentServer(t)
16 c := protocolClient(t, s, dir)
17 w, root, _ := surfaceMapper(t, c, events)()
18 drainContents(contents)
19 comp := bindVersion(t, c, "wl_compositor", 4)
20 subc := bindProtocol(t, c, "wl_subcompositor")
21 vpm := bindProtocol(t, c, "wp_viewporter")
22 child := c.AllocateID()
23 requestProtocol(t, c, comp, wayland.CompositorRequestCreateSurface, child)
24 registerProtocol(t, c, child)
25 sub := c.AllocateID()
26 requestProtocol(t, c, subc, wayland.SubcompositorRequestGetSubsurface, sub, child, root)
27 registerProtocol(t, c, sub)
28 vp := c.AllocateID()
29 requestProtocol(t, c, vpm, viewporter.WpViewporterRequestGetViewport, vp, child)
30 registerProtocol(t, c, vp)
31 retained, discarded := shmBuffer(t, c), shmBuffer(t, c)
32 requestProtocol(t, c, vp, viewporter.WpViewportRequestSetSource, int32(0), int32(0), int32(256), int32(256))
33 requestProtocol(t, c, child, wayland.SurfaceRequestAttach, retained, int32(0), int32(0))
34 requestProtocol(t, c, child, wayland.SurfaceRequestCommit)
35 requestProtocol(t, c, root, wayland.SurfaceRequestCommit)
36 roundtrip(t, c)
37 drainContents(contents)
38 requestProtocol(t, c, child, wayland.SurfaceRequestSetBufferScale, int32(2))
39 requestProtocol(t, c, child, wayland.SurfaceRequestAttach, discarded, int32(0), int32(0))
40 requestProtocol(t, c, child, wayland.SurfaceRequestCommit)
41 requestProtocol(t, c, discarded, wayland.BufferRequestDestroy)
42 requestProtocol(t, c, root, wayland.SurfaceRequestCommit)
43 roundtrip(t, c)
44 deadline := time.After(2 * time.Second)
45 for {
46 select {
47 case got := <-contents:
48 if got.ID != w.ID {
49 continue
50 }
51 if len(got.Children) != 1 || got.Children[0].Source != ([4]float32{0, 0, 1, 1}) || got.Children[0].LogicalW != 1 {
52 t.Fatalf("retained buffer changed: %+v", got.Children)
53 }
54 return
55 case <-deadline:
56 t.Fatal("retained publication missing")
57 }
58 }
59}
60
61// A scale-only commit validates the retained buffer; same-buffer reattaches
62// still advance its version.
63func TestScaleOnlyRetainedViewportAndSameBufferReattach(t *testing.T) {
64 s, events, _, contents, dir := contentServer(t)
65 c := protocolClient(t, s, dir)
66 w, root, _ := surfaceMapper(t, c, events)()
67 drainContents(contents)
68 comp := bindVersion(t, c, "wl_compositor", 4)
69 subc := bindProtocol(t, c, "wl_subcompositor")
70 vpm := bindProtocol(t, c, "wp_viewporter")
71 child := c.AllocateID()
72 requestProtocol(t, c, comp, wayland.CompositorRequestCreateSurface, child)
73 registerProtocol(t, c, child)
74 sub := c.AllocateID()
75 requestProtocol(t, c, subc, wayland.SubcompositorRequestGetSubsurface, sub, child, root)
76 registerProtocol(t, c, sub)
77 vp := c.AllocateID()
78 requestProtocol(t, c, vpm, viewporter.WpViewporterRequestGetViewport, vp, child)
79 registerProtocol(t, c, vp)
80 buf := shmBuffer(t, c)
81 requestProtocol(t, c, child, wayland.SurfaceRequestAttach, buf, int32(0), int32(0))
82 requestProtocol(t, c, child, wayland.SurfaceRequestCommit)
83 requestProtocol(t, c, root, wayland.SurfaceRequestCommit)
84 roundtrip(t, c)
85 var first uint64
86 deadline := time.After(2 * time.Second)
87 for first == 0 {
88 select {
89 case got := <-contents:
90 if got.ID == w.ID && len(got.Children) == 1 {
91 first = got.Children[0].Version
92 }
93 case <-deadline:
94 t.Fatal("initial child missing")
95 }
96 }
97 requestProtocol(t, c, child, wayland.SurfaceRequestAttach, buf, int32(0), int32(0))
98 requestProtocol(t, c, child, wayland.SurfaceRequestCommit)
99 requestProtocol(t, c, root, wayland.SurfaceRequestCommit)
100 roundtrip(t, c)
101 deadline = time.After(2 * time.Second)
102 for {
103 select {
104 case got := <-contents:
105 if got.ID != w.ID || len(got.Children) != 1 {
106 continue
107 }
108 if got.Children[0].Version != first+1 {
109 t.Fatalf("same-buffer version %d, want %d", got.Children[0].Version, first+1)
110 }
111 goto reattached
112 case <-deadline:
113 t.Fatal("same-buffer publication missing")
114 }
115 }
116reattached:
117 requestProtocol(t, c, vp, viewporter.WpViewportRequestSetSource, int32(0), int32(0), int32(256), int32(256))
118 requestProtocol(t, c, child, wayland.SurfaceRequestCommit)
119 requestProtocol(t, c, root, wayland.SurfaceRequestCommit)
120 roundtrip(t, c)
121 requestProtocol(t, c, child, wayland.SurfaceRequestSetBufferScale, int32(2))
122 requestProtocol(t, c, child, wayland.SurfaceRequestCommit)
123 requestProtocol(t, c, root, wayland.SurfaceRequestCommit)
124 expectProtocolError(t, c, vp, uint32(viewporter.WpViewportErrorOutOfBuffer))
125}
126
127// A destroyed queued buffer does not suppress the later, valid attachment.
128func TestQueuedDestroyedThenNewBuffer(t *testing.T) {
129 s, events, _, contents, dir := contentServer(t)
130 c := protocolClient(t, s, dir)
131 w, root, _ := surfaceMapper(t, c, events)()
132 drainContents(contents)
133 comp := bindVersion(t, c, "wl_compositor", 4)
134 subc := bindProtocol(t, c, "wl_subcompositor")
135 vpm := bindProtocol(t, c, "wp_viewporter")
136 child := c.AllocateID()
137 requestProtocol(t, c, comp, wayland.CompositorRequestCreateSurface, child)
138 registerProtocol(t, c, child)
139 sub := c.AllocateID()
140 requestProtocol(t, c, subc, wayland.SubcompositorRequestGetSubsurface, sub, child, root)
141 registerProtocol(t, c, sub)
142 vp := c.AllocateID()
143 requestProtocol(t, c, vpm, viewporter.WpViewporterRequestGetViewport, vp, child)
144 registerProtocol(t, c, vp)
145 first, destroyed, next := shmBuffer(t, c), shmBuffer(t, c), shmBuffer(t, c)
146 requestProtocol(t, c, vp, viewporter.WpViewportRequestSetSource, int32(0), int32(0), int32(256), int32(256))
147 requestProtocol(t, c, child, wayland.SurfaceRequestAttach, first, int32(0), int32(0))
148 requestProtocol(t, c, child, wayland.SurfaceRequestCommit)
149 requestProtocol(t, c, root, wayland.SurfaceRequestCommit)
150 roundtrip(t, c)
151 drainContents(contents)
152 requestProtocol(t, c, child, wayland.SurfaceRequestSetBufferScale, int32(2))
153 requestProtocol(t, c, child, wayland.SurfaceRequestAttach, destroyed, int32(0), int32(0))
154 requestProtocol(t, c, child, wayland.SurfaceRequestCommit)
155 requestProtocol(t, c, destroyed, wayland.BufferRequestDestroy)
156 requestProtocol(t, c, child, wayland.SurfaceRequestSetBufferScale, int32(1))
157 requestProtocol(t, c, child, wayland.SurfaceRequestAttach, next, int32(0), int32(0))
158 requestProtocol(t, c, child, wayland.SurfaceRequestCommit)
159 requestProtocol(t, c, root, wayland.SurfaceRequestCommit)
160 roundtrip(t, c)
161 deadline := time.After(2 * time.Second)
162 for {
163 select {
164 case got := <-contents:
165 if got.ID != w.ID || len(got.Children) != 1 {
166 continue
167 }
168 ch := got.Children[0]
169 if ch.Version < 2 {
170 continue
171 }
172 if ch.Version != 2 || ch.LogicalW != 1 || ch.Source != ([4]float32{0, 0, 1, 1}) {
173 t.Fatalf("later buffer did not apply: %+v", ch)
174 }
175 return
176 case <-deadline:
177 t.Fatal("later buffer not published")
178 }
179 }
180}
181
182// A destroyed viewport in an earlier queued update must not replace the new
183// viewport or steal its protocol errors.
184func TestQueuedViewportRecreatedErrorObject(t *testing.T) {
185 for _, oversized := range []bool{false, true} {
186 t.Run(fmt.Sprintf("oversized=%v", oversized), func(t *testing.T) {
187 s, events, _, contents, dir := contentServer(t)
188 c := protocolClient(t, s, dir)
189 _, root, _ := surfaceMapper(t, c, events)()
190 drainContents(contents)
191 comp := bindVersion(t, c, "wl_compositor", 4)
192 subc := bindProtocol(t, c, "wl_subcompositor")
193 vpm := bindProtocol(t, c, "wp_viewporter")
194 child := c.AllocateID()
195 requestProtocol(t, c, comp, wayland.CompositorRequestCreateSurface, child)
196 registerProtocol(t, c, child)
197 sub := c.AllocateID()
198 requestProtocol(t, c, subc, wayland.SubcompositorRequestGetSubsurface, sub, child, root)
199 registerProtocol(t, c, sub)
200 old := c.AllocateID()
201 requestProtocol(t, c, vpm, viewporter.WpViewporterRequestGetViewport, old, child)
202 registerProtocol(t, c, old)
203 buf := shmBuffer(t, c)
204 requestProtocol(t, c, old, viewporter.WpViewportRequestSetSource, int32(0), int32(0), int32(256), int32(256))
205 requestProtocol(t, c, child, wayland.SurfaceRequestAttach, buf, int32(0), int32(0))
206 requestProtocol(t, c, child, wayland.SurfaceRequestCommit) // old viewport queued
207 requestProtocol(t, c, old, viewporter.WpViewportRequestDestroy)
208 current := c.AllocateID()
209 requestProtocol(t, c, vpm, viewporter.WpViewporterRequestGetViewport, current, child)
210 registerProtocol(t, c, current)
211 width := int32(256)
212 if oversized {
213 width = 512
214 }
215 requestProtocol(t, c, current, viewporter.WpViewportRequestSetSource, int32(0), int32(0), width, int32(256))
216 requestProtocol(t, c, child, wayland.SurfaceRequestAttach, buf, int32(0), int32(0))
217 requestProtocol(t, c, child, wayland.SurfaceRequestCommit)
218 requestProtocol(t, c, root, wayland.SurfaceRequestCommit)
219 if oversized {
220 expectProtocolError(t, c, current, uint32(viewporter.WpViewportErrorOutOfBuffer))
221 return
222 }
223 roundtrip(t, c)
224 requestProtocol(t, c, child, wayland.SurfaceRequestCommit)
225 requestProtocol(t, c, root, wayland.SurfaceRequestCommit)
226 roundtrip(t, c)
227 deadline := time.After(2 * time.Second)
228 for {
229 select {
230 case got := <-contents:
231 if len(got.Children) == 1 && got.Children[0].Source == ([4]float32{0, 0, 1, 1}) {
232 return
233 }
234 case <-deadline:
235 t.Fatal("new valid viewport not applied")
236 }
237 }
238 })
239 }
240}