main f2d166b

damage_test.go RawHistoryFolder

1package wayland
2
3import (
4	"testing"
5	"time"
6
7	"github.com/bnema/go-wayland-bindings/server/wayland"
8	"github.com/bnema/go-wayland-bindings/server/xdgshell"
9	"github.com/bnema/neferwl/internal/ports"
10	"golang.org/x/sys/unix"
11)
12
13// Damage reaches the renderer in buffer pixels, per content, with the
14// window's recent history; a new buffer without damage is a full change.
15func TestDamageHistory(t *testing.T) {
16	s, events, _, contents, dir := contentServer(t)
17	c := protocolClient(t, s, dir)
18	comp := bindVersion(t, c, "wl_compositor", 4)
19	wm := bindProtocol(t, c, "xdg_wm_base")
20	shm := bindProtocol(t, c, "wl_shm")
21	registerProtocol(t, c, shm)
22	surf, xdg, top := c.AllocateID(), c.AllocateID(), c.AllocateID()
23	requestProtocol(t, c, comp, wayland.CompositorRequestCreateSurface, surf)
24	registerProtocol(t, c, surf)
25	requestProtocol(t, c, wm, xdgshell.WmBaseRequestGetXdgSurface, xdg, surf)
26	serials := make(chan uint32, 8)
27	xp := &configureProxy{serial: serials}
28	xp.SetID(xdg)
29	registerWireProxy(c, xp)
30	registerProtocol(t, c, top)
31	requestProtocol(t, c, xdg, xdgshell.SurfaceRequestGetToplevel, top)
32	requestProtocol(t, c, surf, wayland.SurfaceRequestCommit)
33	if err := c.Roundtrip(); err != nil {
34		t.Fatal(err)
35	}
36	requestProtocol(t, c, xdg, xdgshell.SurfaceRequestAckConfigure, <-serials)
37	fd, err := unix.MemfdCreate("damage", 0)
38	if err != nil {
39		t.Fatal(err)
40	}
41	defer unix.Close(fd)
42	if err := unix.Ftruncate(fd, 2*16*16*4); err != nil {
43		t.Fatal(err)
44	}
45	pool := c.AllocateID()
46	registerProtocol(t, c, pool)
47	if err := wireRequest(c, shm, uint16(wayland.ShmRequestCreatePool), []int{fd}, pool, int32(2*16*16*4)); err != nil {
48		t.Fatal(err)
49	}
50	bufs := [2]uint32{c.AllocateID(), c.AllocateID()}
51	for i, b := range bufs {
52		registerProtocol(t, c, b)
53		requestProtocol(t, c, pool, wayland.ShmPoolRequestCreateBuffer, b, int32(i*16*16*4), int32(16), int32(16), int32(64), uint32(0))
54	}
55	next := func() ports.SurfaceContent {
56		t.Helper()
57		if err := c.Roundtrip(); err != nil {
58			t.Fatal(err)
59		}
60		deadline := time.After(2 * time.Second)
61		for {
62			select {
63			case got := <-contents:
64				if got.Width == 16 {
65					return got
66				}
67			case <-deadline:
68				t.Fatal("no content")
69			}
70		}
71	}
72	// A new size: full.
73	requestProtocol(t, c, surf, wayland.SurfaceRequestAttach, bufs[0], int32(0), int32(0))
74	requestProtocol(t, c, surf, wayland.SurfaceRequestDamageBuffer, int32(0), int32(0), int32(4), int32(4))
75	requestProtocol(t, c, surf, wayland.SurfaceRequestCommit)
76	mapped(t, events, 2*time.Second)
77	first := next()
78	if h := first.DamageHistory; len(h) == 0 || h[len(h)-1].Seq != first.Seq || !h[len(h)-1].Full {
79		t.Fatalf("resize: %+v", h)
80	}
81	// Buffer and surface damage, clipped to the buffer.
82	requestProtocol(t, c, surf, wayland.SurfaceRequestAttach, bufs[1], int32(0), int32(0))
83	requestProtocol(t, c, surf, wayland.SurfaceRequestDamageBuffer, int32(2), int32(3), int32(4), int32(5))
84	requestProtocol(t, c, surf, wayland.SurfaceRequestDamage, int32(14), int32(14), int32(1<<30), int32(1<<30))
85	requestProtocol(t, c, surf, wayland.SurfaceRequestCommit)
86	second := next()
87	got, ok := second.DamageSince(first.Seq)
88	want := []ports.Rect{{X: 2, Y: 3, W: 4, H: 5}, {X: 14, Y: 14, W: 2, H: 2}}
89	if !ok || len(got) != 2 || got[0] != want[0] || got[1] != want[1] {
90		t.Fatalf("damage %v %v", got, ok)
91	}
92	// Same buffer size, new logical size (buffer scale): full.
93	requestProtocol(t, c, surf, wayland.SurfaceRequestAttach, bufs[0], int32(0), int32(0))
94	requestProtocol(t, c, surf, wayland.SurfaceRequestSetBufferScale, int32(2))
95	requestProtocol(t, c, surf, wayland.SurfaceRequestDamageBuffer, int32(0), int32(0), int32(1), int32(1))
96	requestProtocol(t, c, surf, wayland.SurfaceRequestCommit)
97	scaled := next()
98	if _, ok := scaled.DamageSince(second.Seq); ok {
99		t.Fatal("logical resize kept partial damage")
100	}
101	second = scaled
102	// No damage with a new buffer: full.
103	requestProtocol(t, c, surf, wayland.SurfaceRequestAttach, bufs[0], int32(0), int32(0))
104	requestProtocol(t, c, surf, wayland.SurfaceRequestCommit)
105	third := next()
106	if _, ok := third.DamageSince(second.Seq); ok {
107		t.Fatal("undamaged new buffer taken as unchanged")
108	}
109	// A subsurface with no buffer draws nothing: once its layout is
110	// applied (one full change), the root keeps its damage.
111	subc := bindProtocol(t, c, "wl_subcompositor")
112	child, sub := c.AllocateID(), c.AllocateID()
113	requestProtocol(t, c, comp, wayland.CompositorRequestCreateSurface, child)
114	registerProtocol(t, c, child)
115	registerProtocol(t, c, sub)
116	requestProtocol(t, c, subc, wayland.SubcompositorRequestGetSubsurface, sub, child, surf)
117	requestProtocol(t, c, surf, wayland.SurfaceRequestAttach, bufs[1], int32(0), int32(0))
118	requestProtocol(t, c, surf, wayland.SurfaceRequestCommit)
119	third = next()
120	requestProtocol(t, c, surf, wayland.SurfaceRequestAttach, bufs[0], int32(0), int32(0))
121	requestProtocol(t, c, surf, wayland.SurfaceRequestDamageBuffer, int32(1), int32(1), int32(2), int32(2))
122	requestProtocol(t, c, surf, wayland.SurfaceRequestCommit)
123	fourth := next()
124	if got, ok := fourth.DamageSince(third.Seq); !ok || len(got) != 1 || got[0] != (ports.Rect{X: 1, Y: 1, W: 2, H: 2}) {
125		t.Fatalf("empty subsurface: damage %v %v", got, ok)
126	}
127	// A synced child drawn, then hidden by a null attach the parent's
128	// commit applies: its old area must be redrawn, both times.
129	rootCommit := func(what string, wantChildren int) {
130		t.Helper()
131		prev := fourth
132		requestProtocol(t, c, surf, wayland.SurfaceRequestCommit)
133		fourth = next()
134		if len(fourth.Children) != wantChildren {
135			t.Fatalf("%s: %d children, want %d", what, len(fourth.Children), wantChildren)
136		}
137		if got, ok := fourth.DamageSince(prev.Seq); ok {
138			t.Fatalf("%s kept partial damage %v", what, got)
139		}
140	}
141	requestProtocol(t, c, child, wayland.SurfaceRequestAttach, bufs[1], int32(0), int32(0))
142	requestProtocol(t, c, child, wayland.SurfaceRequestCommit)
143	rootCommit("child appearing", 1)
144	requestProtocol(t, c, child, wayland.SurfaceRequestAttach, uint32(0), int32(0), int32(0))
145	requestProtocol(t, c, child, wayland.SurfaceRequestCommit)
146	rootCommit("child hidden", 0)
147	// Destroying a child that draws nothing changes nothing on screen.
148	prev := fourth
149	requestProtocol(t, c, sub, wayland.SubsurfaceRequestDestroy)
150	fourth = next()
151	if got, ok := fourth.DamageSince(prev.Seq); !ok || len(got) != 0 {
152		t.Fatalf("empty child destroyed: damage %v %v", got, ok)
153	}
154	prev = fourth
155	requestProtocol(t, c, surf, wayland.SurfaceRequestAttach, bufs[0], int32(0), int32(0))
156	requestProtocol(t, c, surf, wayland.SurfaceRequestDamageBuffer, int32(1), int32(1), int32(2), int32(2))
157	requestProtocol(t, c, surf, wayland.SurfaceRequestCommit)
158	fourth = next()
159	if got, ok := fourth.DamageSince(prev.Seq); !ok || len(got) != 1 {
160		t.Fatalf("after empty child: damage %v %v", got, ok)
161	}
162	// Destroying a drawn child redraws its area.
163	child2, sub2 := c.AllocateID(), c.AllocateID()
164	requestProtocol(t, c, comp, wayland.CompositorRequestCreateSurface, child2)
165	registerProtocol(t, c, child2)
166	registerProtocol(t, c, sub2)
167	requestProtocol(t, c, subc, wayland.SubcompositorRequestGetSubsurface, sub2, child2, surf)
168	requestProtocol(t, c, child2, wayland.SurfaceRequestAttach, bufs[1], int32(0), int32(0))
169	requestProtocol(t, c, child2, wayland.SurfaceRequestCommit)
170	rootCommit("second child appearing", 1)
171	prev = fourth
172	requestProtocol(t, c, sub2, wayland.SubsurfaceRequestDestroy)
173	fourth = next()
174	if got, ok := fourth.DamageSince(prev.Seq); ok || len(fourth.Children) != 0 {
175		t.Fatalf("drawn child destroyed: damage %v %v, %d children", got, ok, len(fourth.Children))
176	}
177}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 25

Tags 10

↑↓ moveenter openesc close