1package bootstrap
2
3import (
4 "context"
5 "encoding/json"
6 "fmt"
7
8 "github.com/bnema/dumber/internal/application/dto"
9 "github.com/bnema/dumber/internal/application/port"
10 "github.com/bnema/dumber/internal/application/usecase"
11 audiofactory "github.com/bnema/dumber/internal/infrastructure/audio/factory"
12 "github.com/bnema/dumber/internal/infrastructure/cef"
13 clipboardinfra "github.com/bnema/dumber/internal/infrastructure/clipboard"
14 "github.com/bnema/dumber/internal/infrastructure/config"
15 "github.com/bnema/dumber/internal/infrastructure/env"
16 "github.com/bnema/dumber/internal/infrastructure/gtkmenu"
17 "github.com/bnema/dumber/internal/infrastructure/handlers"
18 "github.com/bnema/dumber/internal/infrastructure/runtimeprofile"
19 "github.com/bnema/dumber/internal/infrastructure/webkit"
20 "github.com/bnema/dumber/internal/ui/theme"
21 "github.com/rs/zerolog"
22)
23
24// EngineInput holds the input for BuildEngine.
25type EngineInput struct {
26 Ctx context.Context
27 Config *config.Config
28 RuntimeProfile runtimeprofile.Profile
29 ThemeManager *theme.Manager
30 ColorResolver port.ColorSchemeResolver
31 Logger zerolog.Logger
32}
33
34// BuildEngine constructs a port.Engine for the engine type specified in cfg.Engine.Type.
35func BuildEngine(input EngineInput) (port.Engine, error) {
36 cfg := input.Config
37 systemviewReader := config.NewSystemviewConfigReader(env.NewHardwareSurveyor())
38 systemviewUC := usecase.NewReadSystemviewConfigUseCase(systemviewReader)
39 buildConfigPayload := func(read func(context.Context) (dto.SystemviewConfigPayload, error)) func() ([]byte, error) {
40 return func() ([]byte, error) {
41 payload, err := read(input.Ctx)
42 if err != nil {
43 return nil, err
44 }
45 return json.Marshal(payload)
46 }
47 }
48 currentConfigPayload := buildConfigPayload(systemviewUC.Current)
49 defaultConfigPayload := buildConfigPayload(systemviewUC.Default)
50 contextMenuBuilder := usecase.NewBuildContextMenuUseCase()
51 contextMenuExecutorFactory := &usecase.ContextMenuActionExecutorFactory{}
52 engineType := cfg.Engine.ResolveEngineType()
53 switch engineType {
54 case config.EngineTypeWebKit:
55 profile, err := requireRuntimeProfile(input.RuntimeProfile, engineType)
56 if err != nil {
57 return nil, err
58 }
59 opts := port.EngineOptions{
60 CookiePolicy: port.CookiePolicy(cfg.Engine.CookiePolicy),
61 }
62 wkCfg := webkit.EngineConfigFromConfig(cfg.Engine.WebKit)
63
64 return webkit.NewEngine(
65 input.Ctx, cfg, opts, profile, wkCfg,
66 currentConfigPayload, defaultConfigPayload,
67 input.ThemeManager, input.ColorResolver,
68 contextMenuBuilder, contextMenuExecutorFactory,
69 input.Logger,
70 )
71 case config.EngineTypeCEF:
72 profile, err := requireRuntimeProfile(input.RuntimeProfile, engineType)
73 if err != nil {
74 return nil, err
75 }
76 opts := port.EngineOptions{
77 CookiePolicy: port.CookiePolicy(cfg.Engine.CookiePolicy),
78 }
79 cefCfg := cef.RuntimeConfig{
80 CEFDir: cfg.Engine.CEF.CEFDir,
81 LogFile: cfg.Engine.CEF.LogFile,
82 LogSeverity: cfg.Engine.CEF.LogSeverity,
83 RenderStack: string(cfg.Engine.CEF.CEFRenderStack()),
84 AdaptiveWindowlessFrameRate: cfg.Engine.CEF.CEFAdaptiveWindowlessFrameRate(),
85 WindowlessFrameRate: cfg.Engine.CEF.WindowlessFrameRate,
86 WindowlessFrameRateMax: cfg.Engine.CEF.CEFWindowlessFrameRateMax(),
87 EnableAudioHandler: cfg.Engine.CEF.EnableAudioHandler,
88 TraceHandlers: cfg.Engine.CEF.TraceHandlers,
89 }
90 deps := cef.EngineDependencies{
91 RegisterHandlers: handlers.RegisterAll,
92 RegisterAccentHandlers: handlers.RegisterAccentHandlers,
93 CurrentConfigPayload: currentConfigPayload,
94 DefaultConfigPayload: defaultConfigPayload,
95 ContextMenuBuilder: contextMenuBuilder,
96 ContextMenuExecutorFactory: contextMenuExecutorFactory,
97 ContextMenuRenderer: gtkmenu.NewRenderer(nil),
98 Clipboard: clipboardinfra.New(),
99 ImageDataResolver: webkit.NewContextMenuResolver(),
100 }
101 audioFactory := audiofactory.NewAudioOutputFactory()
102 return cef.NewEngine(input.Ctx, opts, cef.RuntimePaths{
103 StateRoot: profile.CEFUserDataDir(),
104 LogFile: profile.CEFLogFile(),
105 ProfileLogDir: profile.Shared.LogDir,
106 }, cefCfg, audioFactory, deps)
107 default:
108 return nil, fmt.Errorf("unknown engine type: %q", engineType)
109 }
110}
111
112func requireRuntimeProfile(profile runtimeprofile.Profile, engineType string) (runtimeprofile.Profile, error) {
113 if profile.Engine == "" {
114 return runtimeprofile.Profile{}, fmt.Errorf("missing runtime profile for %s engine", engineType)
115 }
116 if profile.Engine != engineType {
117 return runtimeprofile.Profile{}, fmt.Errorf("runtime profile engine %q does not match %q", profile.Engine, engineType)
118 }
119 return profile, nil
120}