diff --git a/package.json b/package.json index 917dd41..11dedea 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "arrpc": "github:OpenAsar/arrpc#98879cae0565e6fce34e4cb6f544bf42c6a7e7c8" }, "optionalDependencies": { - "@vencord/venmic": "^3.4.1" + "@vencord/venmic": "^3.4.2" }, "devDependencies": { "@fal-works/esbuild-plugin-global-externals": "^2.1.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7099617..14f9bf6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,8 +11,8 @@ dependencies: optionalDependencies: '@vencord/venmic': - specifier: ^3.4.1 - version: 3.4.1 + specifier: ^3.4.2 + version: 3.4.2 devDependencies: '@fal-works/esbuild-plugin-global-externals': @@ -1000,8 +1000,8 @@ packages: type-fest: 3.13.1 dev: true - /@vencord/venmic@3.4.1: - resolution: {integrity: sha512-PkMXx53nxiYBLWxiMRaBjBm8aTTJTcueKsMZ0v35TtIQ93yfuSzfCilDFLs3kYz1uQHArTaI9IGQykgmSfe/2w==} + /@vencord/venmic@3.4.2: + resolution: {integrity: sha512-nwGjarof1wVvecGksGONfb+PduhY4gSuHTm39LktiQayHS69+yv4lepq4k1lxZzjOgFTy5VXfmJqxt+BpqoUUQ==} engines: {node: '>=14.15'} os: [linux] requiresBuild: true diff --git a/src/main/venmic.ts b/src/main/venmic.ts index b86e7f6..404c0d2 100644 --- a/src/main/venmic.ts +++ b/src/main/venmic.ts @@ -4,17 +4,58 @@ * Copyright (c) 2023 Vendicated and Vencord contributors */ -import type { PatchBay } from "@vencord/venmic"; +import type { PatchBay as PatchBayType } from "@vencord/venmic"; import { app, ipcMain } from "electron"; import { join } from "path"; import { IpcEvents } from "shared/IpcEvents"; import { STATIC_DIR } from "shared/paths"; -type LinkData = Parameters[0]; +type LinkData = Parameters[0]; +let PatchBay: typeof PatchBayType | undefined; +let patchBayInstance: PatchBayType | undefined; + +let imported = false; let initialized = false; -let patchBay: import("@vencord/venmic").PatchBay | undefined; -let isGlibcxxToOld = false; + +let hasPipewirePulse = false; +let isGlibCxxOutdated = false; + +function importVenmic() { + if (imported) { + return; + } + + imported = true; + + try { + PatchBay = (require(join(STATIC_DIR, `dist/venmic-${process.arch}.node`)) as typeof import("@vencord/venmic")) + .PatchBay; + + hasPipewirePulse = PatchBay.hasPipeWire(); + } catch (e: any) { + console.error("Failed to import venmic", e); + isGlibCxxOutdated = (e?.stack || e?.message || "").toLowerCase().includes("glibc"); + } +} + +function obtainVenmic() { + if (!imported) { + importVenmic(); + } + + if (PatchBay && !initialized) { + initialized = true; + + try { + patchBayInstance = new PatchBay(); + } catch (e: any) { + console.error("Failed to instantiate venmic", e); + } + } + + return patchBayInstance; +} function getRendererAudioServicePid() { return ( @@ -25,33 +66,17 @@ function getRendererAudioServicePid() { ); } -function obtainVenmic() { - if (!initialized) { - initialized = true; - try { - const { PatchBay } = require( - join(STATIC_DIR, `dist/venmic-${process.arch}.node`) - ) as typeof import("@vencord/venmic"); - patchBay = new PatchBay(); - } catch (e: any) { - console.error("Failed to initialise venmic. Make sure you're using pipewire", e); - isGlibcxxToOld = (e?.stack || e?.message || "").toLowerCase().includes("glibc"); - } - } - - return patchBay; -} - ipcMain.handle(IpcEvents.VIRT_MIC_LIST, () => { const audioPid = getRendererAudioServicePid(); + const list = obtainVenmic() ?.list() .filter(s => s["application.process.id"] !== audioPid) .map(s => s["application.name"]); - return list - ? { ok: true, targets: [...new Set(list)] } // Remove duplicates - : { ok: false, isGlibcxxToOld }; + const uniqueTargets = [...new Set(list)]; + + return list ? { ok: true, targets: uniqueTargets, hasPipewirePulse } : { ok: false, isGlibCxxOutdated }; }); ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, targets: string[], workaround?: boolean) => { diff --git a/src/preload/VesktopNative.ts b/src/preload/VesktopNative.ts index 4fc068a..184b095 100644 --- a/src/preload/VesktopNative.ts +++ b/src/preload/VesktopNative.ts @@ -62,7 +62,9 @@ export const VesktopNative = { /** only available on Linux. */ virtmic: { list: () => - invoke<{ ok: false; isGlibcxxToOld: boolean } | { ok: true; targets: string[] }>(IpcEvents.VIRT_MIC_LIST), + invoke< + { ok: false; isGlibCxxOutdated: boolean } | { ok: true; targets: string[]; hasPipewirePulse: boolean } + >(IpcEvents.VIRT_MIC_LIST), start: (targets: string[], workaround?: boolean) => invoke(IpcEvents.VIRT_MIC_START, targets, workaround), startSystem: (workaround?: boolean, onlyDefaultSpeakers?: boolean) => invoke(IpcEvents.VIRT_MIC_START_SYSTEM, workaround, onlyDefaultSpeakers), diff --git a/src/renderer/components/ScreenSharePicker.tsx b/src/renderer/components/ScreenSharePicker.tsx index cc0964c..1c53b63 100644 --- a/src/renderer/components/ScreenSharePicker.tsx +++ b/src/renderer/components/ScreenSharePicker.tsx @@ -331,9 +331,13 @@ function AudioSourcePickerLinux({ setOnlyDefaultSpeakers(b: boolean): void; }) { const [sources, _, loading] = useAwaiter(() => VesktopNative.virtmic.list(), { - fallbackValue: { ok: true, targets: [] } + fallbackValue: { ok: true, targets: [], hasPipewirePulse: true } }); + const allSources = sources.ok ? ["None", "Entire System", ...sources.targets] : null; + const hasPipewirePulse = sources.ok ? sources.hasPipewirePulse : true; + + const [ignorePulseWarning, setIgnorePulseWarning] = useState(false); return ( <> @@ -345,32 +349,38 @@ function AudioSourcePickerLinux({ Audio Source )} - {!sources.ok && - (sources.isGlibcxxToOld ? ( - - Failed to retrieve Audio Sources because your C++ library is too old to run venmic. If you - would like to stream with Audio, see{" "} - - this guide - - - ) : ( - - Failed to retrieve Audio Sources. If you would like to stream with Audio, make sure you're - using Pipewire, not Pulseaudio - - ))} + {!sources.ok && sources.isGlibCxxOutdated && ( + + Failed to retrieve Audio Sources because your C++ library is too old to run + + venmic + + . See{" "} + + this guide + {" "} + for possible solutions. + + )} - {allSources && ( - ({ label: s, value: s, default: s === "None" }))} + isSelected={s => s === audioSource} + select={setAudioSource} + serialize={String} + /> + ) + ) : ( + + Could not find pipewire-pulse. This usually means that you do not run pipewire as your main + audio-server.
+ You can still continue, however, please beware that you can only share audio of apps that are + running under pipewire. +
+ setIgnorePulseWarning(true)}>I know what I'm doing +
)} diff --git a/src/renderer/patches/hideSwitchDevice.tsx b/src/renderer/patches/hideSwitchDevice.tsx new file mode 100644 index 0000000..911aed7 --- /dev/null +++ b/src/renderer/patches/hideSwitchDevice.tsx @@ -0,0 +1,24 @@ +/* + * SPDX-License-Identifier: GPL-3.0 + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2023 Vendicated and Vencord contributors + */ + +import { addPatch } from "./shared"; + +addPatch({ + patches: [ + { + find: "lastOutputSystemDevice.justChanged", + replacement: { + // eslint-disable-next-line no-useless-escape + match: /(\i)\.default\.getState\(\).neverShowModal/, + replace: "$& || $self.shouldIgnore($1)" + } + } + ], + + shouldIgnore(state: any) { + return Object.keys(state?.default?.lastDeviceConnected ?? {})?.[0] === "vencord-screen-share"; + } +}); diff --git a/src/renderer/patches/index.ts b/src/renderer/patches/index.ts index 1e74e6f..c243b14 100644 --- a/src/renderer/patches/index.ts +++ b/src/renderer/patches/index.ts @@ -7,6 +7,7 @@ // TODO: Possibly auto generate glob if we have more patches in the future import "./enableNotificationsByDefault"; import "./platformClass"; +import "./hideSwitchDevice"; import "./screenShareFixes"; import "./spellCheck"; import "./windowsTitleBar"; diff --git a/src/renderer/patches/screenShareFixes.ts b/src/renderer/patches/screenShareFixes.ts index 66e4b14..61973a0 100644 --- a/src/renderer/patches/screenShareFixes.ts +++ b/src/renderer/patches/screenShareFixes.ts @@ -11,11 +11,12 @@ import { isLinux } from "renderer/utils"; const logger = new Logger("VesktopStreamFixes"); if (isLinux) { - const original = navigator.mediaDevices.getDisplayMedia; + const originalMedia = navigator.mediaDevices.getDisplayMedia; + const originalDevices = navigator.mediaDevices.enumerateDevices; async function getVirtmic() { try { - const devices = await navigator.mediaDevices.enumerateDevices(); + const devices = await originalDevices(); const audioDevice = devices.find(({ label }) => label === "vencord-screen-share"); return audioDevice?.deviceId; } catch (error) { @@ -23,8 +24,13 @@ if (isLinux) { } } + navigator.mediaDevices.enumerateDevices = async function () { + const result = await originalDevices.call(this); + return result.filter(x => x.label !== "vencord-screen-share"); + }; + navigator.mediaDevices.getDisplayMedia = async function (opts) { - const stream = await original.call(this, opts); + const stream = await originalMedia.call(this, opts); const id = await getVirtmic(); const frameRate = Number(currentSettings?.fps);