fix(voice): stop screen shares failing to reach their viewers (#2835)

This commit is contained in:
Hampus
2026-09-19 02:17:25 +02:00
committed by GitHub
parent f32356801d
commit bc47a724af
119 changed files with 27942 additions and 12524 deletions
+2 -1
View File
@@ -11,7 +11,8 @@
"scripts": {
"build": "node scripts/build.mjs",
"set-channel": "../tools/ci/run.sh build-desktop --step set_build_channel",
"test": "cargo test --manifest-path native/rust/Cargo.toml",
"test": "pnpm test:main && cargo test --manifest-path native/rust/Cargo.toml",
"test:main": "node --test 'src/main/*.test.mjs'",
"typecheck": "pnpm set-channel && tsc --noEmit"
},
"dependencies": {
@@ -0,0 +1,141 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import assert from 'node:assert/strict';
import {readFileSync} from 'node:fs';
import {createRequire} from 'node:module';
import {describe, test} from 'node:test';
import {fileURLToPath} from 'node:url';
import vm from 'node:vm';
const require = createRequire(import.meta.url);
const esbuild = require('esbuild');
const sourcePath = fileURLToPath(new URL('./DesktopDebugInfo.ts', import.meta.url));
const source = readFileSync(sourcePath, 'utf8');
const transformedSource = esbuild.transformSync(source, {
loader: 'ts',
format: 'cjs',
platform: 'node',
target: 'node20',
}).code;
function loadDesktopDebugInfo(options = {}) {
function requireStub(specifier) {
if (specifier === 'electron') {
return {
app: {commandLine: {hasSwitch: () => false}, getPath: () => '', getLocale: () => 'en-US', isReady: () => true},
};
}
if (specifier === 'electron-log') {
return {info() {}, transports: {file: {getFile: () => null}}};
}
if (specifier === '@electron/common/DesktopConfig') {
return {
getAppUrl: () => '',
getCustomAppUrl: () => null,
getDesktopTroubleshootingSettings: () => options.troubleshooting ?? {disableHardwareAcceleration: false},
getDesktopWindowBehaviorSettings: () => ({}),
};
}
if (specifier === '@electron/common/UserDataPath') {
return {isPortableMode: () => false};
}
if (specifier === '@electron/main/ChromiumRuntime') {
return {hasEnabledBlinkFeature: () => false, MIDDLE_CLICK_AUTOSCROLL_BLINK_FEATURE: 'feature'};
}
if (specifier === '@electron/main/PlatformInfo') {
return {getDesktopInfo: async () => ({})};
}
return require(specifier);
}
const module = {exports: {}};
const context = vm.createContext({
require: requireStub,
module,
exports: module.exports,
process: {
...process,
env: {},
argv: options.argv ?? [],
platform: options.platform ?? process.platform,
},
console,
});
vm.runInContext(transformedSource, context, {filename: sourcePath});
return module.exports;
}
describe('DesktopDebugInfo effective troubleshooting settings', () => {
test('reports hardware acceleration disabled when a launch flag disabled it', () => {
const {resolveEffectiveDesktopTroubleshootingSettings} = loadDesktopDebugInfo();
for (const flag of ['--fluxer-disable-gpu', '--fluxer-disable-hardware-acceleration', '--fluxer-safe-mode']) {
assert.equal(
resolveEffectiveDesktopTroubleshootingSettings({disableHardwareAcceleration: false}, [flag], 'win32')
.disableHardwareAcceleration,
true,
flag,
);
assert.equal(
resolveEffectiveDesktopTroubleshootingSettings({disableHardwareAcceleration: false}, [flag], 'linux')
.disableHardwareAcceleration,
true,
flag,
);
}
});
test('honours the persisted setting when no launch flag disabled it', () => {
const {resolveEffectiveDesktopTroubleshootingSettings} = loadDesktopDebugInfo();
assert.equal(
resolveEffectiveDesktopTroubleshootingSettings({disableHardwareAcceleration: true}, [], 'win32')
.disableHardwareAcceleration,
true,
);
assert.equal(
resolveEffectiveDesktopTroubleshootingSettings(
{disableHardwareAcceleration: false},
['--fluxer-devtools'],
'linux',
).disableHardwareAcceleration,
false,
);
});
test('reports hardware acceleration enabled on macOS', () => {
const {resolveEffectiveDesktopTroubleshootingSettings} = loadDesktopDebugInfo();
assert.equal(
resolveEffectiveDesktopTroubleshootingSettings(
{disableHardwareAcceleration: true},
['--fluxer-safe-mode'],
'darwin',
).disableHardwareAcceleration,
false,
);
});
test('keeps the launch answer when the persisted setting changes without a restart', () => {
const troubleshooting = {disableHardwareAcceleration: true};
const {getLaunchDesktopTroubleshootingSettings} = loadDesktopDebugInfo({
troubleshooting,
argv: [],
platform: 'win32',
});
assert.equal(getLaunchDesktopTroubleshootingSettings().disableHardwareAcceleration, true);
troubleshooting.disableHardwareAcceleration = false;
assert.equal(getLaunchDesktopTroubleshootingSettings().disableHardwareAcceleration, true);
});
test('answers the launch flag when nothing is persisted', () => {
const {getLaunchDesktopTroubleshootingSettings} = loadDesktopDebugInfo({
argv: ['--fluxer-disable-hardware-acceleration'],
platform: 'linux',
});
assert.equal(getLaunchDesktopTroubleshootingSettings().disableHardwareAcceleration, true);
});
});
+25 -1
View File
@@ -69,6 +69,30 @@ export function shouldDisableHardwareAccelerationForLaunch(argv: ReadonlyArray<s
return hasFlag(argv, DISABLE_HARDWARE_ACCELERATION_ARGS) || isSafeModeLaunch(argv);
}
export function resolveEffectiveDesktopTroubleshootingSettings(
settings: DesktopTroubleshootingSettings,
argv: ReadonlyArray<string>,
platform: NodeJS.Platform,
): DesktopTroubleshootingSettings {
return {
...settings,
disableHardwareAcceleration:
platform !== 'darwin' &&
(settings.disableHardwareAcceleration || shouldDisableHardwareAccelerationForLaunch(argv)),
};
}
let launchTroubleshootingSettings: DesktopTroubleshootingSettings | null = null;
export function getLaunchDesktopTroubleshootingSettings(): DesktopTroubleshootingSettings {
launchTroubleshootingSettings ??= resolveEffectiveDesktopTroubleshootingSettings(
getDesktopTroubleshootingSettings(),
process.argv,
process.platform,
);
return launchTroubleshootingSettings;
}
export function shouldOpenDevToolsOnLaunch(argv: ReadonlyArray<string>): boolean {
return hasFlag(argv, OPEN_DEVTOOLS_ARGS);
}
@@ -297,7 +321,7 @@ export async function getDesktopDebugInfo(
logFilePath: getLogFilePath(),
configPath: path.join(userDataPath, 'settings.json'),
windowBehavior: getDesktopWindowBehaviorDebugSettings(),
troubleshooting: getDesktopTroubleshootingSettings(),
troubleshooting: getLaunchDesktopTroubleshootingSettings(),
packaged: app.isPackaged,
portable: isPortableMode(),
pid: process.pid,
+3 -9
View File
@@ -3,7 +3,6 @@
import {
type DesktopTroubleshootingSettings,
type DesktopWindowBehaviorSettings,
getDesktopTroubleshootingSettings,
getDesktopWindowBehaviorSettings,
setDesktopWindowBehaviorSettings,
} from '@electron/common/DesktopConfig';
@@ -14,6 +13,7 @@ import type {
TrayPresenceStatus,
} from '@electron/common/Types';
import {hasEnabledBlinkFeature, MIDDLE_CLICK_AUTOSCROLL_BLINK_FEATURE} from '@electron/main/ChromiumRuntime';
import {getLaunchDesktopTroubleshootingSettings} from '@electron/main/DesktopDebugInfo';
import {
applyDesktopWindowBehaviorSettings,
desktopTrayChangePendingRestart,
@@ -156,10 +156,7 @@ export function registerIpcHandlers(): void {
};
});
ipcMain.handle('desktop-troubleshooting-get', (): DesktopTroubleshootingSettings => {
if (process.platform === 'darwin') {
return {...getDesktopTroubleshootingSettings(), disableHardwareAcceleration: false};
}
return getDesktopTroubleshootingSettings();
return getLaunchDesktopTroubleshootingSettings();
});
ipcMain.handle(
'desktop-troubleshooting-set-disable-hardware-acceleration',
@@ -176,10 +173,7 @@ export function registerIpcHandlers(): void {
} else {
setHardwareAccelerationDisabled(disable);
}
if (process.platform === 'darwin') {
return {...getDesktopTroubleshootingSettings(), disableHardwareAcceleration: false};
}
return getDesktopTroubleshootingSettings();
return getLaunchDesktopTroubleshootingSettings();
},
);
ipcMain.handle('desktop-troubleshooting-reload', (): void => {
+71 -1
View File
@@ -19,9 +19,13 @@ const transformedSource = esbuild.transformSync(source, {
target: 'node20',
}).code;
function loadPlatformInfo(platform = 'win32') {
function loadPlatformInfo(platform = 'win32', {nativeGpu = null, electronGpu = null, gpuInfoRequests = []} = {}) {
const app = {
getVersion: () => '0.0.0-test',
getGPUInfo: async (infoType) => {
gpuInfoRequests.push(infoType);
return {gpuDevice: electronGpu ?? []};
},
commandLine: {
getSwitchValue: () => '',
hasSwitch: () => true,
@@ -36,6 +40,9 @@ function loadPlatformInfo(platform = 'win32') {
if (specifier === 'node:module') {
return {
createRequire: () => (moduleSpecifier) => {
if (moduleSpecifier === '@fluxer/platform-info' && nativeGpu) {
return {getGpuInfo: () => ({devices: nativeGpu, source: 'dxgi'}), loadError: null};
}
throw new Error(`Unexpected native module require: ${moduleSpecifier}`);
},
};
@@ -83,3 +90,66 @@ describe('PlatformInfo Chromium runtime diagnostics', () => {
assert.equal(switches.includes('enable-libopenh264'), true);
});
});
describe('PlatformInfo dual-GPU merge', () => {
const NVIDIA = {vendorId: 0x10de, deviceId: 0x2684, deviceString: 'NVIDIA GeForce RTX 4090 Laptop GPU'};
const INTEL = {vendorId: 0x8086, deviceId: 0x9bc4, deviceString: 'Intel(R) UHD Graphics'};
test('keeps the adapter Chromium reports active and drops the native probe active flag', async () => {
const gpuInfoRequests = [];
const module = loadPlatformInfo('win32', {
gpuInfoRequests,
nativeGpu: [
{...NVIDIA, active: true, dedicatedVideoMemory: 16 * 1024 * 1024 * 1024, source: 'dxgi'},
{...INTEL, active: false, dedicatedVideoMemory: 0, source: 'dxgi'},
],
electronGpu: [
{...INTEL, active: true},
{...NVIDIA, active: false},
],
});
const info = await module.getGpuInfo();
const byVendor = new Map(info.devices.map((device) => [device.vendorId, device]));
assert.deepEqual(gpuInfoRequests, ['complete']);
assert.equal(byVendor.get(INTEL.vendorId).active, true);
assert.equal(byVendor.get(NVIDIA.vendorId).active, false);
assert.equal(byVendor.get(NVIDIA.vendorId).dedicatedVideoMemory, 16 * 1024 * 1024 * 1024);
});
test('leaves a native adapter Chromium never lists inactive', async () => {
const module = loadPlatformInfo('win32', {
nativeGpu: [
{...NVIDIA, active: true, dedicatedVideoMemory: 16 * 1024 * 1024 * 1024, source: 'dxgi'},
{...INTEL, active: false, dedicatedVideoMemory: 0, source: 'dxgi'},
],
electronGpu: [{...INTEL, active: true}],
});
const info = await module.getGpuInfo();
const byVendor = new Map(info.devices.map((device) => [device.vendorId, device]));
assert.equal(byVendor.get(NVIDIA.vendorId).active, false);
assert.equal(byVendor.get(INTEL.vendorId).active, true);
});
test('keeps the native details when Electron reports the dedicated adapter active', async () => {
const module = loadPlatformInfo('win32', {
nativeGpu: [
{...NVIDIA, active: true, dedicatedVideoMemory: 16 * 1024 * 1024 * 1024, source: 'dxgi'},
{...INTEL, active: false, dedicatedVideoMemory: 0, source: 'dxgi'},
],
electronGpu: [
{...NVIDIA, active: true},
{...INTEL, active: false},
],
});
const info = await module.getGpuInfo();
const byVendor = new Map(info.devices.map((device) => [device.vendorId, device]));
assert.equal(byVendor.get(NVIDIA.vendorId).active, true);
assert.equal(byVendor.get(INTEL.vendorId).active, false);
});
});
+6 -6
View File
@@ -212,11 +212,11 @@ function sameDevice(a: GpuDeviceInfo, b: GpuDeviceInfo): boolean {
}
function mergeGpuDevice(nativeDevice: GpuDeviceInfo, electronDevice: GpuDeviceInfo | undefined): GpuDeviceInfo {
if (!electronDevice) return nativeDevice;
if (!electronDevice) return {...nativeDevice, active: false};
return {
...electronDevice,
...nativeDevice,
active: nativeDevice.active || electronDevice.active,
active: electronDevice.active,
vendorName: nativeDevice.vendorName ?? electronDevice.vendorName,
deviceString: nativeDevice.deviceString ?? electronDevice.deviceString,
driverVendor: nativeDevice.driverVendor ?? electronDevice.driverVendor,
@@ -234,9 +234,9 @@ function mergeGpuDevices(
const electronIndex = electronDevices.findIndex(
(candidate, index) => !usedElectronIndexes.has(index) && sameDevice(nativeDevice, candidate),
);
if (electronIndex === -1) return nativeDevice;
usedElectronIndexes.add(electronIndex);
return mergeGpuDevice(nativeDevice, electronDevices[electronIndex]);
const electronDevice = electronIndex === -1 ? undefined : electronDevices[electronIndex];
if (electronDevice) usedElectronIndexes.add(electronIndex);
return mergeGpuDevice(nativeDevice, electronDevice);
});
for (const [index, electronDevice] of electronDevices.entries()) {
if (!usedElectronIndexes.has(index)) merged.push(electronDevice);
@@ -283,7 +283,7 @@ export async function getGpuInfo(): Promise<GpuInfo> {
nativeSource: native?.source,
};
} catch {
return {devices: native?.devices ?? [], nativeSource: native?.source};
return {devices: mergeGpuDevices(native?.devices ?? [], []), nativeSource: native?.source};
}
}
+3 -7
View File
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {getDesktopTroubleshootingSettings, setDesktopTroubleshootingSettings} from '@electron/common/DesktopConfig';
import {setDesktopTroubleshootingSettings} from '@electron/common/DesktopConfig';
import {createChildLogger} from '@electron/common/Logger';
import {getLaunchDesktopTroubleshootingSettings} from '@electron/main/DesktopDebugInfo';
import {destroyDesktopTray} from '@electron/main/DesktopTray';
import {getStableRelaunchOptions} from '@electron/main/LinuxLaunchPath';
import {t} from '@electron/main/MainI18n';
@@ -24,11 +25,6 @@ export function relaunchAndExit(): void {
app.exit(0);
}
function isHardwareAccelerationDisabled(): boolean {
if (process.platform === 'darwin') return false;
return getDesktopTroubleshootingSettings().disableHardwareAcceleration;
}
export function setHardwareAccelerationDisabled(disable: boolean): void {
if (process.platform === 'darwin') {
setDesktopTroubleshootingSettings({disableHardwareAcceleration: false});
@@ -111,7 +107,7 @@ export async function resetAppDataAndRestart(options?: {confirm?: boolean}): Pro
}
export function buildTroubleshootingMenuItems(): Array<MenuItemConstructorOptions> {
const hwAccelDisabled = isHardwareAccelerationDisabled();
const hwAccelDisabled = getLaunchDesktopTroubleshootingSettings().disableHardwareAcceleration;
const toggleHwAccelLabel = hwAccelDisabled
? t('desktop.troubleshooting.enableHardwareAccelerationAndRestart')
: t('desktop.troubleshooting.disableHardwareAccelerationAndRestart');
+4 -7
View File
@@ -43,6 +43,7 @@ import {
formatDesktopDebugInfo,
getDesktopDebugInfo,
getLaunchAppUrlOverride,
getLaunchDesktopTroubleshootingSettings,
getLaunchNetLogPath,
hasDesktopDebugInfoArg,
logDesktopDebugInfo,
@@ -173,17 +174,13 @@ if (launchConfigurationError) {
if (shouldResetWindowStateOnLaunch(process.argv)) {
clearSavedWindowBounds();
}
const disableHardwareAccelerationRequested =
shouldDisableHardwareAccelerationForLaunch(process.argv) ||
getDesktopTroubleshootingSettings().disableHardwareAcceleration;
if (process.platform !== 'darwin' && disableHardwareAccelerationRequested) {
const disableHardwareAcceleration = getLaunchDesktopTroubleshootingSettings().disableHardwareAcceleration;
if (disableHardwareAcceleration) {
app.disableHardwareAcceleration();
log.info('Hardware acceleration disabled for this launch', {
commandLine: shouldDisableHardwareAccelerationForLaunch(process.argv),
persistentSetting: getDesktopTroubleshootingSettings().disableHardwareAcceleration,
});
} else if (process.platform === 'darwin' && disableHardwareAccelerationRequested) {
log.info('Hardware acceleration disable request ignored on macOS');
}
log.info('Launch diagnostic modes', launchDiagnosticOptions);
const CHANNEL_APP_NAME = DESKTOP_APP_NAME;
@@ -264,7 +261,7 @@ if (launchConfigurationError) {
}
const disabledChromiumFeatures = new Set(BASE_DISABLED_CHROMIUM_FEATURES);
const enabledChromiumFeatures = new Set<string>();
if (!disableHardwareAccelerationRequested) {
if (!disableHardwareAcceleration) {
addLinuxHardwareVideoEncodeFeatures(enabledChromiumFeatures);
addWindowsHardwareVideoEncodeFeatures(enabledChromiumFeatures);
}