detached 1f211c0

Delegate CEF setup to purego-cef2gtk

commit
1f211c04792c68570e2500c7d7f40dff1862ef36
author
brice · · 2026-04-29 18:38 UTC
parent
bda4c7f
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
6 files · +62 -5
M go.mod +3 -0
@@ -7,6 +7,7 @@ require (
77 github.com/andybalholm/brotli v1.2.1
88 github.com/bnema/purego v0.11.0-bnema.2
99 github.com/bnema/purego-cef v0.12.0
10+ github.com/bnema/purego-cef2gtk v0.0.0-00010101000000-000000000000
1011 github.com/bnema/purego-pipewire v0.1.2
1112 github.com/bnema/purego-sqlite v0.1.2
1213 github.com/bnema/puregotk v0.5.1
@@ -77,4 +78,6 @@ require (
7778 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
7879 )
7980
81+replace github.com/bnema/purego-cef2gtk => ../purego-cef2gtk
82+
8083 tool github.com/a-h/templ/cmd/templ
@@ -7,6 +7,7 @@ require (
7 github.com/andybalholm/brotli v1.2.17 github.com/andybalholm/brotli v1.2.1
8 github.com/bnema/purego v0.11.0-bnema.28 github.com/bnema/purego v0.11.0-bnema.2
9 github.com/bnema/purego-cef v0.12.09 github.com/bnema/purego-cef v0.12.0
10+ github.com/bnema/purego-cef2gtk v0.0.0-00010101000000-000000000000
10 github.com/bnema/purego-pipewire v0.1.211 github.com/bnema/purego-pipewire v0.1.2
11 github.com/bnema/purego-sqlite v0.1.212 github.com/bnema/purego-sqlite v0.1.2
12 github.com/bnema/puregotk v0.5.113 github.com/bnema/puregotk v0.5.1
@@ -77,4 +78,6 @@ require (
77 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect78 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
78 )79 )
79 80
81+replace github.com/bnema/purego-cef2gtk => ../purego-cef2gtk
82+
80 tool github.com/a-h/templ/cmd/templ83 tool github.com/a-h/templ/cmd/templ
M go.sum +0 -2
@@ -12,8 +12,6 @@ github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3v
1212 github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E=
1313 github.com/bnema/purego v0.11.0-bnema.2 h1:7pzcdOJ8bGtLrCz6V582oyleP6css1ICQoGgj+/Ve4o=
1414 github.com/bnema/purego v0.11.0-bnema.2/go.mod h1:5BnOIUj+0fvk/vwv45iottFoGfkDT7cnLXnpM2s6Hng=
15-github.com/bnema/purego-cef v0.11.0 h1:tvHx+DfT3Ubcf/e1vIc4M/rJ5Ad9lDa+y04+63+gchM=
16-github.com/bnema/purego-cef v0.11.0/go.mod h1:AIW4lrXr4f2vesO4WnMDN49yAyfivYHV4x6hxpu5SmM=
1715 github.com/bnema/purego-cef v0.12.0 h1:zw45+1e5WUBJdRooNIXmf2u/Zc8XFR5Xb0qwG2krpiM=
1816 github.com/bnema/purego-cef v0.12.0/go.mod h1:AIW4lrXr4f2vesO4WnMDN49yAyfivYHV4x6hxpu5SmM=
1917 github.com/bnema/purego-pipewire v0.1.2 h1:U057iOlpQatwlis6nfrZQJVrT3aGDFdagRLUz+ll7Ik=
@@ -12,8 +12,6 @@ github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3v
12 github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E=12 github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E=
13 github.com/bnema/purego v0.11.0-bnema.2 h1:7pzcdOJ8bGtLrCz6V582oyleP6css1ICQoGgj+/Ve4o=13 github.com/bnema/purego v0.11.0-bnema.2 h1:7pzcdOJ8bGtLrCz6V582oyleP6css1ICQoGgj+/Ve4o=
14 github.com/bnema/purego v0.11.0-bnema.2/go.mod h1:5BnOIUj+0fvk/vwv45iottFoGfkDT7cnLXnpM2s6Hng=14 github.com/bnema/purego v0.11.0-bnema.2/go.mod h1:5BnOIUj+0fvk/vwv45iottFoGfkDT7cnLXnpM2s6Hng=
15-github.com/bnema/purego-cef v0.11.0 h1:tvHx+DfT3Ubcf/e1vIc4M/rJ5Ad9lDa+y04+63+gchM=
16-github.com/bnema/purego-cef v0.11.0/go.mod h1:AIW4lrXr4f2vesO4WnMDN49yAyfivYHV4x6hxpu5SmM=
17 github.com/bnema/purego-cef v0.12.0 h1:zw45+1e5WUBJdRooNIXmf2u/Zc8XFR5Xb0qwG2krpiM=15 github.com/bnema/purego-cef v0.12.0 h1:zw45+1e5WUBJdRooNIXmf2u/Zc8XFR5Xb0qwG2krpiM=
18 github.com/bnema/purego-cef v0.12.0/go.mod h1:AIW4lrXr4f2vesO4WnMDN49yAyfivYHV4x6hxpu5SmM=16 github.com/bnema/purego-cef v0.12.0/go.mod h1:AIW4lrXr4f2vesO4WnMDN49yAyfivYHV4x6hxpu5SmM=
19 github.com/bnema/purego-pipewire v0.1.2 h1:U057iOlpQatwlis6nfrZQJVrT3aGDFdagRLUz+ll7Ik=17 github.com/bnema/purego-pipewire v0.1.2 h1:U057iOlpQatwlis6nfrZQJVrT3aGDFdagRLUz+ll7Ik=
M internal/infrastructure/cef/app.go +6 -0
@@ -4,6 +4,7 @@ import (
44 "strings"
55
66 purecef "github.com/bnema/purego-cef/cef"
7+ cef2gtk "github.com/bnema/purego-cef2gtk"
78
89 "github.com/bnema/dumber/internal/logging"
910 )
@@ -41,6 +42,11 @@ func configureCommandLine(commandLine purecef.CommandLine) {
4142 return
4243 }
4344
45+ // Delegate Wayland accelerated rendering setup to the GTK bridge.
46+ // Preserves any pre-existing ozone-platform value (e.g. set via
47+ // CEF command-line args) — the bridge is a no-op when already present.
48+ cef2gtk.ConfigureCommandLine(commandLine, cef2gtk.CommandLineOptions{})
49+
4450 // Enable Chromium's built-in smooth scrolling animation — without this,
4551 // mouse wheel scroll jumps in discrete steps with no momentum/easing.
4652 commandLine.AppendSwitch("enable-smooth-scrolling")
@@ -4,6 +4,7 @@ import (
4 "strings"4 "strings"
5 5
6 purecef "github.com/bnema/purego-cef/cef"6 purecef "github.com/bnema/purego-cef/cef"
7+ cef2gtk "github.com/bnema/purego-cef2gtk"
7 8
8 "github.com/bnema/dumber/internal/logging"9 "github.com/bnema/dumber/internal/logging"
9 )10 )
@@ -41,6 +42,11 @@ func configureCommandLine(commandLine purecef.CommandLine) {
41 return42 return
42 }43 }
43 44
45+ // Delegate Wayland accelerated rendering setup to the GTK bridge.
46+ // Preserves any pre-existing ozone-platform value (e.g. set via
47+ // CEF command-line args) — the bridge is a no-op when already present.
48+ cef2gtk.ConfigureCommandLine(commandLine, cef2gtk.CommandLineOptions{})
49+
44 // Enable Chromium's built-in smooth scrolling animation — without this,50 // Enable Chromium's built-in smooth scrolling animation — without this,
45 // mouse wheel scroll jumps in discrete steps with no momentum/easing.51 // mouse wheel scroll jumps in discrete steps with no momentum/easing.
46 commandLine.AppendSwitch("enable-smooth-scrolling")52 commandLine.AppendSwitch("enable-smooth-scrolling")
M internal/infrastructure/cef/app_test.go +36 -0
@@ -261,6 +261,42 @@ func TestConfigureCommandLine_WebAuthnUnsafeOverrideRemovesSwitchWhenOnlyWebAuth
261261 }
262262 }
263263
264+func TestConfigureCommandLine_SetsOzonePlatformToWayland(t *testing.T) {
265+ commandLine := newMutableCommandLineStub()
266+ configureCommandLine(commandLine)
267+
268+ if got := commandLine.GetSwitchValue("ozone-platform"); got != "wayland" {
269+ t.Fatalf("ozone-platform = %q, want %q", got, "wayland")
270+ }
271+
272+ // Dumber-specific switches must remain present.
273+ if !commandLine.HasSwitch("enable-smooth-scrolling") {
274+ t.Fatal("expected enable-smooth-scrolling switch to be present")
275+ }
276+ if got := commandLine.GetSwitchValue("autoplay-policy"); got != "no-user-gesture-required" {
277+ t.Fatalf("autoplay-policy = %q, want %q", got, "no-user-gesture-required")
278+ }
279+}
280+
281+func TestConfigureCommandLine_PreservesExistingOzonePlatform(t *testing.T) {
282+ commandLine := newMutableCommandLineStub()
283+ commandLine.AppendSwitchWithValue("ozone-platform", "x11")
284+
285+ configureCommandLine(commandLine)
286+
287+ if got := commandLine.GetSwitchValue("ozone-platform"); got != "x11" {
288+ t.Fatalf("ozone-platform = %q, want %q (bridge preserves existing value)", got, "x11")
289+ }
290+
291+ // Dumber-specific switches must remain present.
292+ if !commandLine.HasSwitch("enable-smooth-scrolling") {
293+ t.Fatal("expected enable-smooth-scrolling switch to be present")
294+ }
295+ if got := commandLine.GetSwitchValue("autoplay-policy"); got != "no-user-gesture-required" {
296+ t.Fatalf("autoplay-policy = %q, want %q", got, "no-user-gesture-required")
297+ }
298+}
299+
264300 func TestAppendUniqueCommaSeparatedSwitchValues(t *testing.T) {
265301 t.Run("trims whitespace and skips empty existing values", func(t *testing.T) {
266302 commandLine := newMutableCommandLineStub()
@@ -261,6 +261,42 @@ func TestConfigureCommandLine_WebAuthnUnsafeOverrideRemovesSwitchWhenOnlyWebAuth
261 }261 }
262 }262 }
263 263
264+func TestConfigureCommandLine_SetsOzonePlatformToWayland(t *testing.T) {
265+ commandLine := newMutableCommandLineStub()
266+ configureCommandLine(commandLine)
267+
268+ if got := commandLine.GetSwitchValue("ozone-platform"); got != "wayland" {
269+ t.Fatalf("ozone-platform = %q, want %q", got, "wayland")
270+ }
271+
272+ // Dumber-specific switches must remain present.
273+ if !commandLine.HasSwitch("enable-smooth-scrolling") {
274+ t.Fatal("expected enable-smooth-scrolling switch to be present")
275+ }
276+ if got := commandLine.GetSwitchValue("autoplay-policy"); got != "no-user-gesture-required" {
277+ t.Fatalf("autoplay-policy = %q, want %q", got, "no-user-gesture-required")
278+ }
279+}
280+
281+func TestConfigureCommandLine_PreservesExistingOzonePlatform(t *testing.T) {
282+ commandLine := newMutableCommandLineStub()
283+ commandLine.AppendSwitchWithValue("ozone-platform", "x11")
284+
285+ configureCommandLine(commandLine)
286+
287+ if got := commandLine.GetSwitchValue("ozone-platform"); got != "x11" {
288+ t.Fatalf("ozone-platform = %q, want %q (bridge preserves existing value)", got, "x11")
289+ }
290+
291+ // Dumber-specific switches must remain present.
292+ if !commandLine.HasSwitch("enable-smooth-scrolling") {
293+ t.Fatal("expected enable-smooth-scrolling switch to be present")
294+ }
295+ if got := commandLine.GetSwitchValue("autoplay-policy"); got != "no-user-gesture-required" {
296+ t.Fatalf("autoplay-policy = %q, want %q", got, "no-user-gesture-required")
297+ }
298+}
299+
264 func TestAppendUniqueCommaSeparatedSwitchValues(t *testing.T) {300 func TestAppendUniqueCommaSeparatedSwitchValues(t *testing.T) {
265 t.Run("trims whitespace and skips empty existing values", func(t *testing.T) {301 t.Run("trims whitespace and skips empty existing values", func(t *testing.T) {
266 commandLine := newMutableCommandLineStub()302 commandLine := newMutableCommandLineStub()
M internal/infrastructure/cef/factory.go +4 -3
@@ -7,6 +7,7 @@ import (
77 "time"
88
99 purecef "github.com/bnema/purego-cef/cef"
10+ cef2gtk "github.com/bnema/purego-cef2gtk"
1011
1112 "github.com/bnema/dumber/internal/application/port"
1213 "github.com/bnema/dumber/internal/logging"
@@ -77,9 +78,9 @@ func (f *WebViewFactory) setDefaultBackgroundColor(r, g, b, a float64) {
7778 func (f *WebViewFactory) Create(ctx context.Context) (port.WebView, error) {
7879 wv := f.newWebView(ctx)
7980
80- // Configure WindowInfo for off-screen rendering (OSR).
81+ // Delegate windowless/shared-texture setup to the GTK bridge.
8182 windowInfo := purecef.NewWindowInfo()
82- windowInfo.WindowlessRenderingEnabled = 1
83+ cef2gtk.ConfigureWindowInfo(&windowInfo, cef2gtk.WindowInfoOptions{})
8384 if externalBeginFrameEnabled() {
8485 windowInfo.ExternalBeginFrameEnabled = 1
8586 }
@@ -323,7 +324,7 @@ func (f *WebViewFactory) CreateRelated(ctx context.Context, parentID port.WebVie
323324 popupWV.markNativePopupCandidate(parent)
324325
325326 windowInfo := purecef.NewWindowInfo()
326- windowInfo.WindowlessRenderingEnabled = 1
327+ cef2gtk.ConfigureWindowInfo(&windowInfo, cef2gtk.WindowInfoOptions{})
327328 if externalBeginFrameEnabled() {
328329 windowInfo.ExternalBeginFrameEnabled = 1
329330 }
@@ -7,6 +7,7 @@ import (
7 "time"7 "time"
8 8
9 purecef "github.com/bnema/purego-cef/cef"9 purecef "github.com/bnema/purego-cef/cef"
10+ cef2gtk "github.com/bnema/purego-cef2gtk"
10 11
11 "github.com/bnema/dumber/internal/application/port"12 "github.com/bnema/dumber/internal/application/port"
12 "github.com/bnema/dumber/internal/logging"13 "github.com/bnema/dumber/internal/logging"
@@ -77,9 +78,9 @@ func (f *WebViewFactory) setDefaultBackgroundColor(r, g, b, a float64) {
77 func (f *WebViewFactory) Create(ctx context.Context) (port.WebView, error) {78 func (f *WebViewFactory) Create(ctx context.Context) (port.WebView, error) {
78 wv := f.newWebView(ctx)79 wv := f.newWebView(ctx)
79 80
80- // Configure WindowInfo for off-screen rendering (OSR).81+ // Delegate windowless/shared-texture setup to the GTK bridge.
81 windowInfo := purecef.NewWindowInfo()82 windowInfo := purecef.NewWindowInfo()
82- windowInfo.WindowlessRenderingEnabled = 183+ cef2gtk.ConfigureWindowInfo(&windowInfo, cef2gtk.WindowInfoOptions{})
83 if externalBeginFrameEnabled() {84 if externalBeginFrameEnabled() {
84 windowInfo.ExternalBeginFrameEnabled = 185 windowInfo.ExternalBeginFrameEnabled = 1
85 }86 }
@@ -323,7 +324,7 @@ func (f *WebViewFactory) CreateRelated(ctx context.Context, parentID port.WebVie
323 popupWV.markNativePopupCandidate(parent)324 popupWV.markNativePopupCandidate(parent)
324 325
325 windowInfo := purecef.NewWindowInfo()326 windowInfo := purecef.NewWindowInfo()
326- windowInfo.WindowlessRenderingEnabled = 1327+ cef2gtk.ConfigureWindowInfo(&windowInfo, cef2gtk.WindowInfoOptions{})
327 if externalBeginFrameEnabled() {328 if externalBeginFrameEnabled() {
328 windowInfo.ExternalBeginFrameEnabled = 1329 windowInfo.ExternalBeginFrameEnabled = 1
329 }330 }
M internal/infrastructure/cef/factory_resize_test.go +13 -0
@@ -6,6 +6,7 @@ import (
66 "time"
77
88 purecef "github.com/bnema/purego-cef/cef"
9+ cef2gtk "github.com/bnema/purego-cef2gtk"
910 "github.com/stretchr/testify/require"
1011 )
1112
@@ -29,6 +30,15 @@ func TestNotifyBrowserResize_CallsWasResizedThenInvalidate(t *testing.T) {
2930 require.Equal(t, []string{"WasResized", "Invalidate"}, host.calls)
3031 }
3132
33+func TestConfigureWindowInfo_SetsAcceleratedWindowlessAndSharedTexture(t *testing.T) {
34+ info := purecef.NewWindowInfo()
35+
36+ cef2gtk.ConfigureWindowInfo(&info, cef2gtk.WindowInfoOptions{})
37+
38+ require.Equal(t, int32(1), info.WindowlessRenderingEnabled)
39+ require.Equal(t, int32(1), info.SharedTextureEnabled)
40+}
41+
3242 func TestPostPendingBrowserCreate_RetriesWhenPostingTaskFails(t *testing.T) {
3343 oldNewTask := cefNewTask
3444 oldPostTask := cefPostTask
@@ -75,10 +85,13 @@ func TestPostPendingBrowserCreate_RetriesWhenPostingTaskFails(t *testing.T) {
7585 require.NotNil(t, windowInfo)
7686 require.NotNil(t, settings)
7787 require.Equal(t, "about:blank", url)
88+ require.Equal(t, int32(1), windowInfo.WindowlessRenderingEnabled)
89+ require.Equal(t, int32(1), windowInfo.SharedTextureEnabled)
7890 return 1
7991 }
8092
8193 windowInfo := purecef.NewWindowInfo()
94+ cef2gtk.ConfigureWindowInfo(&windowInfo, cef2gtk.WindowInfoOptions{})
8295 settings := purecef.NewBrowserSettings()
8396 wv := &WebView{
8497 ctx: context.Background(),
@@ -6,6 +6,7 @@ import (
6 "time"6 "time"
7 7
8 purecef "github.com/bnema/purego-cef/cef"8 purecef "github.com/bnema/purego-cef/cef"
9+ cef2gtk "github.com/bnema/purego-cef2gtk"
9 "github.com/stretchr/testify/require"10 "github.com/stretchr/testify/require"
10 )11 )
11 12
@@ -29,6 +30,15 @@ func TestNotifyBrowserResize_CallsWasResizedThenInvalidate(t *testing.T) {
29 require.Equal(t, []string{"WasResized", "Invalidate"}, host.calls)30 require.Equal(t, []string{"WasResized", "Invalidate"}, host.calls)
30 }31 }
31 32
33+func TestConfigureWindowInfo_SetsAcceleratedWindowlessAndSharedTexture(t *testing.T) {
34+ info := purecef.NewWindowInfo()
35+
36+ cef2gtk.ConfigureWindowInfo(&info, cef2gtk.WindowInfoOptions{})
37+
38+ require.Equal(t, int32(1), info.WindowlessRenderingEnabled)
39+ require.Equal(t, int32(1), info.SharedTextureEnabled)
40+}
41+
32 func TestPostPendingBrowserCreate_RetriesWhenPostingTaskFails(t *testing.T) {42 func TestPostPendingBrowserCreate_RetriesWhenPostingTaskFails(t *testing.T) {
33 oldNewTask := cefNewTask43 oldNewTask := cefNewTask
34 oldPostTask := cefPostTask44 oldPostTask := cefPostTask
@@ -75,10 +85,13 @@ func TestPostPendingBrowserCreate_RetriesWhenPostingTaskFails(t *testing.T) {
75 require.NotNil(t, windowInfo)85 require.NotNil(t, windowInfo)
76 require.NotNil(t, settings)86 require.NotNil(t, settings)
77 require.Equal(t, "about:blank", url)87 require.Equal(t, "about:blank", url)
88+ require.Equal(t, int32(1), windowInfo.WindowlessRenderingEnabled)
89+ require.Equal(t, int32(1), windowInfo.SharedTextureEnabled)
78 return 190 return 1
79 }91 }
80 92
81 windowInfo := purecef.NewWindowInfo()93 windowInfo := purecef.NewWindowInfo()
94+ cef2gtk.ConfigureWindowInfo(&windowInfo, cef2gtk.WindowInfoOptions{})
82 settings := purecef.NewBrowserSettings()95 settings := purecef.NewBrowserSettings()
83 wv := &WebView{96 wv := &WebView{
84 ctx: context.Background(),97 ctx: context.Background(),

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 8

Tags 42

↑↓ moveenter openesc close