detached 298ec90

refactor(wayland): use go-wayland-bindings for protocol bindings (#1)

Replaces the hand-written Wayland bindings with go-wayland-bindings.

Reviewed-on: https://git.bnema.dev/bnema/nefercap/pulls/1
commit
298ec90558ea8441d81bfeaf6842027dc479e51d
author
brice · · 2026-10-02 10:30 UTC
committer
bnema · · 2026-10-02 10:30 UTC
parent
7e79979
signature
unsigned
8 files · +30 -15
M go.mod +1 -0
@@ -3,6 +3,7 @@ module github.com/bnema/nefercap
33 go 1.27
44
55 require (
6+ github.com/bnema/go-wayland-bindings v0.1.0
67 github.com/bnema/nefergui v0.3.0
78 github.com/bnema/purego-libwayland v0.8.0
89 github.com/bnema/wlturbo v0.5.0
@@ -3,6 +3,7 @@ module github.com/bnema/nefercap
3 go 1.273 go 1.27
4 4
5 require (5 require (
6+ github.com/bnema/go-wayland-bindings v0.1.0
6 github.com/bnema/nefergui v0.3.07 github.com/bnema/nefergui v0.3.0
7 github.com/bnema/purego-libwayland v0.8.08 github.com/bnema/purego-libwayland v0.8.0
8 github.com/bnema/wlturbo v0.5.09 github.com/bnema/wlturbo v0.5.0
M go.sum +2 -0 generated file: collapsed
M internal/adapters/wayland/copycapture.go +2 -2
@@ -5,8 +5,8 @@ import (
55 "fmt"
66 "time"
77
8- "github.com/bnema/wlturbo/protocol/imagecapturesource"
9- "github.com/bnema/wlturbo/protocol/imagecopycapture"
8+ imagecapturesource "github.com/bnema/go-wayland-bindings/client/extimagecapturesource"
9+ imagecopycapture "github.com/bnema/go-wayland-bindings/client/extimagecopycapture"
1010
1111 "github.com/bnema/nefercap/internal/ports"
1212 )
@@ -5,8 +5,8 @@ import (
5 "fmt"5 "fmt"
6 "time"6 "time"
7 7
8- "github.com/bnema/wlturbo/protocol/imagecapturesource"8+ imagecapturesource "github.com/bnema/go-wayland-bindings/client/extimagecapturesource"
9- "github.com/bnema/wlturbo/protocol/imagecopycapture"9+ imagecopycapture "github.com/bnema/go-wayland-bindings/client/extimagecopycapture"
10 10
11 "github.com/bnema/nefercap/internal/ports"11 "github.com/bnema/nefercap/internal/ports"
12 )12 )
M internal/adapters/wayland/extensions.go +15 -6
@@ -4,13 +4,13 @@ import (
44 "context"
55 "errors"
66 "fmt"
7+ "reflect"
78 "sync"
89 "time"
910
11+ imagecapturesource "github.com/bnema/go-wayland-bindings/client/extimagecapturesource"
12+ "github.com/bnema/go-wayland-bindings/client/xdgoutput"
1013 "github.com/bnema/wlturbo"
11- "github.com/bnema/wlturbo/protocol/core"
12- "github.com/bnema/wlturbo/protocol/imagecapturesource"
13- "github.com/bnema/wlturbo/protocol/xdgoutput"
1414
1515 "github.com/bnema/nefercap/internal/ports"
1616 )
@@ -490,12 +490,15 @@ func (l *layerProxy) hand() (state layerState, reason uint32, violation bool) {
490490 // thread-safe, non-blocking work such as cancelling a context. It never sees
491491 // the token. The attachment object is never destroyed after a success: that
492492 // would not release the exclusion, and closing the connection ends it.
493-func AuthorizeLayer(ctx context.Context, display *wlturbo.Display, surface *core.Surface, token string, onDetached func(error)) error {
493+//
494+// surface is the layer's wl_surface proxy, whichever bindings created it; it
495+// must belong to display, and nothing else may dispatch display meanwhile.
496+func AuthorizeLayer(ctx context.Context, display *wlturbo.Display, surface wlturbo.Proxy, token string, onDetached func(error)) error {
494497 return authorizeLayer(ctx, display, surface, token, onDetached, DefaultAttachTimeout)
495498 }
496499
497-func authorizeLayer(ctx context.Context, display *wlturbo.Display, surface *core.Surface, token string, onDetached func(error), timeout time.Duration) error {
498- if display == nil || surface == nil || surface.Context() != display.Context() {
500+func authorizeLayer(ctx context.Context, display *wlturbo.Display, surface wlturbo.Proxy, token string, onDetached func(error), timeout time.Duration) error {
501+ if display == nil || surface == nil || nilPointer(surface) || surface.Context() != display.Context() {
499502 return errors.New("wayland: authorize layer: surface does not belong to display")
500503 }
501504 if !validToken(token) {
@@ -552,6 +555,12 @@ func authorizeLayer(ctx context.Context, display *wlturbo.Display, surface *core
552555 return nil
553556 }
554557
558+// nilPointer reports a typed nil proxy, which is a non-nil interface.
559+func nilPointer(p wlturbo.Proxy) bool {
560+ v := reflect.ValueOf(p)
561+ return v.Kind() == reflect.Pointer && v.IsNil()
562+}
563+
555564 // validToken accepts exactly the compositor's token format.
556565 func validToken(token string) bool {
557566 if len(token) != tokenLen {
@@ -4,13 +4,13 @@ import (
4 "context"4 "context"
5 "errors"5 "errors"
6 "fmt"6 "fmt"
7+ "reflect"
7 "sync"8 "sync"
8 "time"9 "time"
9 10
11+ imagecapturesource "github.com/bnema/go-wayland-bindings/client/extimagecapturesource"
12+ "github.com/bnema/go-wayland-bindings/client/xdgoutput"
10 "github.com/bnema/wlturbo"13 "github.com/bnema/wlturbo"
11- "github.com/bnema/wlturbo/protocol/core"
12- "github.com/bnema/wlturbo/protocol/imagecapturesource"
13- "github.com/bnema/wlturbo/protocol/xdgoutput"
14 14
15 "github.com/bnema/nefercap/internal/ports"15 "github.com/bnema/nefercap/internal/ports"
16 )16 )
@@ -490,12 +490,15 @@ func (l *layerProxy) hand() (state layerState, reason uint32, violation bool) {
490 // thread-safe, non-blocking work such as cancelling a context. It never sees490 // thread-safe, non-blocking work such as cancelling a context. It never sees
491 // the token. The attachment object is never destroyed after a success: that491 // the token. The attachment object is never destroyed after a success: that
492 // would not release the exclusion, and closing the connection ends it.492 // would not release the exclusion, and closing the connection ends it.
493-func AuthorizeLayer(ctx context.Context, display *wlturbo.Display, surface *core.Surface, token string, onDetached func(error)) error {493+//
494+// surface is the layer's wl_surface proxy, whichever bindings created it; it
495+// must belong to display, and nothing else may dispatch display meanwhile.
496+func AuthorizeLayer(ctx context.Context, display *wlturbo.Display, surface wlturbo.Proxy, token string, onDetached func(error)) error {
494 return authorizeLayer(ctx, display, surface, token, onDetached, DefaultAttachTimeout)497 return authorizeLayer(ctx, display, surface, token, onDetached, DefaultAttachTimeout)
495 }498 }
496 499
497-func authorizeLayer(ctx context.Context, display *wlturbo.Display, surface *core.Surface, token string, onDetached func(error), timeout time.Duration) error {500+func authorizeLayer(ctx context.Context, display *wlturbo.Display, surface wlturbo.Proxy, token string, onDetached func(error), timeout time.Duration) error {
498- if display == nil || surface == nil || surface.Context() != display.Context() {501+ if display == nil || surface == nil || nilPointer(surface) || surface.Context() != display.Context() {
499 return errors.New("wayland: authorize layer: surface does not belong to display")502 return errors.New("wayland: authorize layer: surface does not belong to display")
500 }503 }
501 if !validToken(token) {504 if !validToken(token) {
@@ -552,6 +555,12 @@ func authorizeLayer(ctx context.Context, display *wlturbo.Display, surface *core
552 return nil555 return nil
553 }556 }
554 557
558+// nilPointer reports a typed nil proxy, which is a non-nil interface.
559+func nilPointer(p wlturbo.Proxy) bool {
560+ v := reflect.ValueOf(p)
561+ return v.Kind() == reflect.Pointer && v.IsNil()
562+}
563+
555 // validToken accepts exactly the compositor's token format.564 // validToken accepts exactly the compositor's token format.
556 func validToken(token string) bool {565 func validToken(token string) bool {
557 if len(token) != tokenLen {566 if len(token) != tokenLen {
M internal/adapters/wayland/extensions_test.go +4 -1
@@ -9,8 +9,8 @@ import (
99 "testing"
1010 "time"
1111
12+ core "github.com/bnema/go-wayland-bindings/client/wayland"
1213 "github.com/bnema/wlturbo"
13- "github.com/bnema/wlturbo/protocol/core"
1414 "github.com/stretchr/testify/require"
1515
1616 "github.com/bnema/nefercap/internal/adapters/wayland/testserver"
@@ -225,6 +225,9 @@ func TestAuthorizeLayerRejectsBadInput(t *testing.T) {
225225 require.Error(t, AuthorizeLayer(context.Background(), nil, nil, testserver.Token, nil))
226226 _, srv := newSource(t, extConfig())
227227 display, surface := hudConnection(t, srv)
228+ require.Error(t, AuthorizeLayer(context.Background(), display, (*core.Surface)(nil), testserver.Token, nil), "typed nil surface")
229+ _, other := hudConnection(t, srv)
230+ require.Error(t, AuthorizeLayer(context.Background(), display, other, testserver.Token, nil), "surface of another connection")
228231 for _, tok := range []string{"", "has space", testserver.Token[:63], testserver.Token + "0", testserver.Token[:63] + "G", testserver.Token[:63] + "A"} {
229232 require.Error(t, AuthorizeLayer(context.Background(), display, surface, tok, nil), "%q", tok)
230233 }
@@ -9,8 +9,8 @@ import (
9 "testing"9 "testing"
10 "time"10 "time"
11 11
12+ core "github.com/bnema/go-wayland-bindings/client/wayland"
12 "github.com/bnema/wlturbo"13 "github.com/bnema/wlturbo"
13- "github.com/bnema/wlturbo/protocol/core"
14 "github.com/stretchr/testify/require"14 "github.com/stretchr/testify/require"
15 15
16 "github.com/bnema/nefercap/internal/adapters/wayland/testserver"16 "github.com/bnema/nefercap/internal/adapters/wayland/testserver"
@@ -225,6 +225,9 @@ func TestAuthorizeLayerRejectsBadInput(t *testing.T) {
225 require.Error(t, AuthorizeLayer(context.Background(), nil, nil, testserver.Token, nil))225 require.Error(t, AuthorizeLayer(context.Background(), nil, nil, testserver.Token, nil))
226 _, srv := newSource(t, extConfig())226 _, srv := newSource(t, extConfig())
227 display, surface := hudConnection(t, srv)227 display, surface := hudConnection(t, srv)
228+ require.Error(t, AuthorizeLayer(context.Background(), display, (*core.Surface)(nil), testserver.Token, nil), "typed nil surface")
229+ _, other := hudConnection(t, srv)
230+ require.Error(t, AuthorizeLayer(context.Background(), display, other, testserver.Token, nil), "surface of another connection")
228 for _, tok := range []string{"", "has space", testserver.Token[:63], testserver.Token + "0", testserver.Token[:63] + "G", testserver.Token[:63] + "A"} {231 for _, tok := range []string{"", "has space", testserver.Token[:63], testserver.Token + "0", testserver.Token[:63] + "G", testserver.Token[:63] + "A"} {
229 require.Error(t, AuthorizeLayer(context.Background(), display, surface, tok, nil), "%q", tok)232 require.Error(t, AuthorizeLayer(context.Background(), display, surface, tok, nil), "%q", tok)
230 }233 }
M internal/adapters/wayland/shm.go +1 -1
@@ -4,8 +4,8 @@ import (
44 "errors"
55 "fmt"
66
7+ core "github.com/bnema/go-wayland-bindings/client/wayland"
78 "github.com/bnema/wlturbo"
8- "github.com/bnema/wlturbo/protocol/core"
99 "golang.org/x/sys/unix"
1010
1111 "github.com/bnema/nefercap/internal/ports"
@@ -4,8 +4,8 @@ import (
4 "errors"4 "errors"
5 "fmt"5 "fmt"
6 6
7+ core "github.com/bnema/go-wayland-bindings/client/wayland"
7 "github.com/bnema/wlturbo"8 "github.com/bnema/wlturbo"
8- "github.com/bnema/wlturbo/protocol/core"
9 "golang.org/x/sys/unix"9 "golang.org/x/sys/unix"
10 10
11 "github.com/bnema/nefercap/internal/ports"11 "github.com/bnema/nefercap/internal/ports"
M internal/adapters/wayland/source.go +4 -4
@@ -18,11 +18,11 @@ import (
1818 "sync/atomic"
1919 "time"
2020
21+ imagecapturesource "github.com/bnema/go-wayland-bindings/client/extimagecapturesource"
22+ imagecopycapture "github.com/bnema/go-wayland-bindings/client/extimagecopycapture"
23+ core "github.com/bnema/go-wayland-bindings/client/wayland"
24+ "github.com/bnema/go-wayland-bindings/client/xdgoutput"
2125 "github.com/bnema/wlturbo"
22- "github.com/bnema/wlturbo/protocol/core"
23- "github.com/bnema/wlturbo/protocol/imagecapturesource"
24- "github.com/bnema/wlturbo/protocol/imagecopycapture"
25- "github.com/bnema/wlturbo/protocol/xdgoutput"
2626 "github.com/bnema/zerowrap"
2727
2828 "github.com/bnema/nefercap/internal/logging"
@@ -18,11 +18,11 @@ import (
18 "sync/atomic"18 "sync/atomic"
19 "time"19 "time"
20 20
21+ imagecapturesource "github.com/bnema/go-wayland-bindings/client/extimagecapturesource"
22+ imagecopycapture "github.com/bnema/go-wayland-bindings/client/extimagecopycapture"
23+ core "github.com/bnema/go-wayland-bindings/client/wayland"
24+ "github.com/bnema/go-wayland-bindings/client/xdgoutput"
21 "github.com/bnema/wlturbo"25 "github.com/bnema/wlturbo"
22- "github.com/bnema/wlturbo/protocol/core"
23- "github.com/bnema/wlturbo/protocol/imagecapturesource"
24- "github.com/bnema/wlturbo/protocol/imagecopycapture"
25- "github.com/bnema/wlturbo/protocol/xdgoutput"
26 "github.com/bnema/zerowrap"26 "github.com/bnema/zerowrap"
27 27
28 "github.com/bnema/nefercap/internal/logging"28 "github.com/bnema/nefercap/internal/logging"
M internal/adapters/wayland/workspaces.go +1 -1
@@ -8,7 +8,7 @@ import (
88 "unicode"
99 "unicode/utf8"
1010
11- extworkspace "github.com/bnema/wlturbo/protocol/workspace"
11+ "github.com/bnema/go-wayland-bindings/client/extworkspace"
1212
1313 "github.com/bnema/nefercap/internal/ports"
1414 )
@@ -8,7 +8,7 @@ import (
8 "unicode"8 "unicode"
9 "unicode/utf8"9 "unicode/utf8"
10 10
11- extworkspace "github.com/bnema/wlturbo/protocol/workspace"11+ "github.com/bnema/go-wayland-bindings/client/extworkspace"
12 12
13 "github.com/bnema/nefercap/internal/ports"13 "github.com/bnema/nefercap/internal/ports"
14 )14 )

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 2

Tags 9

↑↓ moveenter openesc close