mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
fix(voice): stop screen shares failing to reach their viewers (#2835)
This commit is contained in:
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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,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 => {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user