chore(cef): satisfy lint for scale probe
- commit
- c2c7597304f830516c3c361eccba482e20fd9363
- author
- brice · · 2026-05-27 18:40 UTC
- committer
- brice · · 2026-05-27 18:47 UTC
- parent
- 206243c
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
8 files · +90 -45
M internal/application/port/engine.go +13 -13
| @@ -142,22 +142,22 @@ type Engine interface { | |||
| 142 | 142 | SetHandlerContext(ctx context.Context) | |
| 143 | 143 | } | |
| 144 | 144 | ||
| 145 | + | // EngineSettingsPayload is the engine-facing boundary view of runtime config. | |
| 146 | + | // Add fields here only when an engine needs to react to them at runtime. | |
| 147 | + | type EngineSettingsPayload struct { | |
| 148 | + | DefaultUIScale float64 | |
| 149 | + | } | |
| 150 | + | ||
| 145 | 151 | // EngineSettingsUpdate carries a runtime config change to the engine. | |
| 146 | 152 | // | |
| 147 | - | // The Raw field holds the implementation-specific config object; each engine | |
| 148 | - | // implementation type-asserts it to its concrete config type (e.g. the WebKit | |
| 149 | - | // engine asserts to *infrastructure/config.Config). | |
| 150 | - | // | |
| 151 | - | // This is the only intentional use of `any` in the Engine port. A fully typed | |
| 152 | - | // approach would require either moving the entire config schema into the domain | |
| 153 | - | // layer (a large refactor) or defining a SettingsProvider interface — which | |
| 154 | - | // would itself reduce to `any` because callers would embed engine-specific data | |
| 155 | - | // behind an empty interface. Until the config schema is promoted to domain, | |
| 156 | - | // the type assertion in the engine implementation is the least-invasive option. | |
| 153 | + | // Settings exposes the typed boundary payload that engine adapters should prefer. | |
| 154 | + | // Raw remains for legacy adapters that still consume implementation-specific | |
| 155 | + | // config until their settings managers are narrowed to boundary fields. | |
| 157 | 156 | type EngineSettingsUpdate struct { | |
| 158 | - | // Raw holds the implementation-specific config. | |
| 159 | - | // WebKit engine expects *infrastructure/config.Config. | |
| 160 | - | Raw any //nolint:iface // intentional: see type comment above | |
| 157 | + | Settings EngineSettingsPayload | |
| 158 | + | // Raw holds the implementation-specific config for legacy adapters. | |
| 159 | + | // WebKit engine currently expects *infrastructure/config.Config. | |
| 160 | + | Raw any //nolint:iface // intentional legacy bridge; prefer Settings for new code | |
| 161 | 161 | } | |
| 162 | 162 | ||
| 163 | 163 | // WebUIMessageHandler handles a decoded message payload from the JS bridge. | |
| @@ -142,22 +142,22 @@ type Engine interface { | |||||
| 142 | SetHandlerContext(ctx context.Context) | 142 | SetHandlerContext(ctx context.Context) | ||
| 143 | } | 143 | } | ||
| 144 | 144 | ||||
| 145 | + | // EngineSettingsPayload is the engine-facing boundary view of runtime config. | |||
| 146 | + | // Add fields here only when an engine needs to react to them at runtime. | |||
| 147 | + | type EngineSettingsPayload struct { | |||
| 148 | + | DefaultUIScale float64 | |||
| 149 | + | } | |||
| 150 | + | ||||
| 145 | // EngineSettingsUpdate carries a runtime config change to the engine. | 151 | // EngineSettingsUpdate carries a runtime config change to the engine. | ||
| 146 | // | 152 | // | ||
| 147 | - | // The Raw field holds the implementation-specific config object; each engine | 153 | + | // Settings exposes the typed boundary payload that engine adapters should prefer. |
| 148 | - | // implementation type-asserts it to its concrete config type (e.g. the WebKit | 154 | + | // Raw remains for legacy adapters that still consume implementation-specific |
| 149 | - | // engine asserts to *infrastructure/config.Config). | 155 | + | // config until their settings managers are narrowed to boundary fields. |
| 150 | - | // | |||
| 151 | - | // This is the only intentional use of `any` in the Engine port. A fully typed | |||
| 152 | - | // approach would require either moving the entire config schema into the domain | |||
| 153 | - | // layer (a large refactor) or defining a SettingsProvider interface — which | |||
| 154 | - | // would itself reduce to `any` because callers would embed engine-specific data | |||
| 155 | - | // behind an empty interface. Until the config schema is promoted to domain, | |||
| 156 | - | // the type assertion in the engine implementation is the least-invasive option. | |||
| 157 | type EngineSettingsUpdate struct { | 156 | type EngineSettingsUpdate struct { | ||
| 158 | - | // Raw holds the implementation-specific config. | 157 | + | Settings EngineSettingsPayload |
| 159 | - | // WebKit engine expects *infrastructure/config.Config. | 158 | + | // Raw holds the implementation-specific config for legacy adapters. |
| 160 | - | Raw any //nolint:iface // intentional: see type comment above | 159 | + | // WebKit engine currently expects *infrastructure/config.Config. |
| 160 | + | Raw any //nolint:iface // intentional legacy bridge; prefer Settings for new code | |||
| 161 | } | 161 | } | ||
| 162 | 162 | ||||
| 163 | // WebUIMessageHandler handles a decoded message payload from the JS bridge. | 163 | // WebUIMessageHandler handles a decoded message payload from the JS bridge. | ||
M internal/infrastructure/cef/engine.go +26 -16
| @@ -10,8 +10,6 @@ import ( | |||
| 10 | 10 | "time" | |
| 11 | 11 | "unicode/utf8" | |
| 12 | 12 | ||
| 13 | - | "github.com/bnema/dumber/internal/infrastructure/config" | |
| 14 | - | ||
| 15 | 13 | purecef "github.com/bnema/purego-cef/cef" | |
| 16 | 14 | cef2gtk "github.com/bnema/purego-cef2gtk" | |
| 17 | 15 | ||
| @@ -27,13 +25,14 @@ var _ port.AlreadyRunningAppRelaunchHandlerSetter = (*Engine)(nil) | |||
| 27 | 25 | // Engine implements port.Engine for the CEF browser backend. | |
| 28 | 26 | // It manages the CEF lifecycle and provides access to all engine subsystems. | |
| 29 | 27 | type Engine struct { | |
| 30 | - | ctx context.Context | |
| 31 | - | factory *WebViewFactory | |
| 32 | - | pool *WebViewPool | |
| 33 | - | profileLogDir string | |
| 34 | - | runtimeCEFDir string | |
| 35 | - | renderStackPlan cef2gtk.RenderStackPlan | |
| 36 | - | applicationScale float64 | |
| 28 | + | ctx context.Context | |
| 29 | + | factory *WebViewFactory | |
| 30 | + | pool *WebViewPool | |
| 31 | + | profileLogDir string | |
| 32 | + | runtimeCEFDir string | |
| 33 | + | renderStackPlan cef2gtk.RenderStackPlan | |
| 34 | + | applicationScaleMu sync.RWMutex | |
| 35 | + | applicationScale float64 | |
| 37 | 36 | ||
| 38 | 37 | messageRouter *MessageRouter | |
| 39 | 38 | schemeHandler *dumbSchemeHandler | |
| @@ -357,21 +356,32 @@ func (e *Engine) UpdateAppearance(_ context.Context, r, g, b, alpha float64) err | |||
| 357 | 356 | // a new engine lifetime. Browser zoom remains independently adjustable at | |
| 358 | 357 | // runtime through SetZoomLevel. | |
| 359 | 358 | func (e *Engine) UpdateSettings(ctx context.Context, update port.EngineSettingsUpdate) error { | |
| 360 | - | cfg, ok := update.Raw.(*config.Config) | |
| 361 | - | if !ok { | |
| 362 | - | return fmt.Errorf("UpdateSettings: expected *config.Config, got %T", update.Raw) | |
| 359 | + | newScale := normalizedApplicationScale(update.Settings.DefaultUIScale) | |
| 360 | + | e.applicationScaleMu.Lock() | |
| 361 | + | oldScale := e.applicationScale | |
| 362 | + | if oldScale != newScale { | |
| 363 | + | e.applicationScale = newScale | |
| 363 | 364 | } | |
| 364 | - | newScale := normalizedApplicationScale(cfg.DefaultUIScale) | |
| 365 | - | if e.applicationScale != newScale { | |
| 365 | + | e.applicationScaleMu.Unlock() | |
| 366 | + | ||
| 367 | + | if oldScale != newScale { | |
| 366 | 368 | logging.FromContext(ctx).Info(). | |
| 367 | - | Float64("old_application_scale", e.applicationScale). | |
| 369 | + | Float64("old_application_scale", oldScale). | |
| 368 | 370 | Float64("new_application_scale", newScale). | |
| 369 | 371 | Msg("cef: application scale update will apply to newly-created CEF views") | |
| 370 | - | e.applicationScale = newScale | |
| 371 | 372 | } | |
| 372 | 373 | return nil | |
| 373 | 374 | } | |
| 374 | 375 | ||
| 376 | + | func (e *Engine) currentApplicationScale() float64 { | |
| 377 | + | if e == nil { | |
| 378 | + | return 1 | |
| 379 | + | } | |
| 380 | + | e.applicationScaleMu.RLock() | |
| 381 | + | defer e.applicationScaleMu.RUnlock() | |
| 382 | + | return normalizedApplicationScale(e.applicationScale) | |
| 383 | + | } | |
| 384 | + | ||
| 375 | 385 | func (e *Engine) handleExplicitClipboardBridgeText(viewID port.WebViewID, action, text string) { | |
| 376 | 386 | if e == nil || text == "" { | |
| 377 | 387 | return | |
| @@ -10,8 +10,6 @@ import ( | |||||
| 10 | "time" | 10 | "time" | ||
| 11 | "unicode/utf8" | 11 | "unicode/utf8" | ||
| 12 | 12 | ||||
| 13 | - | "github.com/bnema/dumber/internal/infrastructure/config" | |||
| 14 | - | ||||
| 15 | purecef "github.com/bnema/purego-cef/cef" | 13 | purecef "github.com/bnema/purego-cef/cef" | ||
| 16 | cef2gtk "github.com/bnema/purego-cef2gtk" | 14 | cef2gtk "github.com/bnema/purego-cef2gtk" | ||
| 17 | 15 | ||||
| @@ -27,13 +25,14 @@ var _ port.AlreadyRunningAppRelaunchHandlerSetter = (*Engine)(nil) | |||||
| 27 | // Engine implements port.Engine for the CEF browser backend. | 25 | // Engine implements port.Engine for the CEF browser backend. | ||
| 28 | // It manages the CEF lifecycle and provides access to all engine subsystems. | 26 | // It manages the CEF lifecycle and provides access to all engine subsystems. | ||
| 29 | type Engine struct { | 27 | type Engine struct { | ||
| 30 | - | ctx context.Context | 28 | + | ctx context.Context |
| 31 | - | factory *WebViewFactory | 29 | + | factory *WebViewFactory |
| 32 | - | pool *WebViewPool | 30 | + | pool *WebViewPool |
| 33 | - | profileLogDir string | 31 | + | profileLogDir string |
| 34 | - | runtimeCEFDir string | 32 | + | runtimeCEFDir string |
| 35 | - | renderStackPlan cef2gtk.RenderStackPlan | 33 | + | renderStackPlan cef2gtk.RenderStackPlan |
| 36 | - | applicationScale float64 | 34 | + | applicationScaleMu sync.RWMutex |
| 35 | + | applicationScale float64 | |||
| 37 | 36 | ||||
| 38 | messageRouter *MessageRouter | 37 | messageRouter *MessageRouter | ||
| 39 | schemeHandler *dumbSchemeHandler | 38 | schemeHandler *dumbSchemeHandler | ||
| @@ -357,21 +356,32 @@ func (e *Engine) UpdateAppearance(_ context.Context, r, g, b, alpha float64) err | |||||
| 357 | // a new engine lifetime. Browser zoom remains independently adjustable at | 356 | // a new engine lifetime. Browser zoom remains independently adjustable at | ||
| 358 | // runtime through SetZoomLevel. | 357 | // runtime through SetZoomLevel. | ||
| 359 | func (e *Engine) UpdateSettings(ctx context.Context, update port.EngineSettingsUpdate) error { | 358 | func (e *Engine) UpdateSettings(ctx context.Context, update port.EngineSettingsUpdate) error { | ||
| 360 | - | cfg, ok := update.Raw.(*config.Config) | 359 | + | newScale := normalizedApplicationScale(update.Settings.DefaultUIScale) |
| 361 | - | if !ok { | 360 | + | e.applicationScaleMu.Lock() |
| 362 | - | return fmt.Errorf("UpdateSettings: expected *config.Config, got %T", update.Raw) | 361 | + | oldScale := e.applicationScale |
| 362 | + | if oldScale != newScale { | |||
| 363 | + | e.applicationScale = newScale | |||
| 363 | } | 364 | } | ||
| 364 | - | newScale := normalizedApplicationScale(cfg.DefaultUIScale) | 365 | + | e.applicationScaleMu.Unlock() |
| 365 | - | if e.applicationScale != newScale { | 366 | + | |
| 367 | + | if oldScale != newScale { | |||
| 366 | logging.FromContext(ctx).Info(). | 368 | logging.FromContext(ctx).Info(). | ||
| 367 | - | Float64("old_application_scale", e.applicationScale). | 369 | + | Float64("old_application_scale", oldScale). |
| 368 | Float64("new_application_scale", newScale). | 370 | Float64("new_application_scale", newScale). | ||
| 369 | Msg("cef: application scale update will apply to newly-created CEF views") | 371 | Msg("cef: application scale update will apply to newly-created CEF views") | ||
| 370 | - | e.applicationScale = newScale | |||
| 371 | } | 372 | } | ||
| 372 | return nil | 373 | return nil | ||
| 373 | } | 374 | } | ||
| 374 | 375 | ||||
| 376 | + | func (e *Engine) currentApplicationScale() float64 { | |||
| 377 | + | if e == nil { | |||
| 378 | + | return 1 | |||
| 379 | + | } | |||
| 380 | + | e.applicationScaleMu.RLock() | |||
| 381 | + | defer e.applicationScaleMu.RUnlock() | |||
| 382 | + | return normalizedApplicationScale(e.applicationScale) | |||
| 383 | + | } | |||
| 384 | + | ||||
| 375 | func (e *Engine) handleExplicitClipboardBridgeText(viewID port.WebViewID, action, text string) { | 385 | func (e *Engine) handleExplicitClipboardBridgeText(viewID port.WebViewID, action, text string) { | ||
| 376 | if e == nil || text == "" { | 386 | if e == nil || text == "" { | ||
| 377 | return | 387 | return | ||
M internal/infrastructure/cef/engine_init.go +1 -1
| @@ -88,7 +88,7 @@ func NewEngine( | |||
| 88 | 88 | Bool("external_begin_frame", externalBeginFrameEnabled()). | |
| 89 | 89 | Bool("trace_handlers", cfg.TraceHandlers). | |
| 90 | 90 | Bool("enable_audio_handler", cfg.EnableAudioHandler). | |
| 91 | - | Float64("application_scale", eng.applicationScale). | |
| 91 | + | Float64("application_scale", eng.currentApplicationScale()). | |
| 92 | 92 | Msg("cef: configured engine") | |
| 93 | 93 | ||
| 94 | 94 | if err := initializeCEF(eng, settings, logger); err != nil { | |
| @@ -88,7 +88,7 @@ func NewEngine( | |||||
| 88 | Bool("external_begin_frame", externalBeginFrameEnabled()). | 88 | Bool("external_begin_frame", externalBeginFrameEnabled()). | ||
| 89 | Bool("trace_handlers", cfg.TraceHandlers). | 89 | Bool("trace_handlers", cfg.TraceHandlers). | ||
| 90 | Bool("enable_audio_handler", cfg.EnableAudioHandler). | 90 | Bool("enable_audio_handler", cfg.EnableAudioHandler). | ||
| 91 | - | Float64("application_scale", eng.applicationScale). | 91 | + | Float64("application_scale", eng.currentApplicationScale()). |
| 92 | Msg("cef: configured engine") | 92 | Msg("cef: configured engine") | ||
| 93 | 93 | ||||
| 94 | if err := initializeCEF(eng, settings, logger); err != nil { | 94 | if err := initializeCEF(eng, settings, logger); err != nil { | ||
M internal/infrastructure/cef/engine_settings_test.go +34 -10
| @@ -2,27 +2,51 @@ package cef | |||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "context" | |
| 5 | + | "sync" | |
| 5 | 6 | "testing" | |
| 6 | 7 | ||
| 7 | 8 | "github.com/bnema/dumber/internal/application/port" | |
| 8 | - | "github.com/bnema/dumber/internal/infrastructure/config" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | - | func TestEngineUpdateSettingsUpdatesApplicationScaleForNewViews(t *testing.T) { | |
| 11 | + | func TestEngineUpdateSettingsUpdatesApplicationScaleFromBoundaryPayload(t *testing.T) { | |
| 12 | 12 | e := &Engine{applicationScale: 1} | |
| 13 | - | cfg := &config.Config{DefaultUIScale: 1.25} | |
| 14 | 13 | ||
| 15 | - | if err := e.UpdateSettings(context.Background(), port.EngineSettingsUpdate{Raw: cfg}); err != nil { | |
| 14 | + | if err := e.UpdateSettings(context.Background(), port.EngineSettingsUpdate{ | |
| 15 | + | Settings: port.EngineSettingsPayload{DefaultUIScale: 1.25}, | |
| 16 | + | }); err != nil { | |
| 16 | 17 | t.Fatalf("UpdateSettings returned error: %v", err) | |
| 17 | 18 | } | |
| 18 | - | if e.applicationScale != 1.25 { | |
| 19 | - | t.Fatalf("applicationScale=%v, want 1.25", e.applicationScale) | |
| 19 | + | if got := e.currentApplicationScale(); got != 1.25 { | |
| 20 | + | t.Fatalf("applicationScale=%v, want 1.25", got) | |
| 20 | 21 | } | |
| 21 | 22 | } | |
| 22 | 23 | ||
| 23 | - | func TestEngineUpdateSettingsRejectsUnexpectedConfig(t *testing.T) { | |
| 24 | - | e := &Engine{} | |
| 25 | - | if err := e.UpdateSettings(context.Background(), port.EngineSettingsUpdate{Raw: "wrong"}); err == nil { | |
| 26 | - | t.Fatal("expected error for unexpected config type") | |
| 24 | + | func TestEngineUpdateSettingsIgnoresLegacyRawForApplicationScale(t *testing.T) { | |
| 25 | + | e := &Engine{applicationScale: 1} | |
| 26 | + | ||
| 27 | + | if err := e.UpdateSettings(context.Background(), port.EngineSettingsUpdate{Raw: "wrong"}); err != nil { | |
| 28 | + | t.Fatalf("UpdateSettings returned error: %v", err) | |
| 29 | + | } | |
| 30 | + | if got := e.currentApplicationScale(); got != 1 { | |
| 31 | + | t.Fatalf("applicationScale=%v, want default 1", got) | |
| 32 | + | } | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | func TestEngineApplicationScaleConcurrentAccess(_ *testing.T) { | |
| 36 | + | e := &Engine{applicationScale: 1} | |
| 37 | + | var wg sync.WaitGroup | |
| 38 | + | for i := 0; i < 16; i++ { | |
| 39 | + | wg.Add(2) | |
| 40 | + | go func() { | |
| 41 | + | defer wg.Done() | |
| 42 | + | _ = e.currentApplicationScale() | |
| 43 | + | }() | |
| 44 | + | go func(scale float64) { | |
| 45 | + | defer wg.Done() | |
| 46 | + | _ = e.UpdateSettings(context.Background(), port.EngineSettingsUpdate{ | |
| 47 | + | Settings: port.EngineSettingsPayload{DefaultUIScale: scale}, | |
| 48 | + | }) | |
| 49 | + | }(1 + float64(i)/10) | |
| 27 | 50 | } | |
| 51 | + | wg.Wait() | |
| 28 | 52 | } | |
| @@ -2,27 +2,51 @@ package cef | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "context" | 4 | "context" | ||
| 5 | + | "sync" | |||
| 5 | "testing" | 6 | "testing" | ||
| 6 | 7 | ||||
| 7 | "github.com/bnema/dumber/internal/application/port" | 8 | "github.com/bnema/dumber/internal/application/port" | ||
| 8 | - | "github.com/bnema/dumber/internal/infrastructure/config" | |||
| 9 | ) | 9 | ) | ||
| 10 | 10 | ||||
| 11 | - | func TestEngineUpdateSettingsUpdatesApplicationScaleForNewViews(t *testing.T) { | 11 | + | func TestEngineUpdateSettingsUpdatesApplicationScaleFromBoundaryPayload(t *testing.T) { |
| 12 | e := &Engine{applicationScale: 1} | 12 | e := &Engine{applicationScale: 1} | ||
| 13 | - | cfg := &config.Config{DefaultUIScale: 1.25} | |||
| 14 | 13 | ||||
| 15 | - | if err := e.UpdateSettings(context.Background(), port.EngineSettingsUpdate{Raw: cfg}); err != nil { | 14 | + | if err := e.UpdateSettings(context.Background(), port.EngineSettingsUpdate{ |
| 15 | + | Settings: port.EngineSettingsPayload{DefaultUIScale: 1.25}, | |||
| 16 | + | }); err != nil { | |||
| 16 | t.Fatalf("UpdateSettings returned error: %v", err) | 17 | t.Fatalf("UpdateSettings returned error: %v", err) | ||
| 17 | } | 18 | } | ||
| 18 | - | if e.applicationScale != 1.25 { | 19 | + | if got := e.currentApplicationScale(); got != 1.25 { |
| 19 | - | t.Fatalf("applicationScale=%v, want 1.25", e.applicationScale) | 20 | + | t.Fatalf("applicationScale=%v, want 1.25", got) |
| 20 | } | 21 | } | ||
| 21 | } | 22 | } | ||
| 22 | 23 | ||||
| 23 | - | func TestEngineUpdateSettingsRejectsUnexpectedConfig(t *testing.T) { | 24 | + | func TestEngineUpdateSettingsIgnoresLegacyRawForApplicationScale(t *testing.T) { |
| 24 | - | e := &Engine{} | 25 | + | e := &Engine{applicationScale: 1} |
| 25 | - | if err := e.UpdateSettings(context.Background(), port.EngineSettingsUpdate{Raw: "wrong"}); err == nil { | 26 | + | |
| 26 | - | t.Fatal("expected error for unexpected config type") | 27 | + | if err := e.UpdateSettings(context.Background(), port.EngineSettingsUpdate{Raw: "wrong"}); err != nil { |
| 28 | + | t.Fatalf("UpdateSettings returned error: %v", err) | |||
| 29 | + | } | |||
| 30 | + | if got := e.currentApplicationScale(); got != 1 { | |||
| 31 | + | t.Fatalf("applicationScale=%v, want default 1", got) | |||
| 32 | + | } | |||
| 33 | + | } | |||
| 34 | + | ||||
| 35 | + | func TestEngineApplicationScaleConcurrentAccess(_ *testing.T) { | |||
| 36 | + | e := &Engine{applicationScale: 1} | |||
| 37 | + | var wg sync.WaitGroup | |||
| 38 | + | for i := 0; i < 16; i++ { | |||
| 39 | + | wg.Add(2) | |||
| 40 | + | go func() { | |||
| 41 | + | defer wg.Done() | |||
| 42 | + | _ = e.currentApplicationScale() | |||
| 43 | + | }() | |||
| 44 | + | go func(scale float64) { | |||
| 45 | + | defer wg.Done() | |||
| 46 | + | _ = e.UpdateSettings(context.Background(), port.EngineSettingsUpdate{ | |||
| 47 | + | Settings: port.EngineSettingsPayload{DefaultUIScale: scale}, | |||
| 48 | + | }) | |||
| 49 | + | }(1 + float64(i)/10) | |||
| 27 | } | 50 | } | ||
| 51 | + | wg.Wait() | |||
| 28 | } | 52 | } | ||
M internal/infrastructure/cef/factory.go +3 -2
| @@ -106,7 +106,8 @@ func (f *WebViewFactory) Create(ctx context.Context) (port.WebView, error) { | |||
| 106 | 106 | ||
| 107 | 107 | func (f *WebViewFactory) newWebView(ctx context.Context) (*WebView, error) { | |
| 108 | 108 | id := port.WebViewID(f.nextID.Add(1)) | |
| 109 | - | viewBridge := NewCef2gtkAdapter(f.engine.renderStackPlan, f.engine.applicationScale) | |
| 109 | + | applicationScale := f.engine.currentApplicationScale() | |
| 110 | + | viewBridge := NewCef2gtkAdapter(f.engine.renderStackPlan, applicationScale) | |
| 110 | 111 | if viewBridge == nil { | |
| 111 | 112 | return nil, fmt.Errorf("create cef2gtk view") | |
| 112 | 113 | } | |
| @@ -125,7 +126,7 @@ func (f *WebViewFactory) newWebView(ctx context.Context) (*WebView, error) { | |||
| 125 | 126 | } | |
| 126 | 127 | wv.runOnGTKSync(func() { | |
| 127 | 128 | nativeWidget := viewBridge.Widget() | |
| 128 | - | popupSurface := newPopupBridgeSurface(ctx, nativeWidget, f.engine.renderStackPlan, f.engine.applicationScale) | |
| 129 | + | popupSurface := newPopupBridgeSurface(ctx, nativeWidget, f.engine.renderStackPlan, applicationScale) | |
| 129 | 130 | wv.nativeWidget = nativeWidget | |
| 130 | 131 | if popupSurface != nil { | |
| 131 | 132 | wv.popupSurface = popupSurface | |
| @@ -106,7 +106,8 @@ func (f *WebViewFactory) Create(ctx context.Context) (port.WebView, error) { | |||||
| 106 | 106 | ||||
| 107 | func (f *WebViewFactory) newWebView(ctx context.Context) (*WebView, error) { | 107 | func (f *WebViewFactory) newWebView(ctx context.Context) (*WebView, error) { | ||
| 108 | id := port.WebViewID(f.nextID.Add(1)) | 108 | id := port.WebViewID(f.nextID.Add(1)) | ||
| 109 | - | viewBridge := NewCef2gtkAdapter(f.engine.renderStackPlan, f.engine.applicationScale) | 109 | + | applicationScale := f.engine.currentApplicationScale() |
| 110 | + | viewBridge := NewCef2gtkAdapter(f.engine.renderStackPlan, applicationScale) | |||
| 110 | if viewBridge == nil { | 111 | if viewBridge == nil { | ||
| 111 | return nil, fmt.Errorf("create cef2gtk view") | 112 | return nil, fmt.Errorf("create cef2gtk view") | ||
| 112 | } | 113 | } | ||
| @@ -125,7 +126,7 @@ func (f *WebViewFactory) newWebView(ctx context.Context) (*WebView, error) { | |||||
| 125 | } | 126 | } | ||
| 126 | wv.runOnGTKSync(func() { | 127 | wv.runOnGTKSync(func() { | ||
| 127 | nativeWidget := viewBridge.Widget() | 128 | nativeWidget := viewBridge.Widget() | ||
| 128 | - | popupSurface := newPopupBridgeSurface(ctx, nativeWidget, f.engine.renderStackPlan, f.engine.applicationScale) | 129 | + | popupSurface := newPopupBridgeSurface(ctx, nativeWidget, f.engine.renderStackPlan, applicationScale) |
| 129 | wv.nativeWidget = nativeWidget | 130 | wv.nativeWidget = nativeWidget | ||
| 130 | if popupSurface != nil { | 131 | if popupSurface != nil { | ||
| 131 | wv.popupSurface = popupSurface | 132 | wv.popupSurface = popupSurface | ||
M internal/infrastructure/cef/popup_surface.go +6 -1
| @@ -23,7 +23,12 @@ type popupBridgeSurface struct { | |||
| 23 | 23 | ||
| 24 | 24 | // newPopupBridgeSurface must be called on the GTK thread because it creates | |
| 25 | 25 | // and packs GTK widgets around the primary CEF widget. | |
| 26 | - | func newPopupBridgeSurface(ctx context.Context, mainWidget *gtk.Widget, plan cef2gtk.RenderStackPlan, applicationScale float64) *popupBridgeSurface { | |
| 26 | + | func newPopupBridgeSurface( | |
| 27 | + | ctx context.Context, | |
| 28 | + | mainWidget *gtk.Widget, | |
| 29 | + | plan cef2gtk.RenderStackPlan, | |
| 30 | + | applicationScale float64, | |
| 31 | + | ) *popupBridgeSurface { | |
| 27 | 32 | mainContext := glib.MainContextDefault() | |
| 28 | 33 | if mainContext == nil || !mainContext.IsOwner() { | |
| 29 | 34 | logging.FromContext(popupSurfaceLogContext(ctx)).Error().Msg("cef: newPopupBridgeSurface called off GTK thread") | |
| @@ -23,7 +23,12 @@ type popupBridgeSurface struct { | |||||
| 23 | 23 | ||||
| 24 | // newPopupBridgeSurface must be called on the GTK thread because it creates | 24 | // newPopupBridgeSurface must be called on the GTK thread because it creates | ||
| 25 | // and packs GTK widgets around the primary CEF widget. | 25 | // and packs GTK widgets around the primary CEF widget. | ||
| 26 | - | func newPopupBridgeSurface(ctx context.Context, mainWidget *gtk.Widget, plan cef2gtk.RenderStackPlan, applicationScale float64) *popupBridgeSurface { | 26 | + | func newPopupBridgeSurface( |
| 27 | + | ctx context.Context, | |||
| 28 | + | mainWidget *gtk.Widget, | |||
| 29 | + | plan cef2gtk.RenderStackPlan, | |||
| 30 | + | applicationScale float64, | |||
| 31 | + | ) *popupBridgeSurface { | |||
| 27 | mainContext := glib.MainContextDefault() | 32 | mainContext := glib.MainContextDefault() | ||
| 28 | if mainContext == nil || !mainContext.IsOwner() { | 33 | if mainContext == nil || !mainContext.IsOwner() { | ||
| 29 | logging.FromContext(popupSurfaceLogContext(ctx)).Error().Msg("cef: newPopupBridgeSurface called off GTK thread") | 34 | logging.FromContext(popupSurfaceLogContext(ctx)).Error().Msg("cef: newPopupBridgeSurface called off GTK thread") | ||
M internal/infrastructure/cef/scale_probe.go +3 -1
| @@ -5,6 +5,8 @@ import ( | |||
| 5 | 5 | "strings" | |
| 6 | 6 | ) | |
| 7 | 7 | ||
| 8 | + | const scaleProbeRoundFactor = 1_000_000 | |
| 9 | + | ||
| 8 | 10 | type cefScaleProbeMetrics struct { | |
| 9 | 11 | SurfaceWidth int32 | |
| 10 | 12 | SurfaceHeight int32 | |
| @@ -62,7 +64,7 @@ func roundedScale(v float64) float64 { | |||
| 62 | 64 | if math.IsNaN(v) || math.IsInf(v, 0) { | |
| 63 | 65 | return 1 | |
| 64 | 66 | } | |
| 65 | - | return math.Round(v*1_000_000) / 1_000_000 | |
| 67 | + | return math.Round(v*scaleProbeRoundFactor) / scaleProbeRoundFactor | |
| 66 | 68 | } | |
| 67 | 69 | ||
| 68 | 70 | const cefScaleProbeScript = `(function(){ | |
| @@ -5,6 +5,8 @@ import ( | |||||
| 5 | "strings" | 5 | "strings" | ||
| 6 | ) | 6 | ) | ||
| 7 | 7 | ||||
| 8 | + | const scaleProbeRoundFactor = 1_000_000 | |||
| 9 | + | ||||
| 8 | type cefScaleProbeMetrics struct { | 10 | type cefScaleProbeMetrics struct { | ||
| 9 | SurfaceWidth int32 | 11 | SurfaceWidth int32 | ||
| 10 | SurfaceHeight int32 | 12 | SurfaceHeight int32 | ||
| @@ -62,7 +64,7 @@ func roundedScale(v float64) float64 { | |||||
| 62 | if math.IsNaN(v) || math.IsInf(v, 0) { | 64 | if math.IsNaN(v) || math.IsInf(v, 0) { | ||
| 63 | return 1 | 65 | return 1 | ||
| 64 | } | 66 | } | ||
| 65 | - | return math.Round(v*1_000_000) / 1_000_000 | 67 | + | return math.Round(v*scaleProbeRoundFactor) / scaleProbeRoundFactor |
| 66 | } | 68 | } | ||
| 67 | 69 | ||||
| 68 | const cefScaleProbeScript = `(function(){ | 70 | const cefScaleProbeScript = `(function(){ | ||
M internal/ui/app.go +4 -1
| @@ -4827,7 +4827,10 @@ func (a *App) applyAppearanceConfig(ctx context.Context) { | |||
| 4827 | 4827 | cfg := a.deps.Config | |
| 4828 | 4828 | ||
| 4829 | 4829 | if a.engine != nil { | |
| 4830 | - | if err := a.engine.UpdateSettings(ctx, port.EngineSettingsUpdate{Raw: cfg}); err != nil { | |
| 4830 | + | if err := a.engine.UpdateSettings(ctx, port.EngineSettingsUpdate{ | |
| 4831 | + | Settings: port.EngineSettingsPayload{DefaultUIScale: cfg.DefaultUIScale}, | |
| 4832 | + | Raw: cfg, | |
| 4833 | + | }); err != nil { | |
| 4831 | 4834 | log.Warn().Err(err).Msg("failed to apply engine settings update") | |
| 4832 | 4835 | } | |
| 4833 | 4836 | } | |
| @@ -4827,7 +4827,10 @@ func (a *App) applyAppearanceConfig(ctx context.Context) { | |||||
| 4827 | cfg := a.deps.Config | 4827 | cfg := a.deps.Config | ||
| 4828 | 4828 | ||||
| 4829 | if a.engine != nil { | 4829 | if a.engine != nil { | ||
| 4830 | - | if err := a.engine.UpdateSettings(ctx, port.EngineSettingsUpdate{Raw: cfg}); err != nil { | 4830 | + | if err := a.engine.UpdateSettings(ctx, port.EngineSettingsUpdate{ |
| 4831 | + | Settings: port.EngineSettingsPayload{DefaultUIScale: cfg.DefaultUIScale}, | |||
| 4832 | + | Raw: cfg, | |||
| 4833 | + | }); err != nil { | |||
| 4831 | log.Warn().Err(err).Msg("failed to apply engine settings update") | 4834 | log.Warn().Err(err).Msg("failed to apply engine settings update") | ||
| 4832 | } | 4835 | } | ||
| 4833 | } | 4836 | } | ||