v0.32.0 0617278

render_stack_env.go RawHistoryFolder

1package cef
2
3import (
4	"context"
5	"os"
6	"strings"
7
8	"github.com/bnema/dumber/internal/application/port"
9)
10
11const (
12	dumberRenderStackEnvVar           = "DUMBER_RENDER_STACK"
13	dumberRenderStackAllowSplitEnvVar = "DUMBER_RENDER_STACK_ALLOW_SPLIT"
14
15	cefEngineType                = "cef"
16	renderStackAuto              = "auto"
17	renderStackVulkanDMABUF      = "vulkan-dmabuf"
18	renderStackLegacyGL          = "legacy-gl"
19	cef2gtkBackendEnvVar         = "PUREGO_CEF2GTK_BACKEND"
20	cef2gtkAngleBackendVar       = "PUREGO_CEF2GTK_ANGLE_BACKEND"
21	cef2gtkOSRBackingScaleEnvVar = "PUREGO_CEF2GTK_OSR_BACKING_SCALE"
22	gskRendererEnvVar            = "GSK_RENDERER"
23	cef2gtkBackendGDKDMABUF      = "gdk-dmabuf"
24	cef2gtkBackendGLArea         = "glarea"
25	cef2gtkAngleVulkan           = "vulkan"
26	cef2gtkAngleGLEGL            = "gl-egl"
27	gskRendererVulkan            = "vulkan"
28	gskRendererOpenGL            = "opengl"
29)
30
31// ApplyDefaultRenderStackEnvironment configures the process environment for
32// Dumber's default CEF presentation stack before GTK/libadwaita initialize.
33//
34// DUMBER_RENDER_STACK accepts:
35//   - empty/default or vulkan-dmabuf: use GDK DMABUF presentation with GSK Vulkan and CEF ANGLE Vulkan
36//   - auto: alias for vulkan-dmabuf
37//   - legacy-gl: force the older GtkGLArea/OpenGL bridge for diagnostics
38//
39// Low-level env vars are treated as library-development escape hatches only.
40// Dumber keeps the selected stack coherent by default and overwrites conflicts;
41// set DUMBER_RENDER_STACK_ALLOW_SPLIT=1 to preserve explicit split-stack values.
42func ApplyDefaultRenderStackEnvironment(logger port.Logger) string {
43	return applyDefaultRenderStackEnvironment(logger)
44}
45
46func applyDefaultRenderStackEnvironment(logger port.Logger) string {
47	stack := normalizeRenderStack(logger, os.Getenv(dumberRenderStackEnvVar))
48	switch stack {
49	case renderStackLegacyGL:
50		setRenderStackEnv(logger, gskRendererEnvVar, gskRendererOpenGL)
51		setRenderStackEnv(logger, cef2gtkBackendEnvVar, cef2gtkBackendGLArea)
52		setRenderStackEnv(logger, cef2gtkAngleBackendVar, cef2gtkAngleGLEGL)
53	default:
54		stack = renderStackVulkanDMABUF
55		setRenderStackEnv(logger, gskRendererEnvVar, gskRendererVulkan)
56		setRenderStackEnv(logger, cef2gtkBackendEnvVar, cef2gtkBackendGDKDMABUF)
57		setRenderStackEnv(logger, cef2gtkAngleBackendVar, cef2gtkAngleVulkan)
58		setEnvDefault(logger, cef2gtkOSRBackingScaleEnvVar, "auto")
59	}
60	logRenderStackEnvironment(logger, stack)
61	return stack
62}
63
64func logRenderStackEnvironment(logger port.Logger, stack string) {
65	logInfo(logger, "cef: render stack environment configured",
66		port.Field("render_stack", stack),
67		port.Field("gsk_renderer", os.Getenv(gskRendererEnvVar)),
68		port.Field("cef2gtk_backend", os.Getenv(cef2gtkBackendEnvVar)),
69		port.Field("cef2gtk_angle_backend", os.Getenv(cef2gtkAngleBackendVar)),
70		port.Field("cef2gtk_osr_backing_scale", os.Getenv(cef2gtkOSRBackingScaleEnvVar)),
71		port.Field("split_stack_allowed", envBoolEnabled(dumberRenderStackAllowSplitEnvVar)),
72	)
73}
74
75// HardwareDecodeEnvironmentOptions carries config-derived CEF environment
76// decisions from the composition root without making the CEF adapter import
77// sibling configuration or environment adapters directly.
78type HardwareDecodeEnvironmentOptions struct {
79	EngineType               string
80	HardwareDecodingDisabled bool
81	RenderingEnvManager      port.RenderingEnvManager
82	Logger                   port.Logger
83}
84
85// ApplyDefaultHardwareDecodeEnvironment maps Dumber's media config to CEF's
86// developer-facing VAAPI switch env var before CEF command-line callbacks run.
87// Existing explicit env values are preserved as low-level escape hatches.
88func ApplyDefaultHardwareDecodeEnvironment(ctx context.Context, opts HardwareDecodeEnvironmentOptions) {
89	if opts.EngineType != cefEngineType {
90		return
91	}
92	if opts.HardwareDecodingDisabled {
93		setEnvDefault(opts.Logger, cefEnableVAAPIEnvVar, "0")
94		return
95	}
96	setEnvDefault(opts.Logger, cefEnableVAAPIEnvVar, "1")
97	applyDefaultLIBVADriverEnvironment(ctx, opts.RenderingEnvManager, opts.Logger)
98}
99
100func applyDefaultLIBVADriverEnvironment(ctx context.Context, manager port.RenderingEnvManager, logger port.Logger) {
101	if strings.TrimSpace(os.Getenv("LIBVA_DRIVER_NAME")) != "" || manager == nil {
102		return
103	}
104	switch manager.DetectGPUVendor(ctx) {
105	case port.GPUVendorAMD:
106		setEnvDefault(logger, "LIBVA_DRIVER_NAME", "radeonsi")
107	case port.GPUVendorIntel:
108		setEnvDefault(logger, "LIBVA_DRIVER_NAME", "iHD")
109	case port.GPUVendorNVIDIA:
110		logWarn(logger, "cef: not defaulting LIBVA_DRIVER_NAME for NVIDIA; Chromium/CEF VAAPI support is driver-dependent")
111	}
112}
113
114func normalizeRenderStack(logger port.Logger, value string) string {
115	normalized := strings.ToLower(strings.TrimSpace(value))
116	switch normalized {
117	case "", renderStackAuto, renderStackVulkanDMABUF:
118		return renderStackVulkanDMABUF
119	case renderStackLegacyGL:
120		return renderStackLegacyGL
121	default:
122		logWarn(logger, "cef: unknown render stack, falling back to default",
123			port.Field("render_stack", value),
124			port.Field("fallback", renderStackVulkanDMABUF),
125		)
126		return renderStackVulkanDMABUF
127	}
128}
129
130func setRenderStackEnv(logger port.Logger, key, value string) {
131	if existing := strings.TrimSpace(os.Getenv(key)); existing != "" {
132		fields := []port.LogField{
133			port.Field("key", key),
134			port.Field("value", existing),
135			port.Field("stack_value", value),
136		}
137		if envBoolEnabled(dumberRenderStackAllowSplitEnvVar) {
138			logWarn(logger, "cef: preserving explicit split render stack environment override", fields...)
139			return
140		}
141		logWarn(logger, "cef: overriding low-level render stack environment to keep Dumber stack coherent", fields...)
142	}
143	_ = os.Setenv(key, value)
144}
145
146func setEnvDefault(logger port.Logger, key, value string) {
147	if existing := strings.TrimSpace(os.Getenv(key)); existing != "" {
148		logDebug(logger, "cef: preserving explicit environment override",
149			port.Field("key", key),
150			port.Field("value", existing),
151		)
152		return
153	}
154	_ = os.Setenv(key, value)
155}
156
157func logDebug(logger port.Logger, msg string, fields ...port.LogField) {
158	if logger != nil {
159		logger.Debug(msg, fields...)
160	}
161}
162
163func logInfo(logger port.Logger, msg string, fields ...port.LogField) {
164	if logger != nil {
165		logger.Info(msg, fields...)
166	}
167}
168
169func logWarn(logger port.Logger, msg string, fields ...port.LogField) {
170	if logger != nil {
171		logger.Warn(msg, fields...)
172	}
173}

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