| author | |
| committer | |
| log | 5753207d53362608f281738eda3a9faf4e13b6be |
| tree | 375db027ee193d37692d57f2399dc0bb5916e2a9 |
| parent | 58d253231d83f669a2cdfefa5fe479751d22c582 |
| signature | Signed by PGP key B5690EEEBB952194 |
Disable WaylandWpColorManagerV1 before Electron is ready only on native Wayland Hyprland sessions running a stock 0.48.0-0.50.1 release, which advertise FEATURE_SET_PRIMARIES but reject set_primaries (hyprwm/Hyprland#9444; fixed in 0.51.0 by hyprwm/Hyprland#11499). Any probe failure or explicit feature override keeps the default.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>4 files changed, 299 insertions(+), 1 deletions(-)
apps/desktop/src/app/DesktopEarlyElectronStartup.test.ts+149| ... | @@ -5,6 +5,7 @@ import { assert, describe, it } from "@effect/vitest"; | ... | @@ -5,6 +5,7 @@ import { assert, describe, it } from "@effect/vitest"; |
| 5 | import { | 5 | import { |
| 6 | resolveEarlyLinuxElectronOptions, | 6 | resolveEarlyLinuxElectronOptions, |
| 7 | resolveEarlyLinuxPasswordStorePreference, | 7 | resolveEarlyLinuxPasswordStorePreference, |
| 8 | resolveLinuxDisableFeaturesSwitch, | ||
| 8 | } from "./DesktopEarlyElectronStartup.ts"; | 9 | } from "./DesktopEarlyElectronStartup.ts"; |
| 9 | 10 | ||
| 10 | describe("DesktopEarlyElectronStartup", () => { | 11 | describe("DesktopEarlyElectronStartup", () => { |
| ... | @@ -120,4 +121,152 @@ describe("DesktopEarlyElectronStartup", () => { | ... | @@ -120,4 +121,152 @@ describe("DesktopEarlyElectronStartup", () => { |
| 120 | 121 | ||
| 121 | assert.equal(preference, "gnome-libsecret"); | 122 | assert.equal(preference, "gnome-libsecret"); |
| 122 | }); | 123 | }); |
| 124 | |||
| 125 | describe("resolveLinuxDisableFeaturesSwitch", () => { | ||
| 126 | const hyprland = { XDG_CURRENT_DESKTOP: "Hyprland", XDG_SESSION_TYPE: "wayland" }; | ||
| 127 | const releaseCommits: Record<string, string> = { | ||
| 128 | "0.48.0": "5ee35f914f921e5696030698e74fb5566a804768", | ||
| 129 | "0.48.1": "29e2e59fdbab8ed2cc23a20e3c6043d5decb5cdc", | ||
| 130 | "0.49.0": "9958d297641b5c84dcff93f9039d80a5ad37ab00", | ||
| 131 | "0.50.0": "c4a4c341568944bd4fb9cd503558b2de602c0213", | ||
| 132 | "0.50.1": "4e242d086e20b32951fdc0ebcbfb4d41b5be8dcc", | ||
| 133 | }; | ||
| 134 | // Shape of `hyprctl version -j` from a release tarball, trimmed to the fields we read. | ||
| 135 | const hyprctlVersion = | ||
| 136 | (version: string, overrides: Record<string, unknown> = {}) => | ||
| 137 | () => | ||
| 138 | JSON.stringify({ | ||
| 139 | commit: releaseCommits[version] ?? "0123456789abcdef0123456789abcdef01234567", | ||
| 140 | version, | ||
| 141 | dirty: false, | ||
| 142 | tag: `v${version}`, | ||
| 143 | flags: [], | ||
| 144 | ...overrides, | ||
| 145 | }); | ||
| 146 | const brokenHyprland = { | ||
| 147 | env: hyprland, | ||
| 148 | ozonePlatform: "wayland", | ||
| 149 | enableFeatures: null, | ||
| 150 | disableFeatures: null, | ||
| 151 | readHyprctlVersionJson: hyprctlVersion("0.48.1"), | ||
| 152 | }; | ||
| 153 | |||
| 154 | it.each(["0.48.0", "0.48.1", "0.49.0", "0.50.0", "0.50.1"])( | ||
| 155 | "disables the color manager on Hyprland %s", | ||
| 156 | (version) => { | ||
| 157 | assert.equal( | ||
| 158 | resolveLinuxDisableFeaturesSwitch({ | ||
| 159 | ...brokenHyprland, | ||
| 160 | readHyprctlVersionJson: hyprctlVersion(version), | ||
| 161 | }), | ||
| 162 | "WaylandWpColorManagerV1", | ||
| 163 | ); | ||
| 164 | }, | ||
| 165 | ); | ||
| 166 | |||
| 167 | it.each([ | ||
| 168 | { label: "case-insensitive desktop tokens", env: { XDG_CURRENT_DESKTOP: "foo:HYPRLAND" } }, | ||
| 169 | { | ||
| 170 | label: "XDG_SESSION_DESKTOP when XDG_CURRENT_DESKTOP is unset", | ||
| 171 | env: { XDG_SESSION_DESKTOP: "hyprland" }, | ||
| 172 | }, | ||
| 173 | ])("detects Hyprland via $label", ({ env }) => { | ||
| 174 | assert.equal( | ||
| 175 | resolveLinuxDisableFeaturesSwitch({ ...brokenHyprland, env }), | ||
| 176 | "WaylandWpColorManagerV1", | ||
| 177 | ); | ||
| 178 | }); | ||
| 179 | |||
| 180 | it("keeps existing disabled features", () => { | ||
| 181 | assert.equal( | ||
| 182 | resolveLinuxDisableFeaturesSwitch({ | ||
| 183 | ...brokenHyprland, | ||
| 184 | disableFeatures: "Vulkan,WaylandWpColorManagerV1Extra", | ||
| 185 | }), | ||
| 186 | "Vulkan,WaylandWpColorManagerV1Extra,WaylandWpColorManagerV1", | ||
| 187 | ); | ||
| 188 | }); | ||
| 189 | |||
| 190 | it.each([ | ||
| 191 | { label: "Hyprland 0.47.2", readHyprctlVersionJson: hyprctlVersion("0.47.2") }, | ||
| 192 | { label: "Hyprland 0.51.0", readHyprctlVersionJson: hyprctlVersion("0.51.0") }, | ||
| 193 | { label: "Hyprland 0.56.2", readHyprctlVersionJson: hyprctlVersion("0.56.2") }, | ||
| 194 | { label: "an unrecognized version", readHyprctlVersionJson: hyprctlVersion("1.0.0") }, | ||
| 195 | { | ||
| 196 | label: "a malformed version", | ||
| 197 | readHyprctlVersionJson: hyprctlVersion("0.48.not-a-version"), | ||
| 198 | }, | ||
| 199 | { label: "a two-part version", readHyprctlVersionJson: hyprctlVersion("0.48") }, | ||
| 200 | { label: "a prerelease version", readHyprctlVersionJson: hyprctlVersion("0.49.0-rc1") }, | ||
| 201 | { | ||
| 202 | label: "a development build after an affected release", | ||
| 203 | readHyprctlVersionJson: hyprctlVersion("0.50.1", { tag: "v0.50.1-87-g4e8875b5" }), | ||
| 204 | }, | ||
| 205 | { | ||
| 206 | label: "a later Nix build reporting an affected release tag", | ||
| 207 | readHyprctlVersionJson: hyprctlVersion("0.50.1", { | ||
| 208 | commit: "4e8875b5e9700c81ca4e169dc7b85bb5b3c8cb7a", | ||
| 209 | }), | ||
| 210 | }, | ||
| 211 | { | ||
| 212 | label: "an affected version with another release's commit", | ||
| 213 | readHyprctlVersionJson: hyprctlVersion("0.48.0", { commit: releaseCommits["0.48.1"] }), | ||
| 214 | }, | ||
| 215 | { label: "an unknown patch release", readHyprctlVersionJson: hyprctlVersion("0.50.2") }, | ||
| 216 | { | ||
| 217 | label: "missing commit metadata", | ||
| 218 | readHyprctlVersionJson: () => | ||
| 219 | JSON.stringify({ version: "0.48.1", tag: "v0.48.1", dirty: false }), | ||
| 220 | }, | ||
| 221 | { | ||
| 222 | label: "a build without git metadata", | ||
| 223 | readHyprctlVersionJson: hyprctlVersion("0.48.1", { tag: "" }), | ||
| 224 | }, | ||
| 225 | { | ||
| 226 | label: "a dirty build", | ||
| 227 | readHyprctlVersionJson: hyprctlVersion("0.48.1", { dirty: true }), | ||
| 228 | }, | ||
| 229 | { | ||
| 230 | label: "output without build metadata", | ||
| 231 | readHyprctlVersionJson: () => JSON.stringify({ version: "0.48.1" }), | ||
| 232 | }, | ||
| 233 | { label: "unparseable hyprctl output", readHyprctlVersionJson: () => "ok" }, | ||
| 234 | { | ||
| 235 | label: "a missing hyprctl", | ||
| 236 | readHyprctlVersionJson: () => { | ||
| 237 | throw new Error("spawnSync hyprctl ENOENT"); | ||
| 238 | }, | ||
| 239 | }, | ||
| 240 | ])("preserves color management for $label", (overrides) => { | ||
| 241 | assert.isNull(resolveLinuxDisableFeaturesSwitch({ ...brokenHyprland, ...overrides })); | ||
| 242 | }); | ||
| 243 | |||
| 244 | it.each([ | ||
| 245 | { | ||
| 246 | label: "a stale Hyprland signature in KDE", | ||
| 247 | env: { XDG_CURRENT_DESKTOP: "KDE", HYPRLAND_INSTANCE_SIGNATURE: "abc_123" }, | ||
| 248 | }, | ||
| 249 | { | ||
| 250 | label: "resolved X11 in a Wayland session", | ||
| 251 | ozonePlatform: "x11", | ||
| 252 | env: { ...hyprland, WAYLAND_DISPLAY: "wayland-1" }, | ||
| 253 | }, | ||
| 254 | { label: "a missing platform", ozonePlatform: null }, | ||
| 255 | { label: "an unrelated platform", ozonePlatform: "headless" }, | ||
| 256 | { label: "already disabled features", disableFeatures: "Vulkan, WaylandWpColorManagerV1" }, | ||
| 257 | { label: "an explicitly enabled feature", enableFeatures: "Vulkan,WaylandWpColorManagerV1" }, | ||
| 258 | { | ||
| 259 | label: "a feature enabled with trial parameters", | ||
| 260 | enableFeatures: "*WaylandWpColorManagerV1<Study.Group:mode/strict", | ||
| 261 | }, | ||
| 262 | ])("skips the Hyprland probe for $label", (overrides) => { | ||
| 263 | assert.isNull( | ||
| 264 | resolveLinuxDisableFeaturesSwitch({ | ||
| 265 | ...brokenHyprland, | ||
| 266 | ...overrides, | ||
| 267 | readHyprctlVersionJson: () => assert.fail("hyprctl should not run"), | ||
| 268 | }), | ||
| 269 | ); | ||
| 270 | }); | ||
| 271 | }); | ||
| 123 | }); | 272 | }); |
apps/desktop/src/app/DesktopEarlyElectronStartup.ts+72| ... | @@ -81,6 +81,78 @@ export function resolveEarlyLinuxPasswordStorePreference( | ... | @@ -81,6 +81,78 @@ export function resolveEarlyLinuxPasswordStorePreference( |
| 81 | } | 81 | } |
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | const WAYLAND_COLOR_MANAGER_FEATURE = "WaylandWpColorManagerV1"; | ||
| 85 | |||
| 86 | // Chromium feature entries look like `*Name<Trial.Group:param/value`. | ||
| 87 | const featureListNames = (value: string | null): ReadonlyArray<string> => | ||
| 88 | (value ?? "").split(",").flatMap((entry) => entry.trim().match(/^\*?([^<:]+)/)?.[1] ?? []); | ||
| 89 | |||
| 90 | // A stale HYPRLAND_INSTANCE_SIGNATURE is often inherited into other desktops, so only | ||
| 91 | // trust the session's desktop names. | ||
| 92 | const isHyprlandSession = (env: NodeJS.ProcessEnv): boolean => | ||
| 93 | (trimNonEmpty(env.XDG_CURRENT_DESKTOP) ?? trimNonEmpty(env.XDG_SESSION_DESKTOP) ?? "") | ||
| 94 | .split(":") | ||
| 95 | .some((name) => name.trim().toLowerCase() === "hyprland"); | ||
| 96 | |||
| 97 | const HyprctlVersionJson = fromLenientJson( | ||
| 98 | Schema.Struct({ | ||
| 99 | version: Schema.String, | ||
| 100 | tag: Schema.String, | ||
| 101 | commit: Schema.String, | ||
| 102 | dirty: Schema.Boolean, | ||
| 103 | }), | ||
| 104 | ); | ||
| 105 | const decodeHyprctlVersionJson = Schema.decodeSync(HyprctlVersionJson); | ||
| 106 | |||
| 107 | // Hyprland 0.48.0 added wp-color-manager-v1 (hyprwm/Hyprland#9444) advertising | ||
| 108 | // FEATURE_SET_PRIMARIES while rejecting set_primaries outside debug mode, which Chromium | ||
| 109 | // treats as a fatal protocol error. 0.51.0 accepts them (hyprwm/Hyprland#11499). | ||
| 110 | // Only stock builds of these releases count. Matching the commit as well as the tag | ||
| 111 | // keeps later development builds that still report an old VERSION and tag (Nix | ||
| 112 | // synthesizes `v${VERSION}`) on the default. | ||
| 113 | const AFFECTED_HYPRLAND_RELEASE_COMMITS: ReadonlyMap<string, string> = new Map([ | ||
| 114 | ["0.48.0", "5ee35f914f921e5696030698e74fb5566a804768"], | ||
| 115 | ["0.48.1", "29e2e59fdbab8ed2cc23a20e3c6043d5decb5cdc"], | ||
| 116 | ["0.49.0", "9958d297641b5c84dcff93f9039d80a5ad37ab00"], | ||
| 117 | ["0.50.0", "c4a4c341568944bd4fb9cd503558b2de602c0213"], | ||
| 118 | ["0.50.1", "4e242d086e20b32951fdc0ebcbfb4d41b5be8dcc"], | ||
| 119 | ]); | ||
| 120 | |||
| 121 | const hasBrokenColorManager = (readHyprctlVersionJson: () => string): boolean => { | ||
| 122 | try { | ||
| 123 | const { version, tag, commit, dirty } = decodeHyprctlVersionJson(readHyprctlVersionJson()); | ||
| 124 | return ( | ||
| 125 | AFFECTED_HYPRLAND_RELEASE_COMMITS.get(version) === commit && tag === `v${version}` && !dirty | ||
| 126 | ); | ||
| 127 | } catch { | ||
| 128 | return false; | ||
| 129 | } | ||
| 130 | }; | ||
| 131 | |||
| 132 | /** | ||
| 133 | * Returns the merged `--disable-features` value to apply before Electron is ready, | ||
| 134 | * or null to leave the command line alone. `ozonePlatform` is the resolved | ||
| 135 | * `--ozone-platform` value; Electron fills it in before app code runs. | ||
| 136 | * | ||
| 137 | * Color management stays on unless `hyprctl version -j` identifies a stock Hyprland | ||
| 138 | * release known to kill the native Wayland connection right after the first window opens. | ||
| 139 | */ | ||
| 140 | export function resolveLinuxDisableFeaturesSwitch(input: { | ||
| 141 | readonly env: NodeJS.ProcessEnv; | ||
| 142 | readonly ozonePlatform: string | null; | ||
| 143 | readonly enableFeatures: string | null; | ||
| 144 | readonly disableFeatures: string | null; | ||
| 145 | readonly readHyprctlVersionJson: () => string; | ||
| 146 | }): string | null { | ||
| 147 | if (input.ozonePlatform !== "wayland" || !isHyprlandSession(input.env)) return null; | ||
| 148 | if (featureListNames(input.enableFeatures).includes(WAYLAND_COLOR_MANAGER_FEATURE)) return null; | ||
| 149 | if (featureListNames(input.disableFeatures).includes(WAYLAND_COLOR_MANAGER_FEATURE)) return null; | ||
| 150 | if (!hasBrokenColorManager(input.readHyprctlVersionJson)) return null; | ||
| 151 | return input.disableFeatures === null | ||
| 152 | ? WAYLAND_COLOR_MANAGER_FEATURE | ||
| 153 | : `${input.disableFeatures},${WAYLAND_COLOR_MANAGER_FEATURE}`; | ||
| 154 | } | ||
| 155 | |||
| 84 | export function resolveEarlyLinuxElectronOptions( | 156 | export function resolveEarlyLinuxElectronOptions( |
| 85 | input: EarlyLinuxElectronOptionsInput, | 157 | input: EarlyLinuxElectronOptionsInput, |
| 86 | ): EarlyLinuxElectronOptions { | 158 | ): EarlyLinuxElectronOptions { |
apps/desktop/src/app/DesktopPreReadyPlatform.test.ts+58| ... | @@ -14,6 +14,7 @@ const { | ... | @@ -14,6 +14,7 @@ const { |
| 14 | mkdirSyncMock, | 14 | mkdirSyncMock, |
| 15 | writeFileSyncMock, | 15 | writeFileSyncMock, |
| 16 | copyFileSyncMock, | 16 | copyFileSyncMock, |
| 17 | execFileSyncMock, | ||
| 17 | } = vi.hoisted(() => ({ | 18 | } = vi.hoisted(() => ({ |
| 18 | appendSwitchMock: vi.fn(), | 19 | appendSwitchMock: vi.fn(), |
| 19 | getSwitchValueMock: vi.fn(), | 20 | getSwitchValueMock: vi.fn(), |
| ... | @@ -23,6 +24,7 @@ const { | ... | @@ -23,6 +24,7 @@ const { |
| 23 | mkdirSyncMock: vi.fn(), | 24 | mkdirSyncMock: vi.fn(), |
| 24 | writeFileSyncMock: vi.fn(), | 25 | writeFileSyncMock: vi.fn(), |
| 25 | copyFileSyncMock: vi.fn(), | 26 | copyFileSyncMock: vi.fn(), |
| 27 | execFileSyncMock: vi.fn(), | ||
| 26 | })); | 28 | })); |
| 27 | 29 | ||
| 28 | vi.mock("electron", () => ({ | 30 | vi.mock("electron", () => ({ |
| ... | @@ -49,6 +51,10 @@ vi.mock("node:fs", () => ({ | ... | @@ -49,6 +51,10 @@ vi.mock("node:fs", () => ({ |
| 49 | copyFileSync: copyFileSyncMock, | 51 | copyFileSync: copyFileSyncMock, |
| 50 | })); | 52 | })); |
| 51 | 53 | ||
| 54 | vi.mock("node:child_process", () => ({ | ||
| 55 | execFileSync: execFileSyncMock, | ||
| 56 | })); | ||
| 57 | |||
| 52 | import * as DesktopPreReadyPlatform from "./DesktopPreReadyPlatform.ts"; | 58 | import * as DesktopPreReadyPlatform from "./DesktopPreReadyPlatform.ts"; |
| 53 | 59 | ||
| 54 | describe("DesktopPreReadyPlatform", () => { | 60 | describe("DesktopPreReadyPlatform", () => { |
| ... | @@ -61,6 +67,7 @@ describe("DesktopPreReadyPlatform", () => { | ... | @@ -61,6 +67,7 @@ describe("DesktopPreReadyPlatform", () => { |
| 61 | mkdirSyncMock.mockReset(); | 67 | mkdirSyncMock.mockReset(); |
| 62 | writeFileSyncMock.mockReset(); | 68 | writeFileSyncMock.mockReset(); |
| 63 | copyFileSyncMock.mockReset(); | 69 | copyFileSyncMock.mockReset(); |
| 70 | execFileSyncMock.mockReset(); | ||
| 64 | }); | 71 | }); |
| 65 | 72 | ||
| 66 | it.effect("preserves an explicit Linux password-store switch", () => { | 73 | it.effect("preserves an explicit Linux password-store switch", () => { |
| ... | @@ -81,6 +88,57 @@ describe("DesktopPreReadyPlatform", () => { | ... | @@ -81,6 +88,57 @@ describe("DesktopPreReadyPlatform", () => { |
| 81 | ); | 88 | ); |
| 82 | }); | 89 | }); |
| 83 | 90 | ||
| 91 | it.effect.each([ | ||
| 92 | { | ||
| 93 | label: "merges the color-manager fallback into disabled features on Hyprland 0.48.1", | ||
| 94 | switches: { "ozone-platform": "wayland", "disable-features": "Vulkan" }, | ||
| 95 | hyprlandVersion: "0.48.1", | ||
| 96 | hyprlandCommit: "29e2e59fdbab8ed2cc23a20e3c6043d5decb5cdc", | ||
| 97 | expected: [["disable-features", "Vulkan,WaylandWpColorManagerV1"]], | ||
| 98 | }, | ||
| 99 | { | ||
| 100 | label: "preserves color management on Hyprland 0.51.0", | ||
| 101 | switches: { "ozone-platform": "wayland", "disable-features": "Vulkan" }, | ||
| 102 | hyprlandVersion: "0.51.0", | ||
| 103 | hyprlandCommit: "0123456789abcdef0123456789abcdef01234567", | ||
| 104 | expected: [], | ||
| 105 | }, | ||
| 106 | { | ||
| 107 | label: "preserves color management when Electron resolved X11", | ||
| 108 | switches: { "ozone-platform": "x11", "disable-features": "Vulkan" }, | ||
| 109 | hyprlandVersion: "0.48.1", | ||
| 110 | hyprlandCommit: "29e2e59fdbab8ed2cc23a20e3c6043d5decb5cdc", | ||
| 111 | expected: [], | ||
| 112 | }, | ||
| 113 | ])("$label", ({ switches, hyprlandVersion, hyprlandCommit, expected }) => { | ||
| 114 | vi.stubEnv("XDG_CURRENT_DESKTOP", "Hyprland"); | ||
| 115 | vi.stubEnv("XDG_SESSION_TYPE", "wayland"); | ||
| 116 | const commandLine = new Map<string, string>(Object.entries(switches)); | ||
| 117 | hasSwitchMock.mockImplementation((switchName: string) => commandLine.has(switchName)); | ||
| 118 | getSwitchValueMock.mockImplementation( | ||
| 119 | (switchName: string) => commandLine.get(switchName) ?? "", | ||
| 120 | ); | ||
| 121 | execFileSyncMock.mockReturnValue( | ||
| 122 | JSON.stringify({ | ||
| 123 | commit: hyprlandCommit, | ||
| 124 | version: hyprlandVersion, | ||
| 125 | dirty: false, | ||
| 126 | tag: `v${hyprlandVersion}`, | ||
| 127 | }), | ||
| 128 | ); | ||
| 129 | |||
| 130 | return DesktopPreReadyPlatform.make.pipe( | ||
| 131 | Effect.provideService(HostProcess.Platform, "linux"), | ||
| 132 | Effect.map(() => { | ||
| 133 | const featureSwitches = appendSwitchMock.mock.calls.filter(([name]) => | ||
| 134 | ["disable-features", "enable-features", "ozone-platform"].includes(name), | ||
| 135 | ); | ||
| 136 | assert.deepEqual(featureSwitches, expected); | ||
| 137 | }), | ||
| 138 | Effect.ensuring(Effect.sync(() => vi.unstubAllEnvs())), | ||
| 139 | ); | ||
| 140 | }); | ||
| 141 | |||
| 84 | it.effect.each([ | 142 | it.effect.each([ |
| 85 | { previousEntry: undefined, label: "missing" }, | 143 | { previousEntry: undefined, label: "missing" }, |
| 86 | { previousEntry: 'Exec="/Applications/deleted-previous.AppImage" %U', label: "stale" }, | 144 | { previousEntry: 'Exec="/Applications/deleted-previous.AppImage" %U', label: "stale" }, |
apps/desktop/src/app/DesktopPreReadyPlatform.ts+20-1| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | // @effect-diagnostics nodeBuiltinImport:off - pre-ready Electron setup reads settings and prepares the Linux desktop entry synchronously before app services are available. | 1 | // @effect-diagnostics nodeBuiltinImport:off - pre-ready Electron setup reads settings, prepares the Linux desktop entry, and asks Hyprland for its version synchronously before app services are available. |
| 2 | import * as NodeChildProcess from "node:child_process"; | ||
| 2 | import * as NodeFS from "node:fs"; | 3 | import * as NodeFS from "node:fs"; |
| 3 | import * as NodePath from "node:path"; | 4 | import * as NodePath from "node:path"; |
| 4 | import * as Context from "effect/Context"; | 5 | import * as Context from "effect/Context"; |
| ... | @@ -115,6 +116,24 @@ export const make = Effect.gen(function* () { | ... | @@ -115,6 +116,24 @@ export const make = Effect.gen(function* () { |
| 115 | if (linux.passwordStore !== null && linuxPasswordStoreCommandLine === null) { | 116 | if (linux.passwordStore !== null && linuxPasswordStoreCommandLine === null) { |
| 116 | Electron.app.commandLine.appendSwitch("password-store", linux.passwordStore); | 117 | Electron.app.commandLine.appendSwitch("password-store", linux.passwordStore); |
| 117 | } | 118 | } |
| 119 | // Chromium keeps only the last --disable-features value, so pass the merged list. | ||
| 120 | const disableFeatures = DesktopEarlyElectronStartup.resolveLinuxDisableFeaturesSwitch({ | ||
| 121 | env: process.env, | ||
| 122 | ozonePlatform: readCommandLineSwitchValue(Electron.app.commandLine, "ozone-platform"), | ||
| 123 | enableFeatures: readCommandLineSwitchValue(Electron.app.commandLine, "enable-features"), | ||
| 124 | disableFeatures: readCommandLineSwitchValue(Electron.app.commandLine, "disable-features"), | ||
| 125 | // Runs only on Hyprland Wayland sessions without an explicit feature override. | ||
| 126 | readHyprctlVersionJson: () => | ||
| 127 | NodeChildProcess.execFileSync("hyprctl", ["version", "-j"], { | ||
| 128 | encoding: "utf8", | ||
| 129 | stdio: ["ignore", "pipe", "ignore"], | ||
| 130 | timeout: 1000, | ||
| 131 | maxBuffer: 64 * 1024, | ||
| 132 | }), | ||
| 133 | }); | ||
| 134 | if (disableFeatures !== null) { | ||
| 135 | Electron.app.commandLine.appendSwitch("disable-features", disableFeatures); | ||
| 136 | } | ||
| 118 | } | 137 | } |
| 119 | 138 | ||
| 120 | return { linux, linuxPasswordStoreCommandLine }; | 139 | return { linux, linuxPasswordStoreCommandLine }; |