detached fed7cca

refactor(cef): eliminate unsafe from consumer code via purego-cef safe APIs

Adopt purego-cef's new safe handler signatures and constructors:
- OnPaint buffer: unsafe.Pointer → []byte
- OnBeforePopup/DevToolsPopup: unsafe.Pointer → *Client, *DictionaryValue, *bool
- OnAudioStreamPacket: unsafe.Pointer → [][]float32
- GetScreenPoint: unsafe.Pointer → *int32
- GetResourceRequestHandler: unsafe.Pointer → *int32
- OnSelectClientCertificate: unsafe.Pointer → []X509Certificate
- KeyEvent creation: unsafe.Sizeof + unsafe.Pointer cast → NewKeyEvent()
- ScreenInfo creation: unsafe.Sizeof → NewScreenInfo()
- handlePaint refactored to accept []byte directly

unsafe remains only in gl_loader.go and render_pipeline.go (OpenGL API).
commit
fed7ccae204b2e6fe41669354e460039e3825578
author
brice · · 2026-03-21 06:28 UTC
committer
brice · · 2026-03-28 04:37 UTC
parent
6604a53
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +36 -55
M internal/infrastructure/cef/input_bridge.go +7 -13
@@ -2,7 +2,6 @@ package cef
22
33 import (
44 "sync"
5- "unsafe"
65
76 purecef "github.com/bnema/purego-cef/cef"
87 "github.com/bnema/puregotk/v4/gdk"
@@ -326,10 +325,7 @@ func (ib *inputBridge) onIMCommit(text string) {
326325 continue
327326 }
328327 ch := uint16(r)
329- var evt purecef.KeyEvent
330- evt.Size = unsafe.Sizeof(evt)
331- *(*int32)(unsafe.Pointer(&evt.Type)) = int32(purecef.KeyEventTypeKeyeventChar)
332- evt.WindowsKeyCode = int32(ch)
328+ evt := purecef.NewKeyEvent(purecef.KeyEventTypeKeyeventChar, int32(ch), 0, 0)
333329 evt.Character = ch
334330 evt.UnmodifiedCharacter = ch
335331 host.SendKeyEvent(&evt)
@@ -361,14 +357,12 @@ func buildMouseEvent(x, y float64, gdkMods uint, scale int32) purecef.MouseEvent
361357 }
362358
363359 func buildKeyEvent(keyval, keycode, gdkMods uint, eventType purecef.KeyEventType) purecef.KeyEvent {
364- var evt purecef.KeyEvent
365- evt.Size = unsafe.Sizeof(evt)
366- // Use unsafe to set Type field — the underlying types are both int32 but
367- // the struct field uses an internal (unexportable) named type.
368- *(*int32)(unsafe.Pointer(&evt.Type)) = int32(eventType)
369- evt.Modifiers = translateModifiers(gdkMods)
370- evt.WindowsKeyCode = gdkKeyvalToWindowsVK(keyval)
371- evt.NativeKeyCode = int32(keycode)
360+ evt := purecef.NewKeyEvent(
361+ eventType,
362+ gdkKeyvalToWindowsVK(keyval),
363+ int32(keycode),
364+ translateModifiers(gdkMods),
365+ )
372366 return evt
373367 }
374368
@@ -2,7 +2,6 @@ package cef
2 2
3 import (3 import (
4 "sync"4 "sync"
5- "unsafe"
6 5
7 purecef "github.com/bnema/purego-cef/cef"6 purecef "github.com/bnema/purego-cef/cef"
8 "github.com/bnema/puregotk/v4/gdk"7 "github.com/bnema/puregotk/v4/gdk"
@@ -326,10 +325,7 @@ func (ib *inputBridge) onIMCommit(text string) {
326 continue325 continue
327 }326 }
328 ch := uint16(r)327 ch := uint16(r)
329- var evt purecef.KeyEvent328+ evt := purecef.NewKeyEvent(purecef.KeyEventTypeKeyeventChar, int32(ch), 0, 0)
330- evt.Size = unsafe.Sizeof(evt)
331- *(*int32)(unsafe.Pointer(&evt.Type)) = int32(purecef.KeyEventTypeKeyeventChar)
332- evt.WindowsKeyCode = int32(ch)
333 evt.Character = ch329 evt.Character = ch
334 evt.UnmodifiedCharacter = ch330 evt.UnmodifiedCharacter = ch
335 host.SendKeyEvent(&evt)331 host.SendKeyEvent(&evt)
@@ -361,14 +357,12 @@ func buildMouseEvent(x, y float64, gdkMods uint, scale int32) purecef.MouseEvent
361 }357 }
362 358
363 func buildKeyEvent(keyval, keycode, gdkMods uint, eventType purecef.KeyEventType) purecef.KeyEvent {359 func buildKeyEvent(keyval, keycode, gdkMods uint, eventType purecef.KeyEventType) purecef.KeyEvent {
364- var evt purecef.KeyEvent360+ evt := purecef.NewKeyEvent(
365- evt.Size = unsafe.Sizeof(evt)361+ eventType,
366- // Use unsafe to set Type field — the underlying types are both int32 but362+ gdkKeyvalToWindowsVK(keyval),
367- // the struct field uses an internal (unexportable) named type.363+ int32(keycode),
368- *(*int32)(unsafe.Pointer(&evt.Type)) = int32(eventType)364+ translateModifiers(gdkMods),
369- evt.Modifiers = translateModifiers(gdkMods)365+ )
370- evt.WindowsKeyCode = gdkKeyvalToWindowsVK(keyval)
371- evt.NativeKeyCode = int32(keycode)
372 return evt366 return evt
373 }367 }
374 368
M internal/infrastructure/cef/render_pipeline.go +4 -5
@@ -151,8 +151,8 @@ func newRenderPipeline(ctx context.Context, gl *glLoader, scale int32) *renderPi
151151 // handlePaint is called from CEF's OnPaint callback (on GTK main thread via
152152 // IdleAdd). It copies dirty rect regions from the CEF buffer into the staging
153153 // buffer and queues a GL redraw.
154-func (rp *renderPipeline) handlePaint(buffer unsafe.Pointer, width, height int32, rects []rect) {
155- if buffer == nil || width <= 0 || height <= 0 {
154+func (rp *renderPipeline) handlePaint(buffer []byte, width, height int32, rects []rect) {
155+ if len(buffer) == 0 || width <= 0 || height <= 0 {
156156 return
157157 }
158158
@@ -160,7 +160,6 @@ func (rp *renderPipeline) handlePaint(buffer unsafe.Pointer, width, height int32
160160 rp.mu.Lock()
161161
162162 bufSize := int(int64(width) * int64(height) * 4)
163- srcSlice := unsafe.Slice((*byte)(buffer), bufSize)
164163 copiedBytes := uint64(0)
165164
166165 // Detect size change, or first paint (staging not yet allocated).
@@ -170,7 +169,7 @@ func (rp *renderPipeline) handlePaint(buffer unsafe.Pointer, width, height int32
170169 rp.staging = make([]byte, bufSize)
171170 rp.sizeChanged = true
172171 // On size change, copy the entire buffer.
173- copy(rp.staging, srcSlice)
172+ copy(rp.staging, buffer)
174173 copiedBytes = uint64(bufSize)
175174 } else {
176175 // Copy only dirty rect rows.
@@ -180,7 +179,7 @@ func (rp *renderPipeline) handlePaint(buffer unsafe.Pointer, width, height int32
180179 srcOff := int(row)*stride + int(r.X)*4
181180 dstOff := srcOff
182181 rowBytes := int(r.Width) * 4
183- copy(rp.staging[dstOff:dstOff+rowBytes], srcSlice[srcOff:srcOff+rowBytes])
182+ copy(rp.staging[dstOff:dstOff+rowBytes], buffer[srcOff:srcOff+rowBytes])
184183 copiedBytes += uint64(rowBytes)
185184 }
186185 }
@@ -151,8 +151,8 @@ func newRenderPipeline(ctx context.Context, gl *glLoader, scale int32) *renderPi
151 // handlePaint is called from CEF's OnPaint callback (on GTK main thread via151 // handlePaint is called from CEF's OnPaint callback (on GTK main thread via
152 // IdleAdd). It copies dirty rect regions from the CEF buffer into the staging152 // IdleAdd). It copies dirty rect regions from the CEF buffer into the staging
153 // buffer and queues a GL redraw.153 // buffer and queues a GL redraw.
154-func (rp *renderPipeline) handlePaint(buffer unsafe.Pointer, width, height int32, rects []rect) {154+func (rp *renderPipeline) handlePaint(buffer []byte, width, height int32, rects []rect) {
155- if buffer == nil || width <= 0 || height <= 0 {155+ if len(buffer) == 0 || width <= 0 || height <= 0 {
156 return156 return
157 }157 }
158 158
@@ -160,7 +160,6 @@ func (rp *renderPipeline) handlePaint(buffer unsafe.Pointer, width, height int32
160 rp.mu.Lock()160 rp.mu.Lock()
161 161
162 bufSize := int(int64(width) * int64(height) * 4)162 bufSize := int(int64(width) * int64(height) * 4)
163- srcSlice := unsafe.Slice((*byte)(buffer), bufSize)
164 copiedBytes := uint64(0)163 copiedBytes := uint64(0)
165 164
166 // Detect size change, or first paint (staging not yet allocated).165 // Detect size change, or first paint (staging not yet allocated).
@@ -170,7 +169,7 @@ func (rp *renderPipeline) handlePaint(buffer unsafe.Pointer, width, height int32
170 rp.staging = make([]byte, bufSize)169 rp.staging = make([]byte, bufSize)
171 rp.sizeChanged = true170 rp.sizeChanged = true
172 // On size change, copy the entire buffer.171 // On size change, copy the entire buffer.
173- copy(rp.staging, srcSlice)172+ copy(rp.staging, buffer)
174 copiedBytes = uint64(bufSize)173 copiedBytes = uint64(bufSize)
175 } else {174 } else {
176 // Copy only dirty rect rows.175 // Copy only dirty rect rows.
@@ -180,7 +179,7 @@ func (rp *renderPipeline) handlePaint(buffer unsafe.Pointer, width, height int32
180 srcOff := int(row)*stride + int(r.X)*4179 srcOff := int(row)*stride + int(r.X)*4
181 dstOff := srcOff180 dstOff := srcOff
182 rowBytes := int(r.Width) * 4181 rowBytes := int(r.Width) * 4
183- copy(rp.staging[dstOff:dstOff+rowBytes], srcSlice[srcOff:srcOff+rowBytes])182+ copy(rp.staging[dstOff:dstOff+rowBytes], buffer[srcOff:srcOff+rowBytes])
184 copiedBytes += uint64(rowBytes)183 copiedBytes += uint64(rowBytes)
185 }184 }
186 }185 }
M internal/infrastructure/cef/webview.go +0 -4
@@ -8,7 +8,6 @@ import (
88 "math"
99 "sync"
1010 "sync/atomic"
11- "unsafe"
1211
1312 purecef "github.com/bnema/purego-cef/cef"
1413 "github.com/bnema/puregotk/v4/glib"
@@ -502,6 +501,3 @@ func (wv *WebView) runOnGTK(fn func()) {
502501 })
503502 glib.IdleAddOnce(&cb, 0)
504503 }
505-
506-// suppress unused import for unsafe (used by NativeWidget via GoPointer).
507-var _ = unsafe.Pointer(nil)
@@ -8,7 +8,6 @@ import (
8 "math"8 "math"
9 "sync"9 "sync"
10 "sync/atomic"10 "sync/atomic"
11- "unsafe"
12 11
13 purecef "github.com/bnema/purego-cef/cef"12 purecef "github.com/bnema/purego-cef/cef"
14 "github.com/bnema/puregotk/v4/glib"13 "github.com/bnema/puregotk/v4/glib"
@@ -502,6 +501,3 @@ func (wv *WebView) runOnGTK(fn func()) {
502 })501 })
503 glib.IdleAddOnce(&cb, 0)502 glib.IdleAddOnce(&cb, 0)
504 }503 }
505-
506-// suppress unused import for unsafe (used by NativeWidget via GoPointer).
507-var _ = unsafe.Pointer(nil)
M internal/infrastructure/cef/webview_handlers.go +25 -33
@@ -1,8 +1,6 @@
11 package cef
22
33 import (
4- "unsafe"
5-
64 purecef "github.com/bnema/purego-cef/cef"
75
86 "github.com/bnema/dumber/internal/application/port"
@@ -63,8 +61,6 @@ func (h *handlerSet) GetAccessibilityHandler() purecef.AccessibilityHandler { re
6361 func (h *handlerSet) GetRootScreenRect(_ purecef.Browser, _ *purecef.Rect) int32 { return 0 }
6462
6563 // GetViewRect fills the rect struct with the pipeline dimensions.
66-// The rect pointer points to a cef_rect_t: {HostLayout padding, X, Y, Width, Height} all int32.
67-// The HostLayout field occupies 0 bytes on most platforms but we use the Rect type alias to be safe.
6864 func (h *handlerSet) GetViewRect(_ purecef.Browser, rect *purecef.Rect) {
6965 if rect == nil {
7066 return
@@ -89,7 +85,7 @@ func (h *handlerSet) GetViewRect(_ purecef.Browser, rect *purecef.Rect) {
8985 rect.Height = ht
9086 }
9187
92-func (h *handlerSet) GetScreenPoint(_ purecef.Browser, _, _ int32, _, _ unsafe.Pointer) int32 {
88+func (h *handlerSet) GetScreenPoint(_ purecef.Browser, _, _ int32, _, _ *int32) int32 {
9389 return 0
9490 }
9591
@@ -103,15 +99,12 @@ func (h *handlerSet) GetScreenInfo(_ purecef.Browser, info *purecef.ScreenInfo)
10399 s := h.wv.pipeline.scale
104100 h.wv.pipeline.mu.Unlock()
105101
106- info.Size = unsafe.Sizeof(*info)
107- info.DeviceScaleFactor = float32(s)
108- info.Depth = 24
109- info.DepthPerComponent = 8
110- info.Rect.X = 0
111- info.Rect.Y = 0
112- info.Rect.Width = w
113- info.Rect.Height = ht
114- info.AvailableRect = info.Rect
102+ const (
103+ screenDepth = 24
104+ screenDepthPerComponent = 8
105+ )
106+ r := purecef.Rect{X: 0, Y: 0, Width: w, Height: ht}
107+ *info = purecef.NewScreenInfo(float32(s), screenDepth, screenDepthPerComponent, false, r, r)
115108 return 1
116109 }
117110
@@ -124,9 +117,9 @@ func (h *handlerSet) OnPopupSize(_ purecef.Browser, _ *purecef.Rect) {}
124117 // must first copy the transient CEF buffer before hopping back to GTK.
125118 func (h *handlerSet) OnPaint(
126119 _ purecef.Browser, _ purecef.PaintElementType,
127- dirtyRects []purecef.Rect, buffer unsafe.Pointer, width, height int32,
120+ dirtyRects []purecef.Rect, buffer []byte, width, height int32,
128121 ) {
129- if buffer == nil || width <= 0 || height <= 0 {
122+ if len(buffer) == 0 || width <= 0 || height <= 0 {
130123 return
131124 }
132125 rects := make([]rect, len(dirtyRects))
@@ -134,14 +127,10 @@ func (h *handlerSet) OnPaint(
134127 rects[i] = rect{X: dr.X, Y: dr.Y, Width: dr.Width, Height: dr.Height}
135128 }
136129 if h.wv.engine != nil && h.wv.engine.multiThreadedMessageLoop {
137- bufSize := int(width) * int(height) * 4
138- if bufSize <= 0 {
139- return
140- }
141- pixels := make([]byte, bufSize)
142- copy(pixels, unsafe.Slice((*byte)(buffer), bufSize))
130+ pixels := make([]byte, len(buffer))
131+ copy(pixels, buffer)
143132 h.wv.runOnGTK(func() {
144- h.wv.pipeline.handlePaint(unsafe.Pointer(&pixels[0]), width, height, rects)
133+ h.wv.pipeline.handlePaint(pixels, width, height, rects)
145134 })
146135 return
147136 }
@@ -334,7 +323,7 @@ func (h *handlerSet) OnLoadEnd(_ purecef.Browser, frame purecef.Frame, httpStatu
334323 }
335324 }
336325
337-// OnLoadError is a no-op in Phase 1.
326+// OnLoadError is a no-op.
338327 func (h *handlerSet) OnLoadError(_ purecef.Browser, _ purecef.Frame, _ purecef.Errorcode, _, _ string) {
339328 }
340329
@@ -342,21 +331,24 @@ func (h *handlerSet) OnLoadError(_ purecef.Browser, _ purecef.Frame, _ purecef.E
342331 // LifeSpanHandler (6 methods)
343332 // ===========================================================================
344333
345-// OnBeforePopup blocks all popups in Phase 1.
334+// OnBeforePopup blocks all popups.
335+//
336+//nolint:gocritic // signature imposed by purecef.LifeSpanHandler interface
346337 func (h *handlerSet) OnBeforePopup(
347338 _ purecef.Browser, _ purecef.Frame, _ int32, _, _ string,
348339 _ purecef.WindowOpenDisposition, _ int32, _ *purecef.PopupFeatures,
349- _ *purecef.WindowInfo, _ unsafe.Pointer, _ *purecef.BrowserSettings,
350- _, _ unsafe.Pointer,
340+ _ *purecef.WindowInfo, _ *purecef.Client, _ *purecef.BrowserSettings,
341+ _ *purecef.DictionaryValue, _ *bool,
351342 ) bool {
352343 return true // block
353344 }
354345
355346 func (h *handlerSet) OnBeforePopupAborted(_ purecef.Browser, _ int32) {}
356347
348+//nolint:gocritic // signature imposed by purecef.LifeSpanHandler interface
357349 func (h *handlerSet) OnBeforeDevToolsPopup(
358- _ purecef.Browser, _ *purecef.WindowInfo, _ unsafe.Pointer,
359- _ *purecef.BrowserSettings, _, _ unsafe.Pointer,
350+ _ purecef.Browser, _ *purecef.WindowInfo, _ *purecef.Client,
351+ _ *purecef.BrowserSettings, _ *purecef.DictionaryValue, _ *bool,
360352 ) {
361353 }
362354
@@ -421,7 +413,7 @@ func (h *handlerSet) OnOpenUrlfromTab(_ purecef.Browser, _ purecef.Frame, _ stri
421413
422414 func (h *handlerSet) GetResourceRequestHandler(
423415 _ purecef.Browser, _ purecef.Frame, _ purecef.Request,
424- _, _ int32, _ string, _ unsafe.Pointer,
416+ _, _ int32, _ string, _ *int32,
425417 ) purecef.ResourceRequestHandler {
426418 return nil
427419 }
@@ -438,8 +430,8 @@ func (h *handlerSet) OnCertificateError(_ purecef.Browser, _ purecef.Errorcode,
438430 }
439431
440432 func (h *handlerSet) OnSelectClientCertificate(
441- _ purecef.Browser, _ int32, _ string, _ int32, _ int,
442- _ unsafe.Pointer, _ purecef.SelectClientCertificateCallback,
433+ _ purecef.Browser, _ int32, _ string, _ int32,
434+ _ []purecef.X509Certificate, _ purecef.SelectClientCertificateCallback,
443435 ) int32 {
444436 return 0
445437 }
@@ -509,7 +501,7 @@ func (h *handlerSet) OnAudioStreamStarted(_ purecef.Browser, _ *purecef.AudioPar
509501 h.wv.setAudioPlaying(true)
510502 }
511503
512-func (h *handlerSet) OnAudioStreamPacket(_ purecef.Browser, _ unsafe.Pointer, _ int32, _ int64) {}
504+func (h *handlerSet) OnAudioStreamPacket(_ purecef.Browser, _ [][]float32, _ int32, _ int64) {}
513505
514506 func (h *handlerSet) OnAudioStreamStopped(_ purecef.Browser) {
515507 h.wv.setAudioPlaying(false)
@@ -1,8 +1,6 @@
1 package cef1 package cef
2 2
3 import (3 import (
4- "unsafe"
5-
6 purecef "github.com/bnema/purego-cef/cef"4 purecef "github.com/bnema/purego-cef/cef"
7 5
8 "github.com/bnema/dumber/internal/application/port"6 "github.com/bnema/dumber/internal/application/port"
@@ -63,8 +61,6 @@ func (h *handlerSet) GetAccessibilityHandler() purecef.AccessibilityHandler { re
63 func (h *handlerSet) GetRootScreenRect(_ purecef.Browser, _ *purecef.Rect) int32 { return 0 }61 func (h *handlerSet) GetRootScreenRect(_ purecef.Browser, _ *purecef.Rect) int32 { return 0 }
64 62
65 // GetViewRect fills the rect struct with the pipeline dimensions.63 // GetViewRect fills the rect struct with the pipeline dimensions.
66-// The rect pointer points to a cef_rect_t: {HostLayout padding, X, Y, Width, Height} all int32.
67-// The HostLayout field occupies 0 bytes on most platforms but we use the Rect type alias to be safe.
68 func (h *handlerSet) GetViewRect(_ purecef.Browser, rect *purecef.Rect) {64 func (h *handlerSet) GetViewRect(_ purecef.Browser, rect *purecef.Rect) {
69 if rect == nil {65 if rect == nil {
70 return66 return
@@ -89,7 +85,7 @@ func (h *handlerSet) GetViewRect(_ purecef.Browser, rect *purecef.Rect) {
89 rect.Height = ht85 rect.Height = ht
90 }86 }
91 87
92-func (h *handlerSet) GetScreenPoint(_ purecef.Browser, _, _ int32, _, _ unsafe.Pointer) int32 {88+func (h *handlerSet) GetScreenPoint(_ purecef.Browser, _, _ int32, _, _ *int32) int32 {
93 return 089 return 0
94 }90 }
95 91
@@ -103,15 +99,12 @@ func (h *handlerSet) GetScreenInfo(_ purecef.Browser, info *purecef.ScreenInfo)
103 s := h.wv.pipeline.scale99 s := h.wv.pipeline.scale
104 h.wv.pipeline.mu.Unlock()100 h.wv.pipeline.mu.Unlock()
105 101
106- info.Size = unsafe.Sizeof(*info)102+ const (
107- info.DeviceScaleFactor = float32(s)103+ screenDepth = 24
108- info.Depth = 24104+ screenDepthPerComponent = 8
109- info.DepthPerComponent = 8105+ )
110- info.Rect.X = 0106+ r := purecef.Rect{X: 0, Y: 0, Width: w, Height: ht}
111- info.Rect.Y = 0107+ *info = purecef.NewScreenInfo(float32(s), screenDepth, screenDepthPerComponent, false, r, r)
112- info.Rect.Width = w
113- info.Rect.Height = ht
114- info.AvailableRect = info.Rect
115 return 1108 return 1
116 }109 }
117 110
@@ -124,9 +117,9 @@ func (h *handlerSet) OnPopupSize(_ purecef.Browser, _ *purecef.Rect) {}
124 // must first copy the transient CEF buffer before hopping back to GTK.117 // must first copy the transient CEF buffer before hopping back to GTK.
125 func (h *handlerSet) OnPaint(118 func (h *handlerSet) OnPaint(
126 _ purecef.Browser, _ purecef.PaintElementType,119 _ purecef.Browser, _ purecef.PaintElementType,
127- dirtyRects []purecef.Rect, buffer unsafe.Pointer, width, height int32,120+ dirtyRects []purecef.Rect, buffer []byte, width, height int32,
128 ) {121 ) {
129- if buffer == nil || width <= 0 || height <= 0 {122+ if len(buffer) == 0 || width <= 0 || height <= 0 {
130 return123 return
131 }124 }
132 rects := make([]rect, len(dirtyRects))125 rects := make([]rect, len(dirtyRects))
@@ -134,14 +127,10 @@ func (h *handlerSet) OnPaint(
134 rects[i] = rect{X: dr.X, Y: dr.Y, Width: dr.Width, Height: dr.Height}127 rects[i] = rect{X: dr.X, Y: dr.Y, Width: dr.Width, Height: dr.Height}
135 }128 }
136 if h.wv.engine != nil && h.wv.engine.multiThreadedMessageLoop {129 if h.wv.engine != nil && h.wv.engine.multiThreadedMessageLoop {
137- bufSize := int(width) * int(height) * 4130+ pixels := make([]byte, len(buffer))
138- if bufSize <= 0 {131+ copy(pixels, buffer)
139- return
140- }
141- pixels := make([]byte, bufSize)
142- copy(pixels, unsafe.Slice((*byte)(buffer), bufSize))
143 h.wv.runOnGTK(func() {132 h.wv.runOnGTK(func() {
144- h.wv.pipeline.handlePaint(unsafe.Pointer(&pixels[0]), width, height, rects)133+ h.wv.pipeline.handlePaint(pixels, width, height, rects)
145 })134 })
146 return135 return
147 }136 }
@@ -334,7 +323,7 @@ func (h *handlerSet) OnLoadEnd(_ purecef.Browser, frame purecef.Frame, httpStatu
334 }323 }
335 }324 }
336 325
337-// OnLoadError is a no-op in Phase 1.326+// OnLoadError is a no-op.
338 func (h *handlerSet) OnLoadError(_ purecef.Browser, _ purecef.Frame, _ purecef.Errorcode, _, _ string) {327 func (h *handlerSet) OnLoadError(_ purecef.Browser, _ purecef.Frame, _ purecef.Errorcode, _, _ string) {
339 }328 }
340 329
@@ -342,21 +331,24 @@ func (h *handlerSet) OnLoadError(_ purecef.Browser, _ purecef.Frame, _ purecef.E
342 // LifeSpanHandler (6 methods)331 // LifeSpanHandler (6 methods)
343 // ===========================================================================332 // ===========================================================================
344 333
345-// OnBeforePopup blocks all popups in Phase 1.334+// OnBeforePopup blocks all popups.
335+//
336+//nolint:gocritic // signature imposed by purecef.LifeSpanHandler interface
346 func (h *handlerSet) OnBeforePopup(337 func (h *handlerSet) OnBeforePopup(
347 _ purecef.Browser, _ purecef.Frame, _ int32, _, _ string,338 _ purecef.Browser, _ purecef.Frame, _ int32, _, _ string,
348 _ purecef.WindowOpenDisposition, _ int32, _ *purecef.PopupFeatures,339 _ purecef.WindowOpenDisposition, _ int32, _ *purecef.PopupFeatures,
349- _ *purecef.WindowInfo, _ unsafe.Pointer, _ *purecef.BrowserSettings,340+ _ *purecef.WindowInfo, _ *purecef.Client, _ *purecef.BrowserSettings,
350- _, _ unsafe.Pointer,341+ _ *purecef.DictionaryValue, _ *bool,
351 ) bool {342 ) bool {
352 return true // block343 return true // block
353 }344 }
354 345
355 func (h *handlerSet) OnBeforePopupAborted(_ purecef.Browser, _ int32) {}346 func (h *handlerSet) OnBeforePopupAborted(_ purecef.Browser, _ int32) {}
356 347
348+//nolint:gocritic // signature imposed by purecef.LifeSpanHandler interface
357 func (h *handlerSet) OnBeforeDevToolsPopup(349 func (h *handlerSet) OnBeforeDevToolsPopup(
358- _ purecef.Browser, _ *purecef.WindowInfo, _ unsafe.Pointer,350+ _ purecef.Browser, _ *purecef.WindowInfo, _ *purecef.Client,
359- _ *purecef.BrowserSettings, _, _ unsafe.Pointer,351+ _ *purecef.BrowserSettings, _ *purecef.DictionaryValue, _ *bool,
360 ) {352 ) {
361 }353 }
362 354
@@ -421,7 +413,7 @@ func (h *handlerSet) OnOpenUrlfromTab(_ purecef.Browser, _ purecef.Frame, _ stri
421 413
422 func (h *handlerSet) GetResourceRequestHandler(414 func (h *handlerSet) GetResourceRequestHandler(
423 _ purecef.Browser, _ purecef.Frame, _ purecef.Request,415 _ purecef.Browser, _ purecef.Frame, _ purecef.Request,
424- _, _ int32, _ string, _ unsafe.Pointer,416+ _, _ int32, _ string, _ *int32,
425 ) purecef.ResourceRequestHandler {417 ) purecef.ResourceRequestHandler {
426 return nil418 return nil
427 }419 }
@@ -438,8 +430,8 @@ func (h *handlerSet) OnCertificateError(_ purecef.Browser, _ purecef.Errorcode,
438 }430 }
439 431
440 func (h *handlerSet) OnSelectClientCertificate(432 func (h *handlerSet) OnSelectClientCertificate(
441- _ purecef.Browser, _ int32, _ string, _ int32, _ int,433+ _ purecef.Browser, _ int32, _ string, _ int32,
442- _ unsafe.Pointer, _ purecef.SelectClientCertificateCallback,434+ _ []purecef.X509Certificate, _ purecef.SelectClientCertificateCallback,
443 ) int32 {435 ) int32 {
444 return 0436 return 0
445 }437 }
@@ -509,7 +501,7 @@ func (h *handlerSet) OnAudioStreamStarted(_ purecef.Browser, _ *purecef.AudioPar
509 h.wv.setAudioPlaying(true)501 h.wv.setAudioPlaying(true)
510 }502 }
511 503
512-func (h *handlerSet) OnAudioStreamPacket(_ purecef.Browser, _ unsafe.Pointer, _ int32, _ int64) {}504+func (h *handlerSet) OnAudioStreamPacket(_ purecef.Browser, _ [][]float32, _ int32, _ int64) {}
513 505
514 func (h *handlerSet) OnAudioStreamStopped(_ purecef.Browser) {506 func (h *handlerSet) OnAudioStreamStopped(_ purecef.Browser) {
515 h.wv.setAudioPlaying(false)507 h.wv.setAudioPlaying(false)

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