diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index ce41cca7b7..3a05cbfd37 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -1579,6 +1579,8 @@
"enable-shadows": "Shadows",
"enable-backdrop-effects": "Background effects for menus, popups and panels",
"enable-smooth-scroll": "Smooth scrolling",
+ "enable-hardware-acceleration": "Hardware acceleration (GPU)",
+ "enable-hardware-acceleration-description": "Disable this if the app shows a blank window or has display glitches caused by your GPU drivers. Requires an app restart.",
"app-restart-required": "Requires app restart"
},
"zoom_factor": {
diff --git a/apps/client/src/widgets/type_widgets/options/appearance.tsx b/apps/client/src/widgets/type_widgets/options/appearance.tsx
index 972f2da637..b93026dd99 100644
--- a/apps/client/src/widgets/type_widgets/options/appearance.tsx
+++ b/apps/client/src/widgets/type_widgets/options/appearance.tsx
@@ -658,6 +658,8 @@ function Performance() {
{isElectron() && }
+ {isElectron() && }
+
;
}
@@ -673,6 +675,18 @@ function SmoothScrollEnabledOption() {
/>;
}
+function HardwareAccelerationOption() {
+ const [ hardwareAccelerationEnabled, setHardwareAccelerationEnabled ] = useTriliumOptionBool("hardwareAccelerationEnabled");
+
+ return ;
+}
+
function MaxContentWidth() {
const [maxContentWidth, setMaxContentWidth] = useTriliumOption("maxContentWidth");
const [centerContent, setCenterContent] = useTriliumOptionBool("centerContent");
diff --git a/apps/desktop/src/main.spec.ts b/apps/desktop/src/main.spec.ts
index 3be7ce5b30..5cab3134e0 100644
--- a/apps/desktop/src/main.spec.ts
+++ b/apps/desktop/src/main.spec.ts
@@ -53,6 +53,8 @@ const h = vi.hoisted(() => ({
isPrimaryInstance: true as boolean,
allWindows: [] as unknown[],
smoothScroll: "true" as string | null,
+ hardwareAcceleration: "true" as string | null,
+ disableHardwareAcceleration: vi.fn(),
// When true, reading an option throws (simulates first run before the schema exists).
dbUninitialized: false as boolean,
isDbInitialized: true as boolean,
@@ -91,6 +93,7 @@ vi.mock("electron", () => {
setName: (...a: unknown[]) => h.setName(...a),
setUserTasks: (...a: unknown[]) => h.setUserTasks(...a),
commandLine: { appendSwitch: (...a: unknown[]) => h.appendSwitch(...a) },
+ disableHardwareAcceleration: (...a: unknown[]) => h.disableHardwareAcceleration(...a),
on: (event: string, cb: Handler) => h.appOn.set(event, cb),
quit: (...a: unknown[]) => h.quit(...a),
exit: (...a: unknown[]) => h.exit(...a),
@@ -183,6 +186,7 @@ vi.mock("@triliumnext/server/src/sql_provider.js", () => ({
if (h.dbUninitialized) throw new Error("no such table: options");
const values: Record = {
smoothScrollEnabled: h.smoothScroll,
+ hardwareAccelerationEnabled: h.hardwareAcceleration,
locale: h.locale,
formattingLocale: h.formattingLocale
};
@@ -264,6 +268,8 @@ function resetState() {
h.isPrimaryInstance = true;
h.allWindows = [];
h.smoothScroll = "true";
+ h.hardwareAcceleration = "true";
+ h.disableHardwareAcceleration.mockClear();
h.dbUninitialized = false;
h.isDbInitialized = true;
h.securitySettings = {};
@@ -406,6 +412,28 @@ describe("main() bootstrap", () => {
expect(switches).not.toContain("disable-smooth-scrolling");
});
+ // #10572: hardware acceleration is disabled before `ready` from the shared provider
+ // (same reason as smooth-scroll — core options aren't wired up yet).
+ it("disables hardware acceleration when the option is off", async () => {
+ setPlatform("darwin");
+ h.hardwareAcceleration = "false";
+ const { main } = await importMain();
+ await main();
+ expect(h.disableHardwareAcceleration).toHaveBeenCalled();
+ });
+
+ it("keeps hardware acceleration when the option is on, absent, or the DB has no schema yet", async () => {
+ for (const scenario of [{ value: "true" as string | null }, { value: null }, { value: "false" as string | null, uninitialized: true }]) {
+ resetState();
+ setPlatform("darwin");
+ h.hardwareAcceleration = scenario.value;
+ if (scenario.uninitialized) h.dbUninitialized = true;
+ const { main } = await importMain();
+ await main();
+ expect(h.disableHardwareAcceleration).not.toHaveBeenCalled();
+ }
+ });
+
// Regression for #10559 (sibling of the smooth-scroll bug): the --lang switch is set
// before core init from the same provider, so it must reflect the configured locale
// instead of always falling back to "en".
diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts
index e42ded163e..d39d83bc47 100644
--- a/apps/desktop/src/main.ts
+++ b/apps/desktop/src/main.ts
@@ -191,6 +191,12 @@ export async function main() {
if (readDbOption(dbProvider, "smoothScrollEnabled") === "false") {
app.commandLine.appendSwitch("disable-smooth-scrolling");
}
+ // Lets users work around GPU driver incompatibilities that render a blank window
+ // (see #10572) without having to pass --disable-gpu on the command line. Like the
+ // switch above, this must run before `ready`.
+ if (readDbOption(dbProvider, "hardwareAccelerationEnabled") === "false") {
+ app.disableHardwareAcceleration();
+ }
// The IPC provider just registers an `ipcMain.on` listener; no TCP socket
// or session parser needed, so we can init it here (before startTriliumServer).
diff --git a/packages/commons/src/lib/options_interface.ts b/packages/commons/src/lib/options_interface.ts
index a77893b40c..1829c9ee00 100644
--- a/packages/commons/src/lib/options_interface.ts
+++ b/packages/commons/src/lib/options_interface.ts
@@ -138,6 +138,8 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions([
"motionEnabled",
"shadowsEnabled",
"smoothScrollEnabled",
+ "hardwareAccelerationEnabled",
"backdropEffectsEnabled",
"maxContentWidth",
"centerContent",
diff --git a/packages/trilium-core/src/services/options_init.ts b/packages/trilium-core/src/services/options_init.ts
index 3bf9bbfafe..f446cd0ba1 100644
--- a/packages/trilium-core/src/services/options_init.ts
+++ b/packages/trilium-core/src/services/options_init.ts
@@ -244,6 +244,7 @@ const defaultOptions: DefaultOption[] = [
{ name: "shadowsEnabled", value: "true", isSynced: false },
{ name: "backdropEffectsEnabled", value: "true", isSynced: false },
{ name: "smoothScrollEnabled", value: "true", isSynced: false },
+ { name: "hardwareAccelerationEnabled", value: "true", isSynced: false },
{ name: "newLayout", value: "true", isSynced: true },
// PDF