detached 17529ce

refactor(xwm): log counters on close, drop the test-only Slot lookup and state atoms in present tense

commit
17529ce1e512f84fe75f8eb022e1118eabbe015f
author
brice · · 2026-10-22 14:04 UTC
parent
6299b4d
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
checks
1/1
8 files · +58 -57
M docs/dev/headless.md +1 -1
@@ -66,7 +66,7 @@ WAYLAND_DISPLAY=<name> go run ./examples/testime -preedit nihon -commit 日本
6666
6767 `make e2e-xwayland` runs the end-to-end test of the built-in window manager (`xwayland = builtin`). It is behind the `xwayland` build tag, so `go test ./...` and `make check` only compile it (`go vet -tags xwayland`). It needs `Xwayland` on `PATH` and a GPU, and skips without Xwayland.
6868
69-The test starts a headless NeferWL with a real `Xwayland` and runs `internal/app/testdata/x11probe`, a small X11 client (`-class`, `-title`, `-size`, `-sleep`) that paints its window green, prints `mapped WxH` and `focus`, and exits 0 on `WM_DELETE_WINDOW`. It checks:
69+The test starts a headless NeferWL with a real `Xwayland` on the first free X display (a live session's `:0` stays untouched) and runs `internal/app/testdata/x11probe`, a small X11 client (`-class`, `-title`, `-size`, `-sleep`) that paints its window green, prints `mapped WxH` and `focus`, and exits 0 on `WM_DELETE_WINDOW`. It checks:
7070
7171 - the state lists one visible `X11Probe` window with the probe's pid, and the screenshot is mostly green inside its tile;
7272 - the probe receives focus;
@@ -66,7 +66,7 @@ WAYLAND_DISPLAY=<name> go run ./examples/testime -preedit nihon -commit 日本
66 66
67 `make e2e-xwayland` runs the end-to-end test of the built-in window manager (`xwayland = builtin`). It is behind the `xwayland` build tag, so `go test ./...` and `make check` only compile it (`go vet -tags xwayland`). It needs `Xwayland` on `PATH` and a GPU, and skips without Xwayland.67 `make e2e-xwayland` runs the end-to-end test of the built-in window manager (`xwayland = builtin`). It is behind the `xwayland` build tag, so `go test ./...` and `make check` only compile it (`go vet -tags xwayland`). It needs `Xwayland` on `PATH` and a GPU, and skips without Xwayland.
68 68
69-The test starts a headless NeferWL with a real `Xwayland` and runs `internal/app/testdata/x11probe`, a small X11 client (`-class`, `-title`, `-size`, `-sleep`) that paints its window green, prints `mapped WxH` and `focus`, and exits 0 on `WM_DELETE_WINDOW`. It checks:69+The test starts a headless NeferWL with a real `Xwayland` on the first free X display (a live session's `:0` stays untouched) and runs `internal/app/testdata/x11probe`, a small X11 client (`-class`, `-title`, `-size`, `-sleep`) that paints its window green, prints `mapped WxH` and `focus`, and exits 0 on `WM_DELETE_WINDOW`. It checks:
70 70
71 - the state lists one visible `X11Probe` window with the probe's pid, and the screenshot is mostly green inside its tile;71 - the state lists one visible `X11Probe` window with the probe's pid, and the screenshot is mostly green inside its tile;
72 - the probe receives focus;72 - the probe receives focus;
M internal/adapters/wayland/xwm/atoms.go +2 -2
@@ -1,7 +1,7 @@
11 package xwm
22
3-// Atoms interned at boot, in this fixed order. Clipboard and selection atoms
4-// are interned now so later phases add no round trip.
3+// Atoms interned at boot in one round trip, in this fixed order. The table
4+// includes the clipboard and selection atoms.
55 const (
66 atomWMProtocols = iota
77 atomWMDeleteWindow
@@ -1,7 +1,7 @@
1 package xwm1 package xwm
2 2
3-// Atoms interned at boot, in this fixed order. Clipboard and selection atoms3+// Atoms interned at boot in one round trip, in this fixed order. The table
4-// are interned now so later phases add no round trip.4+// includes the clipboard and selection atoms.
5 const (5 const (
6 atomWMProtocols = iota6 atomWMProtocols = iota
7 atomWMDeleteWindow7 atomWMDeleteWindow
M internal/adapters/wayland/xwm/configure_test.go +8 -8
@@ -15,8 +15,8 @@ func newWin(t *testing.T, x *XWM, p *xpeer, h *mockhost, xid uint32) Slot {
1515 t.Helper()
1616 h.EXPECT().Created(mock.Anything, xid).Return(ports.WindowID(xid)).Once()
1717 p.createNotify(xid, false)
18- readUntil(t, x, func() bool { return x.Slot(xid) != 0 })
19- s := x.Slot(xid)
18+ readUntil(t, x, func() bool { return x.tab.lookup(xid) != 0 })
19+ s := x.tab.lookup(xid)
2020 r := x.tab.rec(s)
2121 r.ready, r.w, r.h = true, 300, 200
2222 return s
@@ -209,7 +209,7 @@ func TestConfigureRequestUnmanagedHonouredVerbatim(t *testing.T) {
209209 x, p, _, _, _ := attached(t)
210210 const win = 0x500009
211211 p.createNotify(win, true)
212- readUntil(t, x, func() bool { return x.Slot(win) != 0 })
212+ readUntil(t, x, func() bool { return x.tab.lookup(win) != 0 })
213213 n := settle(t, x, p)
214214
215215 x.feed(t, p, func() {
@@ -235,7 +235,7 @@ func TestConfigureStaleAndDuplicateDirtySlots(t *testing.T) {
235235 // The window dies with its entry queued; the slot is reused.
236236 h.EXPECT().Dissociate(s, true).Once()
237237 p.destroyNotify(a)
238- readUntil(t, x, func() bool { return x.Slot(a) == 0 })
238+ readUntil(t, x, func() bool { return x.tab.lookup(a) == 0 })
239239 s2 := newWin(t, x, p, h, b)
240240 require.Equal(t, s, s2, "LIFO slot reuse")
241241 n := len(p.requests())
@@ -258,13 +258,13 @@ func TestDirtyQueueNeverGrows(t *testing.T) {
258258 xid := uint32(base + i)
259259 h.EXPECT().Created(mock.Anything, xid).Return(ports.WindowID(1)).Once()
260260 p.createNotify(xid, false)
261- readUntil(t, x, func() bool { return x.Slot(xid) != 0 })
262- s := x.Slot(xid)
261+ readUntil(t, x, func() bool { return x.tab.lookup(xid) != 0 })
262+ s := x.tab.lookup(xid)
263263 x.tab.rec(s).ready = true
264264 x.Configure(s, ports.ConfigureWindow{Width: 10, Height: 10})
265265 h.EXPECT().Dissociate(s, true).Once()
266266 p.destroyNotify(xid)
267- readUntil(t, x, func() bool { return x.Slot(xid) == 0 })
267+ readUntil(t, x, func() bool { return x.tab.lookup(xid) == 0 })
268268 }
269269 require.Equal(t, c0, cap(x.dirty))
270270 require.LessOrEqual(t, len(x.dirty), c0)
@@ -431,7 +431,7 @@ func TestDestroyedActiveWindowClearsActive(t *testing.T) {
431431 settle(t, x, p)
432432 h.EXPECT().Dissociate(s, true).Once()
433433 p.destroyNotify(win)
434- readUntil(t, x, func() bool { return x.Slot(win) == 0 })
434+ readUntil(t, x, func() bool { return x.tab.lookup(win) == 0 })
435435 settle(t, x, p)
436436 require.Zero(t, x.active)
437437 act, _ := p.prop(peerRoot, "_NET_ACTIVE_WINDOW")
@@ -15,8 +15,8 @@ func newWin(t *testing.T, x *XWM, p *xpeer, h *mockhost, xid uint32) Slot {
15 t.Helper()15 t.Helper()
16 h.EXPECT().Created(mock.Anything, xid).Return(ports.WindowID(xid)).Once()16 h.EXPECT().Created(mock.Anything, xid).Return(ports.WindowID(xid)).Once()
17 p.createNotify(xid, false)17 p.createNotify(xid, false)
18- readUntil(t, x, func() bool { return x.Slot(xid) != 0 })18+ readUntil(t, x, func() bool { return x.tab.lookup(xid) != 0 })
19- s := x.Slot(xid)19+ s := x.tab.lookup(xid)
20 r := x.tab.rec(s)20 r := x.tab.rec(s)
21 r.ready, r.w, r.h = true, 300, 20021 r.ready, r.w, r.h = true, 300, 200
22 return s22 return s
@@ -209,7 +209,7 @@ func TestConfigureRequestUnmanagedHonouredVerbatim(t *testing.T) {
209 x, p, _, _, _ := attached(t)209 x, p, _, _, _ := attached(t)
210 const win = 0x500009210 const win = 0x500009
211 p.createNotify(win, true)211 p.createNotify(win, true)
212- readUntil(t, x, func() bool { return x.Slot(win) != 0 })212+ readUntil(t, x, func() bool { return x.tab.lookup(win) != 0 })
213 n := settle(t, x, p)213 n := settle(t, x, p)
214 214
215 x.feed(t, p, func() {215 x.feed(t, p, func() {
@@ -235,7 +235,7 @@ func TestConfigureStaleAndDuplicateDirtySlots(t *testing.T) {
235 // The window dies with its entry queued; the slot is reused.235 // The window dies with its entry queued; the slot is reused.
236 h.EXPECT().Dissociate(s, true).Once()236 h.EXPECT().Dissociate(s, true).Once()
237 p.destroyNotify(a)237 p.destroyNotify(a)
238- readUntil(t, x, func() bool { return x.Slot(a) == 0 })238+ readUntil(t, x, func() bool { return x.tab.lookup(a) == 0 })
239 s2 := newWin(t, x, p, h, b)239 s2 := newWin(t, x, p, h, b)
240 require.Equal(t, s, s2, "LIFO slot reuse")240 require.Equal(t, s, s2, "LIFO slot reuse")
241 n := len(p.requests())241 n := len(p.requests())
@@ -258,13 +258,13 @@ func TestDirtyQueueNeverGrows(t *testing.T) {
258 xid := uint32(base + i)258 xid := uint32(base + i)
259 h.EXPECT().Created(mock.Anything, xid).Return(ports.WindowID(1)).Once()259 h.EXPECT().Created(mock.Anything, xid).Return(ports.WindowID(1)).Once()
260 p.createNotify(xid, false)260 p.createNotify(xid, false)
261- readUntil(t, x, func() bool { return x.Slot(xid) != 0 })261+ readUntil(t, x, func() bool { return x.tab.lookup(xid) != 0 })
262- s := x.Slot(xid)262+ s := x.tab.lookup(xid)
263 x.tab.rec(s).ready = true263 x.tab.rec(s).ready = true
264 x.Configure(s, ports.ConfigureWindow{Width: 10, Height: 10})264 x.Configure(s, ports.ConfigureWindow{Width: 10, Height: 10})
265 h.EXPECT().Dissociate(s, true).Once()265 h.EXPECT().Dissociate(s, true).Once()
266 p.destroyNotify(xid)266 p.destroyNotify(xid)
267- readUntil(t, x, func() bool { return x.Slot(xid) == 0 })267+ readUntil(t, x, func() bool { return x.tab.lookup(xid) == 0 })
268 }268 }
269 require.Equal(t, c0, cap(x.dirty))269 require.Equal(t, c0, cap(x.dirty))
270 require.LessOrEqual(t, len(x.dirty), c0)270 require.LessOrEqual(t, len(x.dirty), c0)
@@ -431,7 +431,7 @@ func TestDestroyedActiveWindowClearsActive(t *testing.T) {
431 settle(t, x, p)431 settle(t, x, p)
432 h.EXPECT().Dissociate(s, true).Once()432 h.EXPECT().Dissociate(s, true).Once()
433 p.destroyNotify(win)433 p.destroyNotify(win)
434- readUntil(t, x, func() bool { return x.Slot(win) == 0 })434+ readUntil(t, x, func() bool { return x.tab.lookup(win) == 0 })
435 settle(t, x, p)435 settle(t, x, p)
436 require.Zero(t, x.active)436 require.Zero(t, x.active)
437 act, _ := p.prop(peerRoot, "_NET_ACTIVE_WINDOW")437 act, _ := p.prop(peerRoot, "_NET_ACTIVE_WINDOW")
M internal/adapters/wayland/xwm/fixes_test.go +2 -2
@@ -132,7 +132,7 @@ func TestGetGeometryBadDrawableIsNotReady(t *testing.T) {
132132 require.False(t, hs.x.tab.rec(s).ready)
133133 hs.h.EXPECT().Dissociate(s, true).Once()
134134 hs.p.destroyNotify(win)
135- readUntil(t, hs.x, func() bool { return hs.x.Slot(win) == 0 })
135+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(win) == 0 })
136136 }
137137
138138 // A host that refocuses the window while dissociating it must not leave a
@@ -143,7 +143,7 @@ func TestForgetWindowClearsActiveSetByHost(t *testing.T) {
143143 s := newWin(t, x, p, h, win)
144144 h.EXPECT().Dissociate(s, true).Run(func(sl Slot, _ bool) { x.Focus(sl) }).Once()
145145 p.destroyNotify(win)
146- readUntil(t, x, func() bool { return x.Slot(win) == 0 })
146+ readUntil(t, x, func() bool { return x.tab.lookup(win) == 0 })
147147 require.Zero(t, x.active)
148148 require.False(t, x.refocus)
149149 settle(t, x, p)
@@ -132,7 +132,7 @@ func TestGetGeometryBadDrawableIsNotReady(t *testing.T) {
132 require.False(t, hs.x.tab.rec(s).ready)132 require.False(t, hs.x.tab.rec(s).ready)
133 hs.h.EXPECT().Dissociate(s, true).Once()133 hs.h.EXPECT().Dissociate(s, true).Once()
134 hs.p.destroyNotify(win)134 hs.p.destroyNotify(win)
135- readUntil(t, hs.x, func() bool { return hs.x.Slot(win) == 0 })135+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(win) == 0 })
136 }136 }
137 137
138 // A host that refocuses the window while dissociating it must not leave a138 // A host that refocuses the window while dissociating it must not leave a
@@ -143,7 +143,7 @@ func TestForgetWindowClearsActiveSetByHost(t *testing.T) {
143 s := newWin(t, x, p, h, win)143 s := newWin(t, x, p, h, win)
144 h.EXPECT().Dissociate(s, true).Run(func(sl Slot, _ bool) { x.Focus(sl) }).Once()144 h.EXPECT().Dissociate(s, true).Run(func(sl Slot, _ bool) { x.Focus(sl) }).Once()
145 p.destroyNotify(win)145 p.destroyNotify(win)
146- readUntil(t, x, func() bool { return x.Slot(win) == 0 })146+ readUntil(t, x, func() bool { return x.tab.lookup(win) == 0 })
147 require.Zero(t, x.active)147 require.Zero(t, x.active)
148 require.False(t, x.refocus)148 require.False(t, x.refocus)
149 settle(t, x, p)149 settle(t, x, p)
M internal/adapters/wayland/xwm/mapflow_test.go +18 -18
@@ -50,8 +50,8 @@ func (hs *harness) create(win uint32) Slot {
5050 hs.t.Helper()
5151 hs.expectCreated(win)
5252 hs.p.createNotify(win, false)
53- readUntil(hs.t, hs.x, func() bool { return hs.x.Slot(win) != 0 })
54- return hs.x.Slot(win)
53+ readUntil(hs.t, hs.x, func() bool { return hs.x.tab.lookup(win) != 0 })
54+ return hs.x.tab.lookup(win)
5555 }
5656
5757 // mapAndWait creates and maps win and waits for Ready.
@@ -247,7 +247,7 @@ func TestMapFlowPIDFromXRes(t *testing.T) {
247247 readUntil(t, hs.x, func() bool { _, ok := hs.ready[win]; return ok })
248248 require.Equal(t, 777, hs.ready[win].PID, "Ready waited for the QueryClientIds reply")
249249 require.Equal(t, 1, hs.count(func(r xpeerReq) bool { return r.major == 144 && r.minor == 4 }))
250- require.Zero(t, hs.x.tab.rec(hs.x.Slot(win)).pending)
250+ require.Zero(t, hs.x.tab.rec(hs.x.tab.lookup(win)).pending)
251251 }
252252
253253 func TestMapFlowPIDUnknownStillReady(t *testing.T) {
@@ -293,7 +293,7 @@ func TestMapFlowBadWindowOnPropertiesNeverReady(t *testing.T) {
293293
294294 hs.h.EXPECT().Dissociate(s, true).Once()
295295 hs.p.destroyNotify(win)
296- readUntil(t, hs.x, func() bool { return hs.x.Slot(win) == 0 })
296+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(win) == 0 })
297297 }
298298
299299 func TestMapRequestTwiceBeforeReadyAsksOnce(t *testing.T) {
@@ -518,10 +518,10 @@ func TestPairingDuplicateSerialIgnored(t *testing.T) {
518518 func TestPairingUnmanagedKeepsSurfaceWithoutAssociate(t *testing.T) {
519519 hs := newHarness(t, nil)
520520 hs.p.createNotify(0x500009, true)
521- readUntil(t, hs.x, func() bool { return hs.x.Slot(0x500009) != 0 })
521+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(0x500009) != 0 })
522522 hs.serialMsg(0x500009, 3)
523523 hs.x.SetSerial(5, 3) // the mock fails on an Associate
524- require.EqualValues(t, 5, hs.x.tab.rec(hs.x.Slot(0x500009)).surface)
524+ require.EqualValues(t, 5, hs.x.tab.rec(hs.x.tab.lookup(0x500009)).surface)
525525 }
526526
527527 func TestPairingTableOverflowDropsOldest(t *testing.T) {
@@ -620,7 +620,7 @@ func TestDestroyWhileMappingNeverReady(t *testing.T) {
620620 hs.waitMap(win)
621621 hs.h.EXPECT().Dissociate(s, true).Once()
622622 hs.p.destroyNotify(win)
623- readUntil(t, hs.x, func() bool { return hs.x.Slot(win) == 0 })
623+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(win) == 0 })
624624 replies := hs.x.Stats().Replies
625625 hs.p.release() // replies for a window that is gone: xturbo retired their slots
626626 readUntil(t, hs.x, func() bool { return hs.x.conn.Stats().DroppedReplies >= uint64(propCount) })
@@ -638,12 +638,12 @@ func TestStaleRepliesAfterSlotReuseAreIgnored(t *testing.T) {
638638 hs.waitMap(old)
639639 hs.h.EXPECT().Dissociate(s, true).Once()
640640 hs.p.destroyNotify(old)
641- readUntil(t, hs.x, func() bool { return hs.x.Slot(old) == 0 })
641+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(old) == 0 })
642642
643643 hs.expectCreated(fresh)
644644 hs.p.createNotify(fresh, false)
645- readUntil(t, hs.x, func() bool { return hs.x.Slot(fresh) != 0 })
646- require.Equal(t, s, hs.x.Slot(fresh), "the freed slot is reused")
645+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(fresh) != 0 })
646+ require.Equal(t, s, hs.x.tab.lookup(fresh), "the freed slot is reused")
647647
648648 hs.p.release()
649649 readUntil(t, hs.x, func() bool { return hs.x.conn.Stats().DroppedReplies >= uint64(propCount) })
@@ -669,7 +669,7 @@ func TestReparentAwayFromRootRemovesManagedWindow(t *testing.T) {
669669 s := hs.mapAndWait(win)
670670 hs.h.EXPECT().Dissociate(s, true).Once()
671671 hs.p.reparentNotify(win, 0x600000)
672- readUntil(t, hs.x, func() bool { return hs.x.Slot(win) == 0 })
672+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(win) == 0 })
673673 list, _ := hs.p.prop(peerRoot, "_NET_CLIENT_LIST")
674674 require.Empty(t, list.data)
675675 require.Zero(t, hs.x.tab.n)
@@ -678,9 +678,9 @@ func TestReparentAwayFromRootRemovesManagedWindow(t *testing.T) {
678678 func TestReparentAwayOfUnmanagedWindowRemovesIt(t *testing.T) {
679679 hs := newHarness(t, nil)
680680 hs.p.createNotify(0x500009, true)
681- readUntil(t, hs.x, func() bool { return hs.x.Slot(0x500009) != 0 })
681+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(0x500009) != 0 })
682682 hs.p.reparentNotify(0x500009, 0x600000)
683- readUntil(t, hs.x, func() bool { return hs.x.Slot(0x500009) == 0 })
683+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(0x500009) == 0 })
684684 }
685685
686686 func TestReparentIntoRootAdoptsWindow(t *testing.T) {
@@ -688,8 +688,8 @@ func TestReparentIntoRootAdoptsWindow(t *testing.T) {
688688 const win = 0x500001
689689 hs.expectCreated(win)
690690 hs.p.reparentNotify(win, peerRoot)
691- readUntil(t, hs.x, func() bool { return hs.x.Slot(win) != 0 })
692- require.Equal(t, kindManaged, hs.x.tab.rec(hs.x.Slot(win)).kind)
691+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(win) != 0 })
692+ require.Equal(t, kindManaged, hs.x.tab.rec(hs.x.tab.lookup(win)).kind)
693693
694694 // Already known: no second Created.
695695 hs.p.reparentNotify(win, peerRoot)
@@ -704,15 +704,15 @@ func TestReparentIntoRootOfOverrideRedirectWindowStaysUnmanaged(t *testing.T) {
704704 binary.LittleEndian.PutUint32(b[12:], peerRoot)
705705 b[20] = 1
706706 })
707- readUntil(t, hs.x, func() bool { return hs.x.Slot(0x500009) != 0 })
708- require.Equal(t, kindUnmanaged, hs.x.tab.rec(hs.x.Slot(0x500009)).kind)
707+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(0x500009) != 0 })
708+ require.Equal(t, kindUnmanaged, hs.x.tab.rec(hs.x.tab.lookup(0x500009)).kind)
709709 }
710710
711711 func TestReparentOfWMWindowIgnored(t *testing.T) {
712712 hs := newHarness(t, nil)
713713 hs.p.reparentNotify(hs.x.wmWin, peerRoot)
714714 readUntil(t, hs.x, func() bool { return hs.x.Stats().Events >= 1 })
715- require.Zero(t, hs.x.Slot(hs.x.wmWin))
715+ require.Zero(t, hs.x.tab.lookup(hs.x.wmWin))
716716 }
717717
718718 // Window known only before Attach: Attach replays the map flow.
@@ -50,8 +50,8 @@ func (hs *harness) create(win uint32) Slot {
50 hs.t.Helper()50 hs.t.Helper()
51 hs.expectCreated(win)51 hs.expectCreated(win)
52 hs.p.createNotify(win, false)52 hs.p.createNotify(win, false)
53- readUntil(hs.t, hs.x, func() bool { return hs.x.Slot(win) != 0 })53+ readUntil(hs.t, hs.x, func() bool { return hs.x.tab.lookup(win) != 0 })
54- return hs.x.Slot(win)54+ return hs.x.tab.lookup(win)
55 }55 }
56 56
57 // mapAndWait creates and maps win and waits for Ready.57 // mapAndWait creates and maps win and waits for Ready.
@@ -247,7 +247,7 @@ func TestMapFlowPIDFromXRes(t *testing.T) {
247 readUntil(t, hs.x, func() bool { _, ok := hs.ready[win]; return ok })247 readUntil(t, hs.x, func() bool { _, ok := hs.ready[win]; return ok })
248 require.Equal(t, 777, hs.ready[win].PID, "Ready waited for the QueryClientIds reply")248 require.Equal(t, 777, hs.ready[win].PID, "Ready waited for the QueryClientIds reply")
249 require.Equal(t, 1, hs.count(func(r xpeerReq) bool { return r.major == 144 && r.minor == 4 }))249 require.Equal(t, 1, hs.count(func(r xpeerReq) bool { return r.major == 144 && r.minor == 4 }))
250- require.Zero(t, hs.x.tab.rec(hs.x.Slot(win)).pending)250+ require.Zero(t, hs.x.tab.rec(hs.x.tab.lookup(win)).pending)
251 }251 }
252 252
253 func TestMapFlowPIDUnknownStillReady(t *testing.T) {253 func TestMapFlowPIDUnknownStillReady(t *testing.T) {
@@ -293,7 +293,7 @@ func TestMapFlowBadWindowOnPropertiesNeverReady(t *testing.T) {
293 293
294 hs.h.EXPECT().Dissociate(s, true).Once()294 hs.h.EXPECT().Dissociate(s, true).Once()
295 hs.p.destroyNotify(win)295 hs.p.destroyNotify(win)
296- readUntil(t, hs.x, func() bool { return hs.x.Slot(win) == 0 })296+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(win) == 0 })
297 }297 }
298 298
299 func TestMapRequestTwiceBeforeReadyAsksOnce(t *testing.T) {299 func TestMapRequestTwiceBeforeReadyAsksOnce(t *testing.T) {
@@ -518,10 +518,10 @@ func TestPairingDuplicateSerialIgnored(t *testing.T) {
518 func TestPairingUnmanagedKeepsSurfaceWithoutAssociate(t *testing.T) {518 func TestPairingUnmanagedKeepsSurfaceWithoutAssociate(t *testing.T) {
519 hs := newHarness(t, nil)519 hs := newHarness(t, nil)
520 hs.p.createNotify(0x500009, true)520 hs.p.createNotify(0x500009, true)
521- readUntil(t, hs.x, func() bool { return hs.x.Slot(0x500009) != 0 })521+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(0x500009) != 0 })
522 hs.serialMsg(0x500009, 3)522 hs.serialMsg(0x500009, 3)
523 hs.x.SetSerial(5, 3) // the mock fails on an Associate523 hs.x.SetSerial(5, 3) // the mock fails on an Associate
524- require.EqualValues(t, 5, hs.x.tab.rec(hs.x.Slot(0x500009)).surface)524+ require.EqualValues(t, 5, hs.x.tab.rec(hs.x.tab.lookup(0x500009)).surface)
525 }525 }
526 526
527 func TestPairingTableOverflowDropsOldest(t *testing.T) {527 func TestPairingTableOverflowDropsOldest(t *testing.T) {
@@ -620,7 +620,7 @@ func TestDestroyWhileMappingNeverReady(t *testing.T) {
620 hs.waitMap(win)620 hs.waitMap(win)
621 hs.h.EXPECT().Dissociate(s, true).Once()621 hs.h.EXPECT().Dissociate(s, true).Once()
622 hs.p.destroyNotify(win)622 hs.p.destroyNotify(win)
623- readUntil(t, hs.x, func() bool { return hs.x.Slot(win) == 0 })623+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(win) == 0 })
624 replies := hs.x.Stats().Replies624 replies := hs.x.Stats().Replies
625 hs.p.release() // replies for a window that is gone: xturbo retired their slots625 hs.p.release() // replies for a window that is gone: xturbo retired their slots
626 readUntil(t, hs.x, func() bool { return hs.x.conn.Stats().DroppedReplies >= uint64(propCount) })626 readUntil(t, hs.x, func() bool { return hs.x.conn.Stats().DroppedReplies >= uint64(propCount) })
@@ -638,12 +638,12 @@ func TestStaleRepliesAfterSlotReuseAreIgnored(t *testing.T) {
638 hs.waitMap(old)638 hs.waitMap(old)
639 hs.h.EXPECT().Dissociate(s, true).Once()639 hs.h.EXPECT().Dissociate(s, true).Once()
640 hs.p.destroyNotify(old)640 hs.p.destroyNotify(old)
641- readUntil(t, hs.x, func() bool { return hs.x.Slot(old) == 0 })641+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(old) == 0 })
642 642
643 hs.expectCreated(fresh)643 hs.expectCreated(fresh)
644 hs.p.createNotify(fresh, false)644 hs.p.createNotify(fresh, false)
645- readUntil(t, hs.x, func() bool { return hs.x.Slot(fresh) != 0 })645+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(fresh) != 0 })
646- require.Equal(t, s, hs.x.Slot(fresh), "the freed slot is reused")646+ require.Equal(t, s, hs.x.tab.lookup(fresh), "the freed slot is reused")
647 647
648 hs.p.release()648 hs.p.release()
649 readUntil(t, hs.x, func() bool { return hs.x.conn.Stats().DroppedReplies >= uint64(propCount) })649 readUntil(t, hs.x, func() bool { return hs.x.conn.Stats().DroppedReplies >= uint64(propCount) })
@@ -669,7 +669,7 @@ func TestReparentAwayFromRootRemovesManagedWindow(t *testing.T) {
669 s := hs.mapAndWait(win)669 s := hs.mapAndWait(win)
670 hs.h.EXPECT().Dissociate(s, true).Once()670 hs.h.EXPECT().Dissociate(s, true).Once()
671 hs.p.reparentNotify(win, 0x600000)671 hs.p.reparentNotify(win, 0x600000)
672- readUntil(t, hs.x, func() bool { return hs.x.Slot(win) == 0 })672+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(win) == 0 })
673 list, _ := hs.p.prop(peerRoot, "_NET_CLIENT_LIST")673 list, _ := hs.p.prop(peerRoot, "_NET_CLIENT_LIST")
674 require.Empty(t, list.data)674 require.Empty(t, list.data)
675 require.Zero(t, hs.x.tab.n)675 require.Zero(t, hs.x.tab.n)
@@ -678,9 +678,9 @@ func TestReparentAwayFromRootRemovesManagedWindow(t *testing.T) {
678 func TestReparentAwayOfUnmanagedWindowRemovesIt(t *testing.T) {678 func TestReparentAwayOfUnmanagedWindowRemovesIt(t *testing.T) {
679 hs := newHarness(t, nil)679 hs := newHarness(t, nil)
680 hs.p.createNotify(0x500009, true)680 hs.p.createNotify(0x500009, true)
681- readUntil(t, hs.x, func() bool { return hs.x.Slot(0x500009) != 0 })681+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(0x500009) != 0 })
682 hs.p.reparentNotify(0x500009, 0x600000)682 hs.p.reparentNotify(0x500009, 0x600000)
683- readUntil(t, hs.x, func() bool { return hs.x.Slot(0x500009) == 0 })683+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(0x500009) == 0 })
684 }684 }
685 685
686 func TestReparentIntoRootAdoptsWindow(t *testing.T) {686 func TestReparentIntoRootAdoptsWindow(t *testing.T) {
@@ -688,8 +688,8 @@ func TestReparentIntoRootAdoptsWindow(t *testing.T) {
688 const win = 0x500001688 const win = 0x500001
689 hs.expectCreated(win)689 hs.expectCreated(win)
690 hs.p.reparentNotify(win, peerRoot)690 hs.p.reparentNotify(win, peerRoot)
691- readUntil(t, hs.x, func() bool { return hs.x.Slot(win) != 0 })691+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(win) != 0 })
692- require.Equal(t, kindManaged, hs.x.tab.rec(hs.x.Slot(win)).kind)692+ require.Equal(t, kindManaged, hs.x.tab.rec(hs.x.tab.lookup(win)).kind)
693 693
694 // Already known: no second Created.694 // Already known: no second Created.
695 hs.p.reparentNotify(win, peerRoot)695 hs.p.reparentNotify(win, peerRoot)
@@ -704,15 +704,15 @@ func TestReparentIntoRootOfOverrideRedirectWindowStaysUnmanaged(t *testing.T) {
704 binary.LittleEndian.PutUint32(b[12:], peerRoot)704 binary.LittleEndian.PutUint32(b[12:], peerRoot)
705 b[20] = 1705 b[20] = 1
706 })706 })
707- readUntil(t, hs.x, func() bool { return hs.x.Slot(0x500009) != 0 })707+ readUntil(t, hs.x, func() bool { return hs.x.tab.lookup(0x500009) != 0 })
708- require.Equal(t, kindUnmanaged, hs.x.tab.rec(hs.x.Slot(0x500009)).kind)708+ require.Equal(t, kindUnmanaged, hs.x.tab.rec(hs.x.tab.lookup(0x500009)).kind)
709 }709 }
710 710
711 func TestReparentOfWMWindowIgnored(t *testing.T) {711 func TestReparentOfWMWindowIgnored(t *testing.T) {
712 hs := newHarness(t, nil)712 hs := newHarness(t, nil)
713 hs.p.reparentNotify(hs.x.wmWin, peerRoot)713 hs.p.reparentNotify(hs.x.wmWin, peerRoot)
714 readUntil(t, hs.x, func() bool { return hs.x.Stats().Events >= 1 })714 readUntil(t, hs.x, func() bool { return hs.x.Stats().Events >= 1 })
715- require.Zero(t, hs.x.Slot(hs.x.wmWin))715+ require.Zero(t, hs.x.tab.lookup(hs.x.wmWin))
716 }716 }
717 717
718 // Window known only before Attach: Attach replays the map flow.718 // Window known only before Attach: Attach replays the map flow.
M internal/adapters/wayland/xwm/streams_test.go +3 -3
@@ -67,7 +67,7 @@ func streamScenario(t *testing.T) *harness {
6767 // The steady state is a focused window: a FocusIn on it is not denied,
6868 // so no request depends on how events are batched into flushes. The
6969 // poke makes sure the focus requests are out before the peer pauses.
70- hs.x.Focus(hs.x.Slot(streamWin))
70+ hs.x.Focus(hs.x.tab.lookup(streamWin))
7171 poke(hs)
7272 return hs
7373 }
@@ -173,11 +173,11 @@ func replay(t *testing.T, k streamKind) (mallocs uint64, hs *harness) {
173173 func TestPropertyUnchangedAllocations(t *testing.T) {
174174 m, hs := replay(t, streamKinds[0])
175175 require.Zero(t, m, "unchanged property cycles must not allocate")
176- require.Equal(t, "net title", hs.x.tab.rec(hs.x.Slot(streamWin)).title)
176+ require.Equal(t, "net title", hs.x.tab.rec(hs.x.tab.lookup(streamWin)).title)
177177 }
178178
179179 func TestDispatchAllocations(t *testing.T) {
180180 m, hs := replay(t, streamKinds[1])
181181 require.Zero(t, m, "steady-state event dispatch must not allocate")
182- require.True(t, hs.x.tab.rec(hs.x.Slot(streamWin)).mapped)
182+ require.True(t, hs.x.tab.rec(hs.x.tab.lookup(streamWin)).mapped)
183183 }
@@ -67,7 +67,7 @@ func streamScenario(t *testing.T) *harness {
67 // The steady state is a focused window: a FocusIn on it is not denied,67 // The steady state is a focused window: a FocusIn on it is not denied,
68 // so no request depends on how events are batched into flushes. The68 // so no request depends on how events are batched into flushes. The
69 // poke makes sure the focus requests are out before the peer pauses.69 // poke makes sure the focus requests are out before the peer pauses.
70- hs.x.Focus(hs.x.Slot(streamWin))70+ hs.x.Focus(hs.x.tab.lookup(streamWin))
71 poke(hs)71 poke(hs)
72 return hs72 return hs
73 }73 }
@@ -173,11 +173,11 @@ func replay(t *testing.T, k streamKind) (mallocs uint64, hs *harness) {
173 func TestPropertyUnchangedAllocations(t *testing.T) {173 func TestPropertyUnchangedAllocations(t *testing.T) {
174 m, hs := replay(t, streamKinds[0])174 m, hs := replay(t, streamKinds[0])
175 require.Zero(t, m, "unchanged property cycles must not allocate")175 require.Zero(t, m, "unchanged property cycles must not allocate")
176- require.Equal(t, "net title", hs.x.tab.rec(hs.x.Slot(streamWin)).title)176+ require.Equal(t, "net title", hs.x.tab.rec(hs.x.tab.lookup(streamWin)).title)
177 }177 }
178 178
179 func TestDispatchAllocations(t *testing.T) {179 func TestDispatchAllocations(t *testing.T) {
180 m, hs := replay(t, streamKinds[1])180 m, hs := replay(t, streamKinds[1])
181 require.Zero(t, m, "steady-state event dispatch must not allocate")181 require.Zero(t, m, "steady-state event dispatch must not allocate")
182- require.True(t, hs.x.tab.rec(hs.x.Slot(streamWin)).mapped)182+ require.True(t, hs.x.tab.rec(hs.x.tab.lookup(streamWin)).mapped)
183 }183 }
M internal/adapters/wayland/xwm/xwm.go +5 -4
@@ -335,6 +335,10 @@ func (x *XWM) Close() {
335335 return
336336 }
337337 x.closed = true
338+ s := x.stats
339+ x.log.Info().Uint64("events", s.Events).Uint64("replies", s.Replies).Uint64("errors", s.Errors).
340+ Uint64("unattributed", s.Unattributed).Uint64("stale", s.Stale).Uint64("ring_full", s.RingFull).
341+ Uint64("legacy_id", s.LegacyID).Uint64("unmanaged_seen", s.UnmanagedSeen).Msg("X11 window management closed")
338342 if x.src != nil {
339343 x.src.Remove()
340344 x.src = nil
@@ -373,12 +377,9 @@ func (x *XWM) fail(err error) {
373377 x.Close()
374378 }
375379
376-// Stats returns the counters.
380+// Stats returns the counters; Close logs them.
377381 func (x *XWM) Stats() Stats { return x.stats }
378382
379-// Slot returns the slot of the X window xid, or 0.
380-func (x *XWM) Slot(xid uint32) Slot { return x.tab.lookup(xid) }
381-
382383 func (x *XWM) onReadable() {
383384 if x.closed {
384385 return
@@ -335,6 +335,10 @@ func (x *XWM) Close() {
335 return335 return
336 }336 }
337 x.closed = true337 x.closed = true
338+ s := x.stats
339+ x.log.Info().Uint64("events", s.Events).Uint64("replies", s.Replies).Uint64("errors", s.Errors).
340+ Uint64("unattributed", s.Unattributed).Uint64("stale", s.Stale).Uint64("ring_full", s.RingFull).
341+ Uint64("legacy_id", s.LegacyID).Uint64("unmanaged_seen", s.UnmanagedSeen).Msg("X11 window management closed")
338 if x.src != nil {342 if x.src != nil {
339 x.src.Remove()343 x.src.Remove()
340 x.src = nil344 x.src = nil
@@ -373,12 +377,9 @@ func (x *XWM) fail(err error) {
373 x.Close()377 x.Close()
374 }378 }
375 379
376-// Stats returns the counters.380+// Stats returns the counters; Close logs them.
377 func (x *XWM) Stats() Stats { return x.stats }381 func (x *XWM) Stats() Stats { return x.stats }
378 382
379-// Slot returns the slot of the X window xid, or 0.
380-func (x *XWM) Slot(xid uint32) Slot { return x.tab.lookup(xid) }
381-
382 func (x *XWM) onReadable() {383 func (x *XWM) onReadable() {
383 if x.closed {384 if x.closed {
384 return385 return
M internal/adapters/wayland/xwm/xwm_test.go +19 -19
@@ -178,10 +178,10 @@ func TestBootExistingWindows(t *testing.T) {
178178 p.attrs[0x400002] = [2]byte{1, 2} // override-redirect, viewable
179179 })
180180 _ = p
181- require.NotZero(t, x.Slot(0x400001))
182- require.NotZero(t, x.Slot(0x400002))
183- require.Equal(t, kindManaged, x.tab.rec(x.Slot(0x400001)).kind)
184- require.Equal(t, kindUnmanaged, x.tab.rec(x.Slot(0x400002)).kind)
181+ require.NotZero(t, x.tab.lookup(0x400001))
182+ require.NotZero(t, x.tab.lookup(0x400002))
183+ require.Equal(t, kindManaged, x.tab.rec(x.tab.lookup(0x400001)).kind)
184+ require.Equal(t, kindUnmanaged, x.tab.rec(x.tab.lookup(0x400002)).kind)
185185 }
186186
187187 // readUntil dispatches the socket until cond holds.
@@ -209,10 +209,10 @@ func TestAttachAnnouncesWindowsFoundAtBoot(t *testing.T) {
209209 })
210210 h := newMockhost(t)
211211 src := newMockfdSource(t)
212- h.EXPECT().Created(x.Slot(0x400001), uint32(0x400001)).Return(ports.WindowID(7)).Once()
212+ h.EXPECT().Created(x.tab.lookup(0x400001), uint32(0x400001)).Return(ports.WindowID(7)).Once()
213213 post, _ := newPost()
214214 x.attach(src, h, post)
215- require.Equal(t, ports.WindowID(7), x.tab.rec(x.Slot(0x400001)).id)
215+ require.Equal(t, ports.WindowID(7), x.tab.rec(x.tab.lookup(0x400001)).id)
216216 }
217217
218218 func attached(t *testing.T) (*XWM, *xpeer, *mockhost, *mockfdSource, *[]func()) {
@@ -230,8 +230,8 @@ func TestCreateAndDestroyManaged(t *testing.T) {
230230 const win = 0x500001
231231 h.EXPECT().Created(mock.Anything, uint32(win)).Return(ports.WindowID(3)).Once()
232232 p.createNotify(win, false)
233- readUntil(t, x, func() bool { return x.Slot(win) != 0 })
234- s := x.Slot(win)
233+ readUntil(t, x, func() bool { return x.tab.lookup(win) != 0 })
234+ s := x.tab.lookup(win)
235235 require.Equal(t, kindManaged, x.tab.rec(s).kind)
236236 require.Equal(t, ports.WindowID(3), x.tab.rec(s).id)
237237
@@ -241,7 +241,7 @@ func TestCreateAndDestroyManaged(t *testing.T) {
241241
242242 h.EXPECT().Dissociate(s, true).Once()
243243 p.destroyNotify(win)
244- readUntil(t, x, func() bool { return x.Slot(win) == 0 })
244+ readUntil(t, x, func() bool { return x.tab.lookup(win) == 0 })
245245 require.Zero(t, x.tab.n)
246246 _ = src
247247 }
@@ -253,18 +253,18 @@ func TestOwnWindowNeverAdopted(t *testing.T) {
253253 p.createNotify(x.wmWin, false)
254254 const marker = 0x500003
255255 p.createNotify(marker, true)
256- readUntil(t, x, func() bool { return x.Slot(marker) != 0 })
257- require.Zero(t, x.Slot(x.wmWin))
256+ readUntil(t, x, func() bool { return x.tab.lookup(marker) != 0 })
257+ require.Zero(t, x.tab.lookup(x.wmWin))
258258 }
259259
260260 func TestOverrideRedirectNeverReachesHost(t *testing.T) {
261261 x, p, _, _, _ := attached(t) // the mock fails the test on any host call
262262 p.createNotify(0x500002, true)
263- readUntil(t, x, func() bool { return x.Slot(0x500002) != 0 })
264- require.Equal(t, kindUnmanaged, x.tab.rec(x.Slot(0x500002)).kind)
263+ readUntil(t, x, func() bool { return x.tab.lookup(0x500002) != 0 })
264+ require.Equal(t, kindUnmanaged, x.tab.rec(x.tab.lookup(0x500002)).kind)
265265 require.EqualValues(t, 1, x.Stats().UnmanagedSeen)
266266 p.destroyNotify(0x500002)
267- readUntil(t, x, func() bool { return x.Slot(0x500002) == 0 })
267+ readUntil(t, x, func() bool { return x.tab.lookup(0x500002) == 0 })
268268 }
269269
270270 func TestCloseDissociatesEverything(t *testing.T) {
@@ -274,9 +274,9 @@ func TestCloseDissociatesEverything(t *testing.T) {
274274 p.createNotify(win, false)
275275 }
276276 p.createNotify(0x500003, true)
277- readUntil(t, x, func() bool { return x.Slot(0x500003) != 0 })
278- h.EXPECT().Dissociate(x.Slot(0x500001), true).Once()
279- h.EXPECT().Dissociate(x.Slot(0x500002), true).Once()
277+ readUntil(t, x, func() bool { return x.tab.lookup(0x500003) != 0 })
278+ h.EXPECT().Dissociate(x.tab.lookup(0x500001), true).Once()
279+ h.EXPECT().Dissociate(x.tab.lookup(0x500002), true).Once()
280280 src.EXPECT().Remove().Once()
281281 x.Close()
282282 x.Close() // idempotent
@@ -288,8 +288,8 @@ func TestEOFClosesXWM(t *testing.T) {
288288 x, p, h, src, _ := attached(t)
289289 h.EXPECT().Created(mock.Anything, uint32(0x500001)).Return(ports.WindowID(1)).Once()
290290 p.createNotify(0x500001, false)
291- readUntil(t, x, func() bool { return x.Slot(0x500001) != 0 })
292- h.EXPECT().Dissociate(x.Slot(0x500001), true).Once()
291+ readUntil(t, x, func() bool { return x.tab.lookup(0x500001) != 0 })
292+ h.EXPECT().Dissociate(x.tab.lookup(0x500001), true).Once()
293293 src.EXPECT().Remove().Once()
294294 p.hangup()
295295 readUntil(t, x, func() bool { return x.closed })
@@ -178,10 +178,10 @@ func TestBootExistingWindows(t *testing.T) {
178 p.attrs[0x400002] = [2]byte{1, 2} // override-redirect, viewable178 p.attrs[0x400002] = [2]byte{1, 2} // override-redirect, viewable
179 })179 })
180 _ = p180 _ = p
181- require.NotZero(t, x.Slot(0x400001))181+ require.NotZero(t, x.tab.lookup(0x400001))
182- require.NotZero(t, x.Slot(0x400002))182+ require.NotZero(t, x.tab.lookup(0x400002))
183- require.Equal(t, kindManaged, x.tab.rec(x.Slot(0x400001)).kind)183+ require.Equal(t, kindManaged, x.tab.rec(x.tab.lookup(0x400001)).kind)
184- require.Equal(t, kindUnmanaged, x.tab.rec(x.Slot(0x400002)).kind)184+ require.Equal(t, kindUnmanaged, x.tab.rec(x.tab.lookup(0x400002)).kind)
185 }185 }
186 186
187 // readUntil dispatches the socket until cond holds.187 // readUntil dispatches the socket until cond holds.
@@ -209,10 +209,10 @@ func TestAttachAnnouncesWindowsFoundAtBoot(t *testing.T) {
209 })209 })
210 h := newMockhost(t)210 h := newMockhost(t)
211 src := newMockfdSource(t)211 src := newMockfdSource(t)
212- h.EXPECT().Created(x.Slot(0x400001), uint32(0x400001)).Return(ports.WindowID(7)).Once()212+ h.EXPECT().Created(x.tab.lookup(0x400001), uint32(0x400001)).Return(ports.WindowID(7)).Once()
213 post, _ := newPost()213 post, _ := newPost()
214 x.attach(src, h, post)214 x.attach(src, h, post)
215- require.Equal(t, ports.WindowID(7), x.tab.rec(x.Slot(0x400001)).id)215+ require.Equal(t, ports.WindowID(7), x.tab.rec(x.tab.lookup(0x400001)).id)
216 }216 }
217 217
218 func attached(t *testing.T) (*XWM, *xpeer, *mockhost, *mockfdSource, *[]func()) {218 func attached(t *testing.T) (*XWM, *xpeer, *mockhost, *mockfdSource, *[]func()) {
@@ -230,8 +230,8 @@ func TestCreateAndDestroyManaged(t *testing.T) {
230 const win = 0x500001230 const win = 0x500001
231 h.EXPECT().Created(mock.Anything, uint32(win)).Return(ports.WindowID(3)).Once()231 h.EXPECT().Created(mock.Anything, uint32(win)).Return(ports.WindowID(3)).Once()
232 p.createNotify(win, false)232 p.createNotify(win, false)
233- readUntil(t, x, func() bool { return x.Slot(win) != 0 })233+ readUntil(t, x, func() bool { return x.tab.lookup(win) != 0 })
234- s := x.Slot(win)234+ s := x.tab.lookup(win)
235 require.Equal(t, kindManaged, x.tab.rec(s).kind)235 require.Equal(t, kindManaged, x.tab.rec(s).kind)
236 require.Equal(t, ports.WindowID(3), x.tab.rec(s).id)236 require.Equal(t, ports.WindowID(3), x.tab.rec(s).id)
237 237
@@ -241,7 +241,7 @@ func TestCreateAndDestroyManaged(t *testing.T) {
241 241
242 h.EXPECT().Dissociate(s, true).Once()242 h.EXPECT().Dissociate(s, true).Once()
243 p.destroyNotify(win)243 p.destroyNotify(win)
244- readUntil(t, x, func() bool { return x.Slot(win) == 0 })244+ readUntil(t, x, func() bool { return x.tab.lookup(win) == 0 })
245 require.Zero(t, x.tab.n)245 require.Zero(t, x.tab.n)
246 _ = src246 _ = src
247 }247 }
@@ -253,18 +253,18 @@ func TestOwnWindowNeverAdopted(t *testing.T) {
253 p.createNotify(x.wmWin, false)253 p.createNotify(x.wmWin, false)
254 const marker = 0x500003254 const marker = 0x500003
255 p.createNotify(marker, true)255 p.createNotify(marker, true)
256- readUntil(t, x, func() bool { return x.Slot(marker) != 0 })256+ readUntil(t, x, func() bool { return x.tab.lookup(marker) != 0 })
257- require.Zero(t, x.Slot(x.wmWin))257+ require.Zero(t, x.tab.lookup(x.wmWin))
258 }258 }
259 259
260 func TestOverrideRedirectNeverReachesHost(t *testing.T) {260 func TestOverrideRedirectNeverReachesHost(t *testing.T) {
261 x, p, _, _, _ := attached(t) // the mock fails the test on any host call261 x, p, _, _, _ := attached(t) // the mock fails the test on any host call
262 p.createNotify(0x500002, true)262 p.createNotify(0x500002, true)
263- readUntil(t, x, func() bool { return x.Slot(0x500002) != 0 })263+ readUntil(t, x, func() bool { return x.tab.lookup(0x500002) != 0 })
264- require.Equal(t, kindUnmanaged, x.tab.rec(x.Slot(0x500002)).kind)264+ require.Equal(t, kindUnmanaged, x.tab.rec(x.tab.lookup(0x500002)).kind)
265 require.EqualValues(t, 1, x.Stats().UnmanagedSeen)265 require.EqualValues(t, 1, x.Stats().UnmanagedSeen)
266 p.destroyNotify(0x500002)266 p.destroyNotify(0x500002)
267- readUntil(t, x, func() bool { return x.Slot(0x500002) == 0 })267+ readUntil(t, x, func() bool { return x.tab.lookup(0x500002) == 0 })
268 }268 }
269 269
270 func TestCloseDissociatesEverything(t *testing.T) {270 func TestCloseDissociatesEverything(t *testing.T) {
@@ -274,9 +274,9 @@ func TestCloseDissociatesEverything(t *testing.T) {
274 p.createNotify(win, false)274 p.createNotify(win, false)
275 }275 }
276 p.createNotify(0x500003, true)276 p.createNotify(0x500003, true)
277- readUntil(t, x, func() bool { return x.Slot(0x500003) != 0 })277+ readUntil(t, x, func() bool { return x.tab.lookup(0x500003) != 0 })
278- h.EXPECT().Dissociate(x.Slot(0x500001), true).Once()278+ h.EXPECT().Dissociate(x.tab.lookup(0x500001), true).Once()
279- h.EXPECT().Dissociate(x.Slot(0x500002), true).Once()279+ h.EXPECT().Dissociate(x.tab.lookup(0x500002), true).Once()
280 src.EXPECT().Remove().Once()280 src.EXPECT().Remove().Once()
281 x.Close()281 x.Close()
282 x.Close() // idempotent282 x.Close() // idempotent
@@ -288,8 +288,8 @@ func TestEOFClosesXWM(t *testing.T) {
288 x, p, h, src, _ := attached(t)288 x, p, h, src, _ := attached(t)
289 h.EXPECT().Created(mock.Anything, uint32(0x500001)).Return(ports.WindowID(1)).Once()289 h.EXPECT().Created(mock.Anything, uint32(0x500001)).Return(ports.WindowID(1)).Once()
290 p.createNotify(0x500001, false)290 p.createNotify(0x500001, false)
291- readUntil(t, x, func() bool { return x.Slot(0x500001) != 0 })291+ readUntil(t, x, func() bool { return x.tab.lookup(0x500001) != 0 })
292- h.EXPECT().Dissociate(x.Slot(0x500001), true).Once()292+ h.EXPECT().Dissociate(x.tab.lookup(0x500001), true).Once()
293 src.EXPECT().Remove().Once()293 src.EXPECT().Remove().Once()
294 p.hangup()294 p.hangup()
295 readUntil(t, x, func() bool { return x.closed })295 readUntil(t, x, func() bool { return x.closed })

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