fix: reduce render pipeline log noise + add clipboard debug logging
- Change all per-frame render logs (OnPaint, handlePaint, uploadToPBO, onGLRender) from Debug to Trace level — they fire at 60-120fps - Add comprehensive debug logging to clipboard paste flow: init, interception, ReadTextAsync, callback, ImeCommitText - Add debug logging to clipboard copy flow (/api/clipboard-set) - Pass context to inputBridge instead of explicit logger
- commit
- 8166fb6cfde2e87c113706be9777dc8e275da8de
- author
- brice · · 2026-03-25 06:26 UTC
- committer
- brice · · 2026-03-28 04:39 UTC
- parent
- 6c5dd44
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
5 files · +56 -13
M internal/infrastructure/cef/factory.go +1 -1
| @@ -93,7 +93,7 @@ func (f *WebViewFactory) Create(ctx context.Context) (port.WebView, error) { | |||
| 93 | 93 | wv.handlers = handlers | |
| 94 | 94 | wv.findCtrl = newFindController() | |
| 95 | 95 | ||
| 96 | - | input := newInputBridge(f.scale) | |
| 96 | + | input := newInputBridge(ctx, f.scale) | |
| 97 | 97 | input.attachTo(pipeline.glArea) | |
| 98 | 98 | wv.input = input | |
| 99 | 99 | ||
| @@ -93,7 +93,7 @@ func (f *WebViewFactory) Create(ctx context.Context) (port.WebView, error) { | |||||
| 93 | wv.handlers = handlers | 93 | wv.handlers = handlers | ||
| 94 | wv.findCtrl = newFindController() | 94 | wv.findCtrl = newFindController() | ||
| 95 | 95 | ||||
| 96 | - | input := newInputBridge(f.scale) | 96 | + | input := newInputBridge(ctx, f.scale) |
| 97 | input.attachTo(pipeline.glArea) | 97 | input.attachTo(pipeline.glArea) | ||
| 98 | wv.input = input | 98 | wv.input = input | ||
| 99 | 99 | ||||
M internal/infrastructure/cef/input_bridge.go +38 -3
| @@ -1,12 +1,15 @@ | |||
| 1 | 1 | package cef | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | + | "context" | |
| 4 | 5 | "sync" | |
| 5 | 6 | ||
| 6 | 7 | purecef "github.com/bnema/purego-cef/cef" | |
| 7 | 8 | "github.com/bnema/puregotk/v4/gdk" | |
| 8 | 9 | "github.com/bnema/puregotk/v4/gio" | |
| 9 | 10 | "github.com/bnema/puregotk/v4/gtk" | |
| 11 | + | ||
| 12 | + | "github.com/bnema/dumber/internal/logging" | |
| 10 | 13 | ) | |
| 11 | 14 | ||
| 12 | 15 | // GDK dead key range boundaries. Dead keys (0xfe50–0xfe8c) are compose | |
| @@ -40,6 +43,7 @@ const ( | |||
| 40 | 43 | type inputBridge struct { | |
| 41 | 44 | host purecef.BrowserHost | |
| 42 | 45 | scale int32 | |
| 46 | + | ctx context.Context | |
| 43 | 47 | mu sync.Mutex | |
| 44 | 48 | ||
| 45 | 49 | // Last known pointer position, used for scroll events which don't | |
| @@ -63,8 +67,8 @@ type inputBridge struct { | |||
| 63 | 67 | } | |
| 64 | 68 | ||
| 65 | 69 | // newInputBridge creates an input bridge with the given HiDPI scale factor. | |
| 66 | - | func newInputBridge(scale int32) *inputBridge { | |
| 67 | - | return &inputBridge{scale: scale} | |
| 70 | + | func newInputBridge(ctx context.Context, scale int32) *inputBridge { | |
| 71 | + | return &inputBridge{ctx: ctx, scale: scale} | |
| 68 | 72 | } | |
| 69 | 73 | ||
| 70 | 74 | // setHost is called once the browser is created to enable event dispatch. | |
| @@ -80,6 +84,13 @@ func (ib *inputBridge) attachTo(glArea *gtk.GLArea) { | |||
| 80 | 84 | // Wayland clipboard, so we bridge it via GDK → ImeCommitText. | |
| 81 | 85 | if display := gdk.DisplayGetDefault(); display != nil { | |
| 82 | 86 | ib.clipboard = display.GetClipboard() | |
| 87 | + | if ib.clipboard != nil { | |
| 88 | + | logging.FromContext(ib.ctx).Debug().Msg("cef: GDK clipboard acquired for paste bridge") | |
| 89 | + | } else { | |
| 90 | + | logging.FromContext(ib.ctx).Warn().Msg("cef: GDK display found but GetClipboard returned nil") | |
| 91 | + | } | |
| 92 | + | } else { | |
| 93 | + | logging.FromContext(ib.ctx).Warn().Msg("cef: no GDK display — clipboard paste will not work") | |
| 83 | 94 | } | |
| 84 | 95 | ||
| 85 | 96 | // Motion (mouse move + enter/leave) | |
| @@ -180,6 +191,10 @@ func (ib *inputBridge) attachFocusAndKeyboard(glArea *gtk.GLArea) { | |||
| 180 | 191 | // ImeCommitText instead of letting CEF handle the paste. | |
| 181 | 192 | if mods&uint(gdk.ControlMaskValue) != 0 && | |
| 182 | 193 | (keyval == gdkKeyLowercaseV || keyval == gdkKeyUppercaseV) { | |
| 194 | + | logging.FromContext(ib.ctx).Debug(). | |
| 195 | + | Uint("keyval", keyval). | |
| 196 | + | Uint("mods", mods). | |
| 197 | + | Msg("cef: clipboard paste intercepted (Ctrl+V)") | |
| 183 | 198 | ib.pasteFromClipboard() | |
| 184 | 199 | return true | |
| 185 | 200 | } | |
| @@ -380,23 +395,39 @@ func (ib *inputBridge) onKeyRelease(keyval, keycode, mods uint) { | |||
| 380 | 395 | // injects it into the focused CEF input via ImeCommitText. This bridges the | |
| 381 | 396 | // gap where CEF OSR mode cannot read the Wayland/X11 system clipboard. | |
| 382 | 397 | func (ib *inputBridge) pasteFromClipboard() { | |
| 398 | + | log := logging.FromContext(ib.ctx) | |
| 399 | + | ||
| 383 | 400 | cb := ib.clipboard | |
| 384 | 401 | if cb == nil { | |
| 402 | + | log.Warn().Msg("cef: paste aborted — no GDK clipboard reference") | |
| 385 | 403 | return | |
| 386 | 404 | } | |
| 387 | 405 | ib.mu.Lock() | |
| 388 | 406 | host := ib.host | |
| 389 | 407 | ib.mu.Unlock() | |
| 390 | 408 | if host == nil { | |
| 409 | + | log.Warn().Msg("cef: paste aborted — no browser host") | |
| 391 | 410 | return | |
| 392 | 411 | } | |
| 393 | 412 | ||
| 413 | + | log.Debug().Msg("cef: calling ReadTextAsync on GDK clipboard") | |
| 414 | + | ||
| 394 | 415 | asyncCb := gio.AsyncReadyCallback(func(_, resultPtr, _ uintptr) { | |
| 416 | + | log.Debug().Uint64("result_ptr", uint64(resultPtr)).Msg("cef: ReadTextAsync callback fired") | |
| 417 | + | ||
| 395 | 418 | result := &gio.AsyncResultBase{Ptr: resultPtr} | |
| 396 | 419 | text, err := cb.ReadTextFinish(result) | |
| 397 | - | if err != nil || text == "" { | |
| 420 | + | if err != nil { | |
| 421 | + | log.Warn().Err(err).Msg("cef: ReadTextFinish failed") | |
| 398 | 422 | return | |
| 399 | 423 | } | |
| 424 | + | if text == "" { | |
| 425 | + | log.Debug().Msg("cef: clipboard text is empty, nothing to paste") | |
| 426 | + | return | |
| 427 | + | } | |
| 428 | + | ||
| 429 | + | log.Debug().Int("text_len", len(text)).Msg("cef: injecting clipboard text via ImeCommitText") | |
| 430 | + | ||
| 400 | 431 | // The GLib async callback runs on the main loop (UI thread), | |
| 401 | 432 | // which is the correct thread for CEF ImeCommitText. | |
| 402 | 433 | ib.mu.Lock() | |
| @@ -404,10 +435,14 @@ func (ib *inputBridge) pasteFromClipboard() { | |||
| 404 | 435 | ib.mu.Unlock() | |
| 405 | 436 | if h != nil { | |
| 406 | 437 | h.ImeCommitText(text, nil, 0) | |
| 438 | + | log.Debug().Msg("cef: ImeCommitText completed") | |
| 439 | + | } else { | |
| 440 | + | log.Warn().Msg("cef: paste — host became nil before ImeCommitText") | |
| 407 | 441 | } | |
| 408 | 442 | }) | |
| 409 | 443 | ||
| 410 | 444 | cb.ReadTextAsync(nil, &asyncCb, 0) | |
| 445 | + | log.Debug().Msg("cef: ReadTextAsync dispatched") | |
| 411 | 446 | } | |
| 412 | 447 | ||
| 413 | 448 | // --------------------------------------------------------------------------- | |
| @@ -1,12 +1,15 @@ | |||||
| 1 | package cef | 1 | package cef | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | + | "context" | |||
| 4 | "sync" | 5 | "sync" | ||
| 5 | 6 | ||||
| 6 | purecef "github.com/bnema/purego-cef/cef" | 7 | purecef "github.com/bnema/purego-cef/cef" | ||
| 7 | "github.com/bnema/puregotk/v4/gdk" | 8 | "github.com/bnema/puregotk/v4/gdk" | ||
| 8 | "github.com/bnema/puregotk/v4/gio" | 9 | "github.com/bnema/puregotk/v4/gio" | ||
| 9 | "github.com/bnema/puregotk/v4/gtk" | 10 | "github.com/bnema/puregotk/v4/gtk" | ||
| 11 | + | ||||
| 12 | + | "github.com/bnema/dumber/internal/logging" | |||
| 10 | ) | 13 | ) | ||
| 11 | 14 | ||||
| 12 | // GDK dead key range boundaries. Dead keys (0xfe50–0xfe8c) are compose | 15 | // GDK dead key range boundaries. Dead keys (0xfe50–0xfe8c) are compose | ||
| @@ -40,6 +43,7 @@ const ( | |||||
| 40 | type inputBridge struct { | 43 | type inputBridge struct { | ||
| 41 | host purecef.BrowserHost | 44 | host purecef.BrowserHost | ||
| 42 | scale int32 | 45 | scale int32 | ||
| 46 | + | ctx context.Context | |||
| 43 | mu sync.Mutex | 47 | mu sync.Mutex | ||
| 44 | 48 | ||||
| 45 | // Last known pointer position, used for scroll events which don't | 49 | // Last known pointer position, used for scroll events which don't | ||
| @@ -63,8 +67,8 @@ type inputBridge struct { | |||||
| 63 | } | 67 | } | ||
| 64 | 68 | ||||
| 65 | // newInputBridge creates an input bridge with the given HiDPI scale factor. | 69 | // newInputBridge creates an input bridge with the given HiDPI scale factor. | ||
| 66 | - | func newInputBridge(scale int32) *inputBridge { | 70 | + | func newInputBridge(ctx context.Context, scale int32) *inputBridge { |
| 67 | - | return &inputBridge{scale: scale} | 71 | + | return &inputBridge{ctx: ctx, scale: scale} |
| 68 | } | 72 | } | ||
| 69 | 73 | ||||
| 70 | // setHost is called once the browser is created to enable event dispatch. | 74 | // setHost is called once the browser is created to enable event dispatch. | ||
| @@ -80,6 +84,13 @@ func (ib *inputBridge) attachTo(glArea *gtk.GLArea) { | |||||
| 80 | // Wayland clipboard, so we bridge it via GDK → ImeCommitText. | 84 | // Wayland clipboard, so we bridge it via GDK → ImeCommitText. | ||
| 81 | if display := gdk.DisplayGetDefault(); display != nil { | 85 | if display := gdk.DisplayGetDefault(); display != nil { | ||
| 82 | ib.clipboard = display.GetClipboard() | 86 | ib.clipboard = display.GetClipboard() | ||
| 87 | + | if ib.clipboard != nil { | |||
| 88 | + | logging.FromContext(ib.ctx).Debug().Msg("cef: GDK clipboard acquired for paste bridge") | |||
| 89 | + | } else { | |||
| 90 | + | logging.FromContext(ib.ctx).Warn().Msg("cef: GDK display found but GetClipboard returned nil") | |||
| 91 | + | } | |||
| 92 | + | } else { | |||
| 93 | + | logging.FromContext(ib.ctx).Warn().Msg("cef: no GDK display — clipboard paste will not work") | |||
| 83 | } | 94 | } | ||
| 84 | 95 | ||||
| 85 | // Motion (mouse move + enter/leave) | 96 | // Motion (mouse move + enter/leave) | ||
| @@ -180,6 +191,10 @@ func (ib *inputBridge) attachFocusAndKeyboard(glArea *gtk.GLArea) { | |||||
| 180 | // ImeCommitText instead of letting CEF handle the paste. | 191 | // ImeCommitText instead of letting CEF handle the paste. | ||
| 181 | if mods&uint(gdk.ControlMaskValue) != 0 && | 192 | if mods&uint(gdk.ControlMaskValue) != 0 && | ||
| 182 | (keyval == gdkKeyLowercaseV || keyval == gdkKeyUppercaseV) { | 193 | (keyval == gdkKeyLowercaseV || keyval == gdkKeyUppercaseV) { | ||
| 194 | + | logging.FromContext(ib.ctx).Debug(). | |||
| 195 | + | Uint("keyval", keyval). | |||
| 196 | + | Uint("mods", mods). | |||
| 197 | + | Msg("cef: clipboard paste intercepted (Ctrl+V)") | |||
| 183 | ib.pasteFromClipboard() | 198 | ib.pasteFromClipboard() | ||
| 184 | return true | 199 | return true | ||
| 185 | } | 200 | } | ||
| @@ -380,23 +395,39 @@ func (ib *inputBridge) onKeyRelease(keyval, keycode, mods uint) { | |||||
| 380 | // injects it into the focused CEF input via ImeCommitText. This bridges the | 395 | // injects it into the focused CEF input via ImeCommitText. This bridges the | ||
| 381 | // gap where CEF OSR mode cannot read the Wayland/X11 system clipboard. | 396 | // gap where CEF OSR mode cannot read the Wayland/X11 system clipboard. | ||
| 382 | func (ib *inputBridge) pasteFromClipboard() { | 397 | func (ib *inputBridge) pasteFromClipboard() { | ||
| 398 | + | log := logging.FromContext(ib.ctx) | |||
| 399 | + | ||||
| 383 | cb := ib.clipboard | 400 | cb := ib.clipboard | ||
| 384 | if cb == nil { | 401 | if cb == nil { | ||
| 402 | + | log.Warn().Msg("cef: paste aborted — no GDK clipboard reference") | |||
| 385 | return | 403 | return | ||
| 386 | } | 404 | } | ||
| 387 | ib.mu.Lock() | 405 | ib.mu.Lock() | ||
| 388 | host := ib.host | 406 | host := ib.host | ||
| 389 | ib.mu.Unlock() | 407 | ib.mu.Unlock() | ||
| 390 | if host == nil { | 408 | if host == nil { | ||
| 409 | + | log.Warn().Msg("cef: paste aborted — no browser host") | |||
| 391 | return | 410 | return | ||
| 392 | } | 411 | } | ||
| 393 | 412 | ||||
| 413 | + | log.Debug().Msg("cef: calling ReadTextAsync on GDK clipboard") | |||
| 414 | + | ||||
| 394 | asyncCb := gio.AsyncReadyCallback(func(_, resultPtr, _ uintptr) { | 415 | asyncCb := gio.AsyncReadyCallback(func(_, resultPtr, _ uintptr) { | ||
| 416 | + | log.Debug().Uint64("result_ptr", uint64(resultPtr)).Msg("cef: ReadTextAsync callback fired") | |||
| 417 | + | ||||
| 395 | result := &gio.AsyncResultBase{Ptr: resultPtr} | 418 | result := &gio.AsyncResultBase{Ptr: resultPtr} | ||
| 396 | text, err := cb.ReadTextFinish(result) | 419 | text, err := cb.ReadTextFinish(result) | ||
| 397 | - | if err != nil || text == "" { | 420 | + | if err != nil { |
| 421 | + | log.Warn().Err(err).Msg("cef: ReadTextFinish failed") | |||
| 398 | return | 422 | return | ||
| 399 | } | 423 | } | ||
| 424 | + | if text == "" { | |||
| 425 | + | log.Debug().Msg("cef: clipboard text is empty, nothing to paste") | |||
| 426 | + | return | |||
| 427 | + | } | |||
| 428 | + | ||||
| 429 | + | log.Debug().Int("text_len", len(text)).Msg("cef: injecting clipboard text via ImeCommitText") | |||
| 430 | + | ||||
| 400 | // The GLib async callback runs on the main loop (UI thread), | 431 | // The GLib async callback runs on the main loop (UI thread), | ||
| 401 | // which is the correct thread for CEF ImeCommitText. | 432 | // which is the correct thread for CEF ImeCommitText. | ||
| 402 | ib.mu.Lock() | 433 | ib.mu.Lock() | ||
| @@ -404,10 +435,14 @@ func (ib *inputBridge) pasteFromClipboard() { | |||||
| 404 | ib.mu.Unlock() | 435 | ib.mu.Unlock() | ||
| 405 | if h != nil { | 436 | if h != nil { | ||
| 406 | h.ImeCommitText(text, nil, 0) | 437 | h.ImeCommitText(text, nil, 0) | ||
| 438 | + | log.Debug().Msg("cef: ImeCommitText completed") | |||
| 439 | + | } else { | |||
| 440 | + | log.Warn().Msg("cef: paste — host became nil before ImeCommitText") | |||
| 407 | } | 441 | } | ||
| 408 | }) | 442 | }) | ||
| 409 | 443 | ||||
| 410 | cb.ReadTextAsync(nil, &asyncCb, 0) | 444 | cb.ReadTextAsync(nil, &asyncCb, 0) | ||
| 445 | + | log.Debug().Msg("cef: ReadTextAsync dispatched") | |||
| 411 | } | 446 | } | ||
| 412 | 447 | ||||
| 413 | // --------------------------------------------------------------------------- | 448 | // --------------------------------------------------------------------------- | ||
M internal/infrastructure/cef/render_pipeline.go +6 -6
| @@ -179,7 +179,7 @@ func (rp *renderPipeline) handlePaint(buffer []byte, width, height int32, rects | |||
| 179 | 179 | startedAt := time.Now() | |
| 180 | 180 | rp.lastQueuedPaintSeq.Store(paintSeq) | |
| 181 | 181 | if rp.ctx != nil { | |
| 182 | - | logging.FromContext(rp.ctx).Debug(). | |
| 182 | + | logging.FromContext(rp.ctx).Trace(). | |
| 183 | 183 | Uint64("paint_seq", paintSeq). | |
| 184 | 184 | Int32("width", width). | |
| 185 | 185 | Int32("height", height). | |
| @@ -253,7 +253,7 @@ func (rp *renderPipeline) handlePaint(buffer []byte, width, height int32, rects | |||
| 253 | 253 | Int("expected_len", bufSize). | |
| 254 | 254 | Msg("cef: truncated full paint buffer on resize") | |
| 255 | 255 | } | |
| 256 | - | logging.FromContext(rp.ctx).Debug(). | |
| 256 | + | logging.FromContext(rp.ctx).Trace(). | |
| 257 | 257 | Uint64("paint_seq", paintSeq). | |
| 258 | 258 | Uint64("copied_bytes", copiedBytes). | |
| 259 | 259 | Bool("size_changed", sizeChanged). | |
| @@ -269,7 +269,7 @@ func (rp *renderPipeline) onGLRender() bool { | |||
| 269 | 269 | paintSeq := rp.lastQueuedPaintSeq.Load() | |
| 270 | 270 | renderStartedAt := time.Now() | |
| 271 | 271 | if rp.ctx != nil { | |
| 272 | - | logging.FromContext(rp.ctx).Debug(). | |
| 272 | + | logging.FromContext(rp.ctx).Trace(). | |
| 273 | 273 | Uint64("render_seq", renderSeq). | |
| 274 | 274 | Uint64("paint_seq", paintSeq). | |
| 275 | 275 | Msg("cef: onGLRender begin") | |
| @@ -341,7 +341,7 @@ func (rp *renderPipeline) onGLRender() bool { | |||
| 341 | 341 | } | |
| 342 | 342 | rp.maybeLogDiagnostics() | |
| 343 | 343 | if rp.ctx != nil { | |
| 344 | - | logging.FromContext(rp.ctx).Debug(). | |
| 344 | + | logging.FromContext(rp.ctx).Trace(). | |
| 345 | 345 | Uint64("render_seq", renderSeq). | |
| 346 | 346 | Uint64("paint_seq", paintSeq). | |
| 347 | 347 | Dur("render_duration", renderDuration). | |
| @@ -374,7 +374,7 @@ func (rp *renderPipeline) uploadToPBO(dirtyRects []rect, fullUpload bool, render | |||
| 374 | 374 | ||
| 375 | 375 | w, h, _ := rp.viewRectSize() | |
| 376 | 376 | if rp.ctx != nil { | |
| 377 | - | logging.FromContext(rp.ctx).Debug(). | |
| 377 | + | logging.FromContext(rp.ctx).Trace(). | |
| 378 | 378 | Uint64("render_seq", renderSeq). | |
| 379 | 379 | Uint64("paint_seq", paintSeq). | |
| 380 | 380 | Int32("width", w). | |
| @@ -463,7 +463,7 @@ func (rp *renderPipeline) uploadToPBO(dirtyRects []rect, fullUpload bool, render | |||
| 463 | 463 | ||
| 464 | 464 | gl.bindBuffer(glPixelUnpackBuffer, 0) | |
| 465 | 465 | if rp.ctx != nil { | |
| 466 | - | logging.FromContext(rp.ctx).Debug(). | |
| 466 | + | logging.FromContext(rp.ctx).Trace(). | |
| 467 | 467 | Uint64("render_seq", renderSeq). | |
| 468 | 468 | Uint64("paint_seq", paintSeq). | |
| 469 | 469 | Uint64("uploaded_bytes", uploadedBytes). | |
| @@ -179,7 +179,7 @@ func (rp *renderPipeline) handlePaint(buffer []byte, width, height int32, rects | |||||
| 179 | startedAt := time.Now() | 179 | startedAt := time.Now() | ||
| 180 | rp.lastQueuedPaintSeq.Store(paintSeq) | 180 | rp.lastQueuedPaintSeq.Store(paintSeq) | ||
| 181 | if rp.ctx != nil { | 181 | if rp.ctx != nil { | ||
| 182 | - | logging.FromContext(rp.ctx).Debug(). | 182 | + | logging.FromContext(rp.ctx).Trace(). |
| 183 | Uint64("paint_seq", paintSeq). | 183 | Uint64("paint_seq", paintSeq). | ||
| 184 | Int32("width", width). | 184 | Int32("width", width). | ||
| 185 | Int32("height", height). | 185 | Int32("height", height). | ||
| @@ -253,7 +253,7 @@ func (rp *renderPipeline) handlePaint(buffer []byte, width, height int32, rects | |||||
| 253 | Int("expected_len", bufSize). | 253 | Int("expected_len", bufSize). | ||
| 254 | Msg("cef: truncated full paint buffer on resize") | 254 | Msg("cef: truncated full paint buffer on resize") | ||
| 255 | } | 255 | } | ||
| 256 | - | logging.FromContext(rp.ctx).Debug(). | 256 | + | logging.FromContext(rp.ctx).Trace(). |
| 257 | Uint64("paint_seq", paintSeq). | 257 | Uint64("paint_seq", paintSeq). | ||
| 258 | Uint64("copied_bytes", copiedBytes). | 258 | Uint64("copied_bytes", copiedBytes). | ||
| 259 | Bool("size_changed", sizeChanged). | 259 | Bool("size_changed", sizeChanged). | ||
| @@ -269,7 +269,7 @@ func (rp *renderPipeline) onGLRender() bool { | |||||
| 269 | paintSeq := rp.lastQueuedPaintSeq.Load() | 269 | paintSeq := rp.lastQueuedPaintSeq.Load() | ||
| 270 | renderStartedAt := time.Now() | 270 | renderStartedAt := time.Now() | ||
| 271 | if rp.ctx != nil { | 271 | if rp.ctx != nil { | ||
| 272 | - | logging.FromContext(rp.ctx).Debug(). | 272 | + | logging.FromContext(rp.ctx).Trace(). |
| 273 | Uint64("render_seq", renderSeq). | 273 | Uint64("render_seq", renderSeq). | ||
| 274 | Uint64("paint_seq", paintSeq). | 274 | Uint64("paint_seq", paintSeq). | ||
| 275 | Msg("cef: onGLRender begin") | 275 | Msg("cef: onGLRender begin") | ||
| @@ -341,7 +341,7 @@ func (rp *renderPipeline) onGLRender() bool { | |||||
| 341 | } | 341 | } | ||
| 342 | rp.maybeLogDiagnostics() | 342 | rp.maybeLogDiagnostics() | ||
| 343 | if rp.ctx != nil { | 343 | if rp.ctx != nil { | ||
| 344 | - | logging.FromContext(rp.ctx).Debug(). | 344 | + | logging.FromContext(rp.ctx).Trace(). |
| 345 | Uint64("render_seq", renderSeq). | 345 | Uint64("render_seq", renderSeq). | ||
| 346 | Uint64("paint_seq", paintSeq). | 346 | Uint64("paint_seq", paintSeq). | ||
| 347 | Dur("render_duration", renderDuration). | 347 | Dur("render_duration", renderDuration). | ||
| @@ -374,7 +374,7 @@ func (rp *renderPipeline) uploadToPBO(dirtyRects []rect, fullUpload bool, render | |||||
| 374 | 374 | ||||
| 375 | w, h, _ := rp.viewRectSize() | 375 | w, h, _ := rp.viewRectSize() | ||
| 376 | if rp.ctx != nil { | 376 | if rp.ctx != nil { | ||
| 377 | - | logging.FromContext(rp.ctx).Debug(). | 377 | + | logging.FromContext(rp.ctx).Trace(). |
| 378 | Uint64("render_seq", renderSeq). | 378 | Uint64("render_seq", renderSeq). | ||
| 379 | Uint64("paint_seq", paintSeq). | 379 | Uint64("paint_seq", paintSeq). | ||
| 380 | Int32("width", w). | 380 | Int32("width", w). | ||
| @@ -463,7 +463,7 @@ func (rp *renderPipeline) uploadToPBO(dirtyRects []rect, fullUpload bool, render | |||||
| 463 | 463 | ||||
| 464 | gl.bindBuffer(glPixelUnpackBuffer, 0) | 464 | gl.bindBuffer(glPixelUnpackBuffer, 0) | ||
| 465 | if rp.ctx != nil { | 465 | if rp.ctx != nil { | ||
| 466 | - | logging.FromContext(rp.ctx).Debug(). | 466 | + | logging.FromContext(rp.ctx).Trace(). |
| 467 | Uint64("render_seq", renderSeq). | 467 | Uint64("render_seq", renderSeq). | ||
| 468 | Uint64("paint_seq", paintSeq). | 468 | Uint64("paint_seq", paintSeq). | ||
| 469 | Uint64("uploaded_bytes", uploadedBytes). | 469 | Uint64("uploaded_bytes", uploadedBytes). | ||
M internal/infrastructure/cef/scheme_handler.go +8 -0
| @@ -217,8 +217,11 @@ func (h *dumbSchemeHandler) handleConfigAPI(cfg *config.Config) purecef.Resource | |||
| 217 | 217 | // handleClipboardSet receives copied text from JS copy/cut events and writes | |
| 218 | 218 | // it to the system clipboard via the engine callback. | |
| 219 | 219 | func (h *dumbSchemeHandler) handleClipboardSet(request purecef.Request) purecef.ResourceHandler { | |
| 220 | + | h.logger.Debug().Msg("cef: /api/clipboard-set request received") | |
| 221 | + | ||
| 220 | 222 | body := readBodyFromHeader(request) | |
| 221 | 223 | if body == nil { | |
| 224 | + | h.logger.Debug().Msg("cef: clipboard-set — empty body (no X-Dumber-Body header)") | |
| 222 | 225 | return h.newJSONResourceHandler(http.StatusBadRequest, map[string]string{"error": "empty body"}) | |
| 223 | 226 | } | |
| 224 | 227 | ||
| @@ -226,11 +229,16 @@ func (h *dumbSchemeHandler) handleClipboardSet(request purecef.Request) purecef. | |||
| 226 | 229 | Text string `json:"text"` | |
| 227 | 230 | } | |
| 228 | 231 | if err := json.Unmarshal(body, &payload); err != nil || payload.Text == "" { | |
| 232 | + | h.logger.Debug().Int("body_len", len(body)).Msg("cef: clipboard-set — invalid or empty payload") | |
| 229 | 233 | return h.newJSONResourceHandler(http.StatusBadRequest, map[string]string{"error": "invalid payload"}) | |
| 230 | 234 | } | |
| 231 | 235 | ||
| 236 | + | h.logger.Debug().Int("text_len", len(payload.Text)).Msg("cef: clipboard-set — forwarding to GDK clipboard") | |
| 237 | + | ||
| 232 | 238 | if h.onClipboardSet != nil { | |
| 233 | 239 | h.onClipboardSet(payload.Text) | |
| 240 | + | } else { | |
| 241 | + | h.logger.Warn().Msg("cef: clipboard-set — onClipboardSet callback not wired") | |
| 234 | 242 | } | |
| 235 | 243 | ||
| 236 | 244 | return h.newJSONResourceHandler(http.StatusOK, map[string]any{"ok": true}) | |
| @@ -217,8 +217,11 @@ func (h *dumbSchemeHandler) handleConfigAPI(cfg *config.Config) purecef.Resource | |||||
| 217 | // handleClipboardSet receives copied text from JS copy/cut events and writes | 217 | // handleClipboardSet receives copied text from JS copy/cut events and writes | ||
| 218 | // it to the system clipboard via the engine callback. | 218 | // it to the system clipboard via the engine callback. | ||
| 219 | func (h *dumbSchemeHandler) handleClipboardSet(request purecef.Request) purecef.ResourceHandler { | 219 | func (h *dumbSchemeHandler) handleClipboardSet(request purecef.Request) purecef.ResourceHandler { | ||
| 220 | + | h.logger.Debug().Msg("cef: /api/clipboard-set request received") | |||
| 221 | + | ||||
| 220 | body := readBodyFromHeader(request) | 222 | body := readBodyFromHeader(request) | ||
| 221 | if body == nil { | 223 | if body == nil { | ||
| 224 | + | h.logger.Debug().Msg("cef: clipboard-set — empty body (no X-Dumber-Body header)") | |||
| 222 | return h.newJSONResourceHandler(http.StatusBadRequest, map[string]string{"error": "empty body"}) | 225 | return h.newJSONResourceHandler(http.StatusBadRequest, map[string]string{"error": "empty body"}) | ||
| 223 | } | 226 | } | ||
| 224 | 227 | ||||
| @@ -226,11 +229,16 @@ func (h *dumbSchemeHandler) handleClipboardSet(request purecef.Request) purecef. | |||||
| 226 | Text string `json:"text"` | 229 | Text string `json:"text"` | ||
| 227 | } | 230 | } | ||
| 228 | if err := json.Unmarshal(body, &payload); err != nil || payload.Text == "" { | 231 | if err := json.Unmarshal(body, &payload); err != nil || payload.Text == "" { | ||
| 232 | + | h.logger.Debug().Int("body_len", len(body)).Msg("cef: clipboard-set — invalid or empty payload") | |||
| 229 | return h.newJSONResourceHandler(http.StatusBadRequest, map[string]string{"error": "invalid payload"}) | 233 | return h.newJSONResourceHandler(http.StatusBadRequest, map[string]string{"error": "invalid payload"}) | ||
| 230 | } | 234 | } | ||
| 231 | 235 | ||||
| 236 | + | h.logger.Debug().Int("text_len", len(payload.Text)).Msg("cef: clipboard-set — forwarding to GDK clipboard") | |||
| 237 | + | ||||
| 232 | if h.onClipboardSet != nil { | 238 | if h.onClipboardSet != nil { | ||
| 233 | h.onClipboardSet(payload.Text) | 239 | h.onClipboardSet(payload.Text) | ||
| 240 | + | } else { | |||
| 241 | + | h.logger.Warn().Msg("cef: clipboard-set — onClipboardSet callback not wired") | |||
| 234 | } | 242 | } | ||
| 235 | 243 | ||||
| 236 | return h.newJSONResourceHandler(http.StatusOK, map[string]any{"ok": true}) | 244 | return h.newJSONResourceHandler(http.StatusOK, map[string]any{"ok": true}) | ||
M internal/infrastructure/cef/webview_handlers.go +3 -3
| @@ -193,7 +193,7 @@ func (h *handlerSet) OnPaint( | |||
| 193 | 193 | } | |
| 194 | 194 | paintSeq := h.wv.pipeline.nextPaintSeq() | |
| 195 | 195 | if h.wv != nil && h.wv.ctx != nil { | |
| 196 | - | logging.FromContext(h.wv.ctx).Debug(). | |
| 196 | + | logging.FromContext(h.wv.ctx).Trace(). | |
| 197 | 197 | Uint64("paint_seq", paintSeq). | |
| 198 | 198 | Int32("width", width). | |
| 199 | 199 | Int32("height", height). | |
| @@ -216,7 +216,7 @@ func (h *handlerSet) OnPaint( | |||
| 216 | 216 | h.wv.pipeline.handlePaint(pixels, width, height, rects, paintSeq) | |
| 217 | 217 | }) | |
| 218 | 218 | if h.wv != nil && h.wv.ctx != nil { | |
| 219 | - | logging.FromContext(h.wv.ctx).Debug(). | |
| 219 | + | logging.FromContext(h.wv.ctx).Trace(). | |
| 220 | 220 | Uint64("paint_seq", paintSeq). | |
| 221 | 221 | Msg("cef: OnPaint queued to GTK") | |
| 222 | 222 | } | |
| @@ -228,7 +228,7 @@ func (h *handlerSet) OnPaint( | |||
| 228 | 228 | } | |
| 229 | 229 | h.wv.pipeline.handlePaint(buffer, width, height, rects, paintSeq) | |
| 230 | 230 | if h.wv != nil && h.wv.ctx != nil { | |
| 231 | - | logging.FromContext(h.wv.ctx).Debug(). | |
| 231 | + | logging.FromContext(h.wv.ctx).Trace(). | |
| 232 | 232 | Uint64("paint_seq", paintSeq). | |
| 233 | 233 | Msg("cef: OnPaint handled inline") | |
| 234 | 234 | } | |
| @@ -193,7 +193,7 @@ func (h *handlerSet) OnPaint( | |||||
| 193 | } | 193 | } | ||
| 194 | paintSeq := h.wv.pipeline.nextPaintSeq() | 194 | paintSeq := h.wv.pipeline.nextPaintSeq() | ||
| 195 | if h.wv != nil && h.wv.ctx != nil { | 195 | if h.wv != nil && h.wv.ctx != nil { | ||
| 196 | - | logging.FromContext(h.wv.ctx).Debug(). | 196 | + | logging.FromContext(h.wv.ctx).Trace(). |
| 197 | Uint64("paint_seq", paintSeq). | 197 | Uint64("paint_seq", paintSeq). | ||
| 198 | Int32("width", width). | 198 | Int32("width", width). | ||
| 199 | Int32("height", height). | 199 | Int32("height", height). | ||
| @@ -216,7 +216,7 @@ func (h *handlerSet) OnPaint( | |||||
| 216 | h.wv.pipeline.handlePaint(pixels, width, height, rects, paintSeq) | 216 | h.wv.pipeline.handlePaint(pixels, width, height, rects, paintSeq) | ||
| 217 | }) | 217 | }) | ||
| 218 | if h.wv != nil && h.wv.ctx != nil { | 218 | if h.wv != nil && h.wv.ctx != nil { | ||
| 219 | - | logging.FromContext(h.wv.ctx).Debug(). | 219 | + | logging.FromContext(h.wv.ctx).Trace(). |
| 220 | Uint64("paint_seq", paintSeq). | 220 | Uint64("paint_seq", paintSeq). | ||
| 221 | Msg("cef: OnPaint queued to GTK") | 221 | Msg("cef: OnPaint queued to GTK") | ||
| 222 | } | 222 | } | ||
| @@ -228,7 +228,7 @@ func (h *handlerSet) OnPaint( | |||||
| 228 | } | 228 | } | ||
| 229 | h.wv.pipeline.handlePaint(buffer, width, height, rects, paintSeq) | 229 | h.wv.pipeline.handlePaint(buffer, width, height, rects, paintSeq) | ||
| 230 | if h.wv != nil && h.wv.ctx != nil { | 230 | if h.wv != nil && h.wv.ctx != nil { | ||
| 231 | - | logging.FromContext(h.wv.ctx).Debug(). | 231 | + | logging.FromContext(h.wv.ctx).Trace(). |
| 232 | Uint64("paint_seq", paintSeq). | 232 | Uint64("paint_seq", paintSeq). | ||
| 233 | Msg("cef: OnPaint handled inline") | 233 | Msg("cef: OnPaint handled inline") | ||
| 234 | } | 234 | } | ||