Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions electron/gpuSwitches.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,8 @@ describe("getGpuSwitches", () => {
});
});

it("returns the X11 EGL workaround on Linux X11", () => {
it("does not force EGL on Linux X11 (default ANGLE backend)", () => {
expect(getGpuSwitches("linux", { XDG_SESSION_TYPE: "x11" })).toEqual({
useGl: "egl",
disableFeatures: ["VaapiVideoDecoder", "VaapiVideoEncoder"],
});
});
Expand Down
9 changes: 7 additions & 2 deletions electron/gpuSwitches.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function shouldForceLinuxEgl(env: NodeJS.ProcessEnv): boolean {

export function getGpuSwitches(
platform: NodeJS.Platform,
env: NodeJS.ProcessEnv = process.env,
_env: NodeJS.ProcessEnv = process.env,
): GpuSwitches {
if (platform === "darwin") {
return {
Expand All @@ -56,8 +56,13 @@ export function getGpuSwitches(
}

if (platform === "linux") {
// Do NOT force --use-gl=egl: native EGL (gl=egl-gles2,angle=none) is not
// an allowed GL implementation on Electron's Linux builds, which only
// ship ANGLE (gl=egl-angle). Requesting it makes the GPU process
// crash-loop on init, which in turn breaks screen capture and forces the
// renderer into software compositing. Let Chromium use its default ANGLE
// backend instead.
return {
useGl: shouldForceLinuxEgl(env) ? "egl" : undefined,
disableFeatures: ["VaapiVideoDecoder", "VaapiVideoEncoder"],
};
}
Expand Down
30 changes: 14 additions & 16 deletions electron/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
import { RECORDINGS_DIR } from "./appPaths";
import { showCursor } from "./cursorHider";
import { registerExtensionIpcHandlers } from "./extensions/extensionIpc";
import { getGpuSwitches } from "./gpuSwitches";
import { getGpuSwitches, shouldForceLinuxEgl } from "./gpuSwitches";
import {
cleanupAllExportStreams,
cleanupNativeVideoExportSessions,
Expand Down Expand Up @@ -1006,28 +1006,26 @@ app.whenReady().then(async () => {
session.defaultSession.setDisplayMediaRequestHandler(async (_request, callback) => {
try {
const sourceId = getSelectedSourceId();
// On Linux/Wayland, calling desktopCapturer.getSources() itself
// invokes the xdg-desktop-portal picker. If we then return one of
// those sources, Chromium triggers a SECOND portal because the
// pre-enumerated source IDs are stale on Wayland. To collapse this
// into a single portal invocation, when the Linux portal sentinel
// is set we skip getSources entirely and hand back a synthetic
// source id; Chromium then opens the portal once to actually
// resolve the capture.
// Default to the sentinel on Linux when no source has been
// pre-selected (e.g. fresh session where the renderer skipped the
// source picker entirely). This avoids calling getSources() which
// would itself trigger an extra portal dialog.
// The synthetic sentinel source id is ONLY correct on Wayland, where
// Chromium resolves getDisplayMedia through xdg-desktop-portal and
// hand-back of a real desktopCapturer id would trigger a duplicate
// portal dialog. On X11 there is no portal path for getDisplayMedia:
// Chromium must receive a REAL desktopCapturer source id, otherwise it
// cannot resolve the capture and throws "Could not start video source".
const isWaylandSession =
process.platform === "linux" && !shouldForceLinuxEgl(process.env);
const isLinuxPortalSentinel =
process.platform === "linux" && (sourceId === "screen:linux-portal" || !sourceId);
isWaylandSession && (sourceId === "screen:linux-portal" || !sourceId);
if (isLinuxPortalSentinel) {
callback({ video: { id: "screen:0:0", name: "Entire screen" } });
return;
}
const sources = await desktopCapturer.getSources({ types: ["screen", "window"] });
const source = sourceId
? (sources.find((s) => s.id === sourceId) ?? sources[0])
: sources[0];
? (sources.find((s) => s.id === sourceId) ??
sources.find((s) => s.id.startsWith("screen:")) ??
sources[0])
: (sources.find((s) => s.id.startsWith("screen:")) ?? sources[0]);
if (source) {
callback({
video: { id: source.id, name: source.name },
Expand Down
10 changes: 7 additions & 3 deletions electron/windows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,13 @@ function setHudOverlayMousePassthrough(ignore: boolean) {
}

if (!isHudOverlayMousePassthroughSupported()) {
if (process.platform !== "linux") {
setHudOverlayFallbackExpanded(!ignore);
}
// Expand the fallback overlay while the HUD is interactive so that
// popovers/menus have room to render. Without this the window stays at
// the compact 860x160 fallback and upward popovers are clipped by the
// window edge (e.g. only the last items visible). This previously
// skipped Linux, where passthrough is unsupported, so the bug showed up
// exactly there.
setHudOverlayFallbackExpanded(!ignore);
hudOverlayWindow.setIgnoreMouseEvents(false);
return;
}
Expand Down