detached 4da3a75

refactor(cef): address CodeRabbit findings — DRY monitor, sentinel framerate, cleanup

- Extract getPrimaryMonitor() helper to deduplicate GDK monitor lookup
  between detectHiDPIScale and detectMonitorRefreshRate
- Use 0 as sentinel for auto-detect frame rate (previously checked == 60
  which silently overrode explicit user config of 60fps)
- Round milliHz to nearest Hz instead of truncating (59999 → 60, not 59)
- Remove dead enableAudioHandler field — AudioHandler is now always
  active since it's required for media decoding pipeline
- Remove extra blank line in app.go
- Update test to reflect always-on AudioHandler
commit
4da3a75a770c7baf71605d0d2cbba59d1c2e5cb3
author
brice · · 2026-03-23 19:19 UTC
committer
brice · · 2026-03-28 04:38 UTC
parent
73a1699
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
6 files · +29 -37
M internal/infrastructure/cef/app.go +0 -1
@@ -38,7 +38,6 @@ func (a *dumberApp) OnBeforeCommandLineProcessing(processType string, commandLin
3838 // Chromium blocks them, showing an infinite spinner.
3939 commandLine.AppendSwitchWithValue("autoplay-policy", "no-user-gesture-required")
4040
41-
4241 cmdline := commandLine.GetCommandLineString()
4342 if len(cmdline) > maxCmdLineLogLen {
4443 runes := []rune(cmdline)
@@ -38,7 +38,6 @@ func (a *dumberApp) OnBeforeCommandLineProcessing(processType string, commandLin
38 // Chromium blocks them, showing an infinite spinner.38 // Chromium blocks them, showing an infinite spinner.
39 commandLine.AppendSwitchWithValue("autoplay-policy", "no-user-gesture-required")39 commandLine.AppendSwitchWithValue("autoplay-policy", "no-user-gesture-required")
40 40
41-
42 cmdline := commandLine.GetCommandLineString()41 cmdline := commandLine.GetCommandLineString()
43 if len(cmdline) > maxCmdLineLogLen {42 if len(cmdline) > maxCmdLineLogLen {
44 runes := []rune(cmdline)43 runes := []rune(cmdline)
M internal/infrastructure/cef/engine_init.go +24 -25
@@ -127,18 +127,20 @@ func wireEngine(
127127
128128 scale := detectHiDPIScale(logger)
129129
130- // If frame rate is the static default (60), try to match the monitor's
131- // actual refresh rate for smoother scrolling on high-refresh displays.
132- if windowlessFrameRate == 60 {
130+ // Auto-detect frame rate from monitor when config uses the default (0 = auto).
131+ // CEFWindowlessFrameRate() returns 0 when the user hasn't explicitly set a value,
132+ // allowing us to match the monitor's refresh rate for smoother scrolling.
133+ if windowlessFrameRate == 0 {
133134 if hz := detectMonitorRefreshRate(logger); hz > 0 {
134135 windowlessFrameRate = hz
136+ } else {
137+ windowlessFrameRate = 60 // safe fallback
135138 }
136139 }
137140
138141 factory := newWebViewFactory(eng, gl, webViewFactoryOptions{
139142 scale: scale,
140143 windowlessFrameRate: windowlessFrameRate,
141- enableAudioHandler: cfg.EnableAudioHandler,
142144 enableContextMenuHandler: cfg.EnableContextMenuHandler,
143145 })
144146 pool := newWebViewPool(factory)
@@ -165,25 +167,32 @@ func wireEngine(
165167 return eng, nil
166168 }
167169
168-// detectHiDPIScale queries the primary GDK monitor for its scale factor.
169-func detectHiDPIScale(logger *zerolog.Logger) int32 {
170+// getPrimaryMonitor returns the primary GDK monitor, or nil if unavailable.
171+func getPrimaryMonitor() *gdk.Monitor {
170172 display := gdk.DisplayGetDefault()
171173 if display == nil {
172- logger.Debug().Msg("cef: no GDK display available, using scale=1")
173- return 1
174+ return nil
174175 }
175176 monitors := display.GetMonitors()
176177 if monitors == nil {
177- logger.Debug().Msg("cef: no monitors found, using scale=1")
178- return 1
178+ return nil
179179 }
180180 obj := monitors.GetObject(0)
181181 if obj == nil {
182- logger.Debug().Msg("cef: primary monitor not available, using scale=1")
183- return 1
182+ return nil
184183 }
185184 mon := &gdk.Monitor{}
186185 mon.SetGoPointer(obj.GoPointer())
186+ return mon
187+}
188+
189+// detectHiDPIScale queries the primary GDK monitor for its scale factor.
190+func detectHiDPIScale(logger *zerolog.Logger) int32 {
191+ mon := getPrimaryMonitor()
192+ if mon == nil {
193+ logger.Debug().Msg("cef: no primary monitor, using scale=1")
194+ return 1
195+ }
187196 if s := mon.GetScaleFactor(); s > 0 {
188197 logger.Info().Int32("scale", int32(s)).Msg("cef: detected HiDPI scale from monitor")
189198 return int32(s)
@@ -195,25 +204,15 @@ func detectHiDPIScale(logger *zerolog.Logger) int32 {
195204 // detectMonitorRefreshRate queries the primary GDK monitor for its refresh rate.
196205 // Returns the rate in Hz (e.g., 120 for a 120Hz display), or 0 if detection fails.
197206 func detectMonitorRefreshRate(logger *zerolog.Logger) int32 {
198- display := gdk.DisplayGetDefault()
199- if display == nil {
200- return 0
201- }
202- monitors := display.GetMonitors()
203- if monitors == nil {
207+ mon := getPrimaryMonitor()
208+ if mon == nil {
204209 return 0
205210 }
206- obj := monitors.GetObject(0)
207- if obj == nil {
208- return 0
209- }
210- mon := &gdk.Monitor{}
211- mon.SetGoPointer(obj.GoPointer())
212211 milliHz := mon.GetRefreshRate()
213212 if milliHz <= 0 {
214213 return 0
215214 }
216- hz := int32(milliHz / 1000)
215+ hz := int32((milliHz + 500) / 1000) // round to nearest Hz
217216 logger.Info().Int32("refresh_rate_hz", hz).Msg("cef: detected monitor refresh rate")
218217 return hz
219218 }
@@ -127,18 +127,20 @@ func wireEngine(
127 127
128 scale := detectHiDPIScale(logger)128 scale := detectHiDPIScale(logger)
129 129
130- // If frame rate is the static default (60), try to match the monitor's130+ // Auto-detect frame rate from monitor when config uses the default (0 = auto).
131- // actual refresh rate for smoother scrolling on high-refresh displays.131+ // CEFWindowlessFrameRate() returns 0 when the user hasn't explicitly set a value,
132- if windowlessFrameRate == 60 {132+ // allowing us to match the monitor's refresh rate for smoother scrolling.
133+ if windowlessFrameRate == 0 {
133 if hz := detectMonitorRefreshRate(logger); hz > 0 {134 if hz := detectMonitorRefreshRate(logger); hz > 0 {
134 windowlessFrameRate = hz135 windowlessFrameRate = hz
136+ } else {
137+ windowlessFrameRate = 60 // safe fallback
135 }138 }
136 }139 }
137 140
138 factory := newWebViewFactory(eng, gl, webViewFactoryOptions{141 factory := newWebViewFactory(eng, gl, webViewFactoryOptions{
139 scale: scale,142 scale: scale,
140 windowlessFrameRate: windowlessFrameRate,143 windowlessFrameRate: windowlessFrameRate,
141- enableAudioHandler: cfg.EnableAudioHandler,
142 enableContextMenuHandler: cfg.EnableContextMenuHandler,144 enableContextMenuHandler: cfg.EnableContextMenuHandler,
143 })145 })
144 pool := newWebViewPool(factory)146 pool := newWebViewPool(factory)
@@ -165,25 +167,32 @@ func wireEngine(
165 return eng, nil167 return eng, nil
166 }168 }
167 169
168-// detectHiDPIScale queries the primary GDK monitor for its scale factor.170+// getPrimaryMonitor returns the primary GDK monitor, or nil if unavailable.
169-func detectHiDPIScale(logger *zerolog.Logger) int32 {171+func getPrimaryMonitor() *gdk.Monitor {
170 display := gdk.DisplayGetDefault()172 display := gdk.DisplayGetDefault()
171 if display == nil {173 if display == nil {
172- logger.Debug().Msg("cef: no GDK display available, using scale=1")174+ return nil
173- return 1
174 }175 }
175 monitors := display.GetMonitors()176 monitors := display.GetMonitors()
176 if monitors == nil {177 if monitors == nil {
177- logger.Debug().Msg("cef: no monitors found, using scale=1")178+ return nil
178- return 1
179 }179 }
180 obj := monitors.GetObject(0)180 obj := monitors.GetObject(0)
181 if obj == nil {181 if obj == nil {
182- logger.Debug().Msg("cef: primary monitor not available, using scale=1")182+ return nil
183- return 1
184 }183 }
185 mon := &gdk.Monitor{}184 mon := &gdk.Monitor{}
186 mon.SetGoPointer(obj.GoPointer())185 mon.SetGoPointer(obj.GoPointer())
186+ return mon
187+}
188+
189+// detectHiDPIScale queries the primary GDK monitor for its scale factor.
190+func detectHiDPIScale(logger *zerolog.Logger) int32 {
191+ mon := getPrimaryMonitor()
192+ if mon == nil {
193+ logger.Debug().Msg("cef: no primary monitor, using scale=1")
194+ return 1
195+ }
187 if s := mon.GetScaleFactor(); s > 0 {196 if s := mon.GetScaleFactor(); s > 0 {
188 logger.Info().Int32("scale", int32(s)).Msg("cef: detected HiDPI scale from monitor")197 logger.Info().Int32("scale", int32(s)).Msg("cef: detected HiDPI scale from monitor")
189 return int32(s)198 return int32(s)
@@ -195,25 +204,15 @@ func detectHiDPIScale(logger *zerolog.Logger) int32 {
195 // detectMonitorRefreshRate queries the primary GDK monitor for its refresh rate.204 // detectMonitorRefreshRate queries the primary GDK monitor for its refresh rate.
196 // Returns the rate in Hz (e.g., 120 for a 120Hz display), or 0 if detection fails.205 // Returns the rate in Hz (e.g., 120 for a 120Hz display), or 0 if detection fails.
197 func detectMonitorRefreshRate(logger *zerolog.Logger) int32 {206 func detectMonitorRefreshRate(logger *zerolog.Logger) int32 {
198- display := gdk.DisplayGetDefault()207+ mon := getPrimaryMonitor()
199- if display == nil {208+ if mon == nil {
200- return 0
201- }
202- monitors := display.GetMonitors()
203- if monitors == nil {
204 return 0209 return 0
205 }210 }
206- obj := monitors.GetObject(0)
207- if obj == nil {
208- return 0
209- }
210- mon := &gdk.Monitor{}
211- mon.SetGoPointer(obj.GoPointer())
212 milliHz := mon.GetRefreshRate()211 milliHz := mon.GetRefreshRate()
213 if milliHz <= 0 {212 if milliHz <= 0 {
214 return 0213 return 0
215 }214 }
216- hz := int32(milliHz / 1000)215+ hz := int32((milliHz + 500) / 1000) // round to nearest Hz
217 logger.Info().Int32("refresh_rate_hz", hz).Msg("cef: detected monitor refresh rate")216 logger.Info().Int32("refresh_rate_hz", hz).Msg("cef: detected monitor refresh rate")
218 return hz217 return hz
219 }218 }
M internal/infrastructure/cef/factory.go +0 -4
@@ -22,7 +22,6 @@ type WebViewFactory struct {
2222 nextID atomic.Uint64
2323 scale int32
2424 windowlessFrameRate int32
25- enableAudioHandler bool
2625 enableContextMenuHandler bool
2726 bgColor atomic.Uint32 // packed ARGB for BrowserSettings.BackgroundColor
2827 }
@@ -30,7 +29,6 @@ type WebViewFactory struct {
3029 type webViewFactoryOptions struct {
3130 scale int32
3231 windowlessFrameRate int32
33- enableAudioHandler bool
3432 enableContextMenuHandler bool
3533 }
3634
@@ -48,7 +46,6 @@ func newWebViewFactory(engine *Engine, gl *glLoader, opts webViewFactoryOptions)
4846 gl: gl,
4947 scale: opts.scale,
5048 windowlessFrameRate: opts.windowlessFrameRate,
51- enableAudioHandler: opts.enableAudioHandler,
5249 enableContextMenuHandler: opts.enableContextMenuHandler,
5350 }
5451 }
@@ -77,7 +74,6 @@ func (f *WebViewFactory) Create(ctx context.Context) (port.WebView, error) {
7774
7875 handlers := &handlerSet{
7976 wv: wv,
80- enableAudioHandler: f.enableAudioHandler,
8177 enableContextMenuHandler: f.enableContextMenuHandler,
8278 }
8379 wv.handlers = handlers
@@ -22,7 +22,6 @@ type WebViewFactory struct {
22 nextID atomic.Uint6422 nextID atomic.Uint64
23 scale int3223 scale int32
24 windowlessFrameRate int3224 windowlessFrameRate int32
25- enableAudioHandler bool
26 enableContextMenuHandler bool25 enableContextMenuHandler bool
27 bgColor atomic.Uint32 // packed ARGB for BrowserSettings.BackgroundColor26 bgColor atomic.Uint32 // packed ARGB for BrowserSettings.BackgroundColor
28 }27 }
@@ -30,7 +29,6 @@ type WebViewFactory struct {
30 type webViewFactoryOptions struct {29 type webViewFactoryOptions struct {
31 scale int3230 scale int32
32 windowlessFrameRate int3231 windowlessFrameRate int32
33- enableAudioHandler bool
34 enableContextMenuHandler bool32 enableContextMenuHandler bool
35 }33 }
36 34
@@ -48,7 +46,6 @@ func newWebViewFactory(engine *Engine, gl *glLoader, opts webViewFactoryOptions)
48 gl: gl,46 gl: gl,
49 scale: opts.scale,47 scale: opts.scale,
50 windowlessFrameRate: opts.windowlessFrameRate,48 windowlessFrameRate: opts.windowlessFrameRate,
51- enableAudioHandler: opts.enableAudioHandler,
52 enableContextMenuHandler: opts.enableContextMenuHandler,49 enableContextMenuHandler: opts.enableContextMenuHandler,
53 }50 }
54 }51 }
@@ -77,7 +74,6 @@ func (f *WebViewFactory) Create(ctx context.Context) (port.WebView, error) {
77 74
78 handlers := &handlerSet{75 handlers := &handlerSet{
79 wv: wv,76 wv: wv,
80- enableAudioHandler: f.enableAudioHandler,
81 enableContextMenuHandler: f.enableContextMenuHandler,77 enableContextMenuHandler: f.enableContextMenuHandler,
82 }78 }
83 wv.handlers = handlers79 wv.handlers = handlers
M internal/infrastructure/cef/webview_handlers.go +0 -2
@@ -12,7 +12,6 @@ import (
1212 // methods can return the same receiver, avoiding extra allocations.
1313 type handlerSet struct {
1414 wv *WebView
15- enableAudioHandler bool
1615 enableContextMenuHandler bool
1716 }
1817
@@ -453,7 +452,6 @@ func (h *handlerSet) OnAfterCreated(browser purecef.Browser) {
453452 browserID := browser.GetIdentifier()
454453 log.Debug().
455454 Int32("browser_id", browserID).
456- Bool("audio_handler_enabled", h.enableAudioHandler).
457455 Bool("context_menu_handler_enabled", h.enableContextMenuHandler).
458456 Msg("cef: OnAfterCreated")
459457 if h.wv.engine != nil {
@@ -12,7 +12,6 @@ import (
12 // methods can return the same receiver, avoiding extra allocations.12 // methods can return the same receiver, avoiding extra allocations.
13 type handlerSet struct {13 type handlerSet struct {
14 wv *WebView14 wv *WebView
15- enableAudioHandler bool
16 enableContextMenuHandler bool15 enableContextMenuHandler bool
17 }16 }
18 17
@@ -453,7 +452,6 @@ func (h *handlerSet) OnAfterCreated(browser purecef.Browser) {
453 browserID := browser.GetIdentifier()452 browserID := browser.GetIdentifier()
454 log.Debug().453 log.Debug().
455 Int32("browser_id", browserID).454 Int32("browser_id", browserID).
456- Bool("audio_handler_enabled", h.enableAudioHandler).
457 Bool("context_menu_handler_enabled", h.enableContextMenuHandler).455 Bool("context_menu_handler_enabled", h.enableContextMenuHandler).
458 Msg("cef: OnAfterCreated")456 Msg("cef: OnAfterCreated")
459 if h.wv.engine != nil {457 if h.wv.engine != nil {
M internal/infrastructure/cef/webview_handlers_test.go +2 -3
@@ -108,12 +108,11 @@ func TestGetScreenInfoUsesDIPRectAndScale(t *testing.T) {
108108 func TestOptionalHandlersRespectFactoryFlags(t *testing.T) {
109109 h := &handlerSet{}
110110
111- require.Nil(t, h.GetAudioHandler())
111+ // AudioHandler is always enabled (required for media decoding).
112+ require.Same(t, h, h.GetAudioHandler())
112113 require.Nil(t, h.GetContextMenuHandler())
113114
114- h.enableAudioHandler = true
115115 h.enableContextMenuHandler = true
116116
117- require.Same(t, h, h.GetAudioHandler())
118117 require.Same(t, h, h.GetContextMenuHandler())
119118 }
@@ -108,12 +108,11 @@ func TestGetScreenInfoUsesDIPRectAndScale(t *testing.T) {
108 func TestOptionalHandlersRespectFactoryFlags(t *testing.T) {108 func TestOptionalHandlersRespectFactoryFlags(t *testing.T) {
109 h := &handlerSet{}109 h := &handlerSet{}
110 110
111- require.Nil(t, h.GetAudioHandler())111+ // AudioHandler is always enabled (required for media decoding).
112+ require.Same(t, h, h.GetAudioHandler())
112 require.Nil(t, h.GetContextMenuHandler())113 require.Nil(t, h.GetContextMenuHandler())
113 114
114- h.enableAudioHandler = true
115 h.enableContextMenuHandler = true115 h.enableContextMenuHandler = true
116 116
117- require.Same(t, h, h.GetAudioHandler())
118 require.Same(t, h, h.GetContextMenuHandler())117 require.Same(t, h, h.GetContextMenuHandler())
119 }118 }
M internal/infrastructure/config/engine.go +3 -2
@@ -118,12 +118,13 @@ func (c CEFEngineConfig) CEFManualPumpIntervalMs() int64 {
118118 return defaultCEFManualPumpIntervalMs
119119 }
120120
121-// CEFWindowlessFrameRate returns the effective OSR frame rate with default 60.
121+// CEFWindowlessFrameRate returns the configured OSR frame rate.
122+// Returns 0 (auto-detect from monitor) when the user hasn't set an explicit value.
122123 func (c CEFEngineConfig) CEFWindowlessFrameRate() int32 {
123124 if c.WindowlessFrameRate > 0 {
124125 return c.WindowlessFrameRate
125126 }
126- return defaultCEFWindowlessFrameRate
127+ return 0 // auto-detect from monitor refresh rate
127128 }
128129
129130 // PerformanceConfigFromEngine constructs a PerformanceConfig from the
@@ -118,12 +118,13 @@ func (c CEFEngineConfig) CEFManualPumpIntervalMs() int64 {
118 return defaultCEFManualPumpIntervalMs118 return defaultCEFManualPumpIntervalMs
119 }119 }
120 120
121-// CEFWindowlessFrameRate returns the effective OSR frame rate with default 60.121+// CEFWindowlessFrameRate returns the configured OSR frame rate.
122+// Returns 0 (auto-detect from monitor) when the user hasn't set an explicit value.
122 func (c CEFEngineConfig) CEFWindowlessFrameRate() int32 {123 func (c CEFEngineConfig) CEFWindowlessFrameRate() int32 {
123 if c.WindowlessFrameRate > 0 {124 if c.WindowlessFrameRate > 0 {
124 return c.WindowlessFrameRate125 return c.WindowlessFrameRate
125 }126 }
126- return defaultCEFWindowlessFrameRate127+ return 0 // auto-detect from monitor refresh rate
127 }128 }
128 129
129 // PerformanceConfigFromEngine constructs a PerformanceConfig from the130 // PerformanceConfigFromEngine constructs a PerformanceConfig from the

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