diff --git a/package.json b/package.json index 1130fb2..e89f73b 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,10 @@ "updateMeta": "tsx scripts/utils/updateMeta.mts" }, "dependencies": { - "arrpc": "github:OpenAsar/arrpc#98879cae0565e6fce34e4cb6f544bf42c6a7e7c8" + "arrpc": "github:OpenAsar/arrpc#6960a8fd4d65d566da93dbdb8a7ca474aa0a3c9c" }, "optionalDependencies": { - "@vencord/venmic": "^3.3.2" + "@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 4c33237..feab853 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,13 +6,13 @@ settings: dependencies: arrpc: - specifier: github:OpenAsar/arrpc#98879cae0565e6fce34e4cb6f544bf42c6a7e7c8 - version: github.com/OpenAsar/arrpc/98879cae0565e6fce34e4cb6f544bf42c6a7e7c8 + specifier: github:OpenAsar/arrpc#6960a8fd4d65d566da93dbdb8a7ca474aa0a3c9c + version: github.com/OpenAsar/arrpc/6960a8fd4d65d566da93dbdb8a7ca474aa0a3c9c optionalDependencies: '@vencord/venmic': - specifier: ^3.3.2 - version: 3.3.2 + specifier: ^3.4.2 + version: 3.4.2 devDependencies: '@fal-works/esbuild-plugin-global-externals': @@ -1000,14 +1000,14 @@ packages: type-fest: 3.13.1 dev: true - /@vencord/venmic@3.3.2: - resolution: {integrity: sha512-fwGr5v+Xe4EisKxxhTDlUDamBGlBzSK3+yYZH/7d9ui1j8Q78wCbE1iP8MnDjBvV2kIKn/xV/84wjt9iQvDkFw==} + /@vencord/venmic@3.4.2: + resolution: {integrity: sha512-nwGjarof1wVvecGksGONfb+PduhY4gSuHTm39LktiQayHS69+yv4lepq4k1lxZzjOgFTy5VXfmJqxt+BpqoUUQ==} engines: {node: '>=14.15'} os: [linux] requiresBuild: true dependencies: cmake-js: 7.3.0 - node-addon-api: 7.1.0 + node-addon-api: 8.0.0 pkg-prebuilds: 0.2.1 transitivePeerDependencies: - supports-color @@ -1333,11 +1333,11 @@ packages: possible-typed-array-names: 1.0.0 dev: true - /axios@1.6.7(debug@4.3.4): - resolution: {integrity: sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==} + /axios@1.6.8(debug@4.3.4): + resolution: {integrity: sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==} requiresBuild: true dependencies: - follow-redirects: 1.15.5(debug@4.3.4) + follow-redirects: 1.15.6(debug@4.3.4) form-data: 4.0.0 proxy-from-env: 1.1.0 transitivePeerDependencies: @@ -1576,7 +1576,7 @@ packages: hasBin: true requiresBuild: true dependencies: - axios: 1.6.7(debug@4.3.4) + axios: 1.6.8(debug@4.3.4) debug: 4.3.4 fs-extra: 11.2.0 lodash.isplainobject: 4.0.6 @@ -2540,8 +2540,8 @@ packages: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} dev: true - /follow-redirects@1.15.5(debug@4.3.4): - resolution: {integrity: sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==} + /follow-redirects@1.15.6(debug@4.3.4): + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} engines: {node: '>=4.0'} requiresBuild: true peerDependencies: @@ -3581,9 +3581,9 @@ packages: dev: true optional: true - /node-addon-api@7.1.0: - resolution: {integrity: sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==} - engines: {node: ^16 || ^18 || >= 20} + /node-addon-api@8.0.0: + resolution: {integrity: sha512-ipO7rsHEBqa9STO5C5T10fj732ml+5kLN1cAG8/jdHd56ldQeGj3Q7+scUS+VHK/qy1zLEwC4wMK5+yM0btPvw==} + engines: {node: ^18 || ^20 || >= 21} requiresBuild: true dev: false optional: true @@ -4783,10 +4783,10 @@ packages: readable-stream: 3.6.2 dev: true - github.com/OpenAsar/arrpc/98879cae0565e6fce34e4cb6f544bf42c6a7e7c8: - resolution: {tarball: https://codeload.github.com/OpenAsar/arrpc/tar.gz/98879cae0565e6fce34e4cb6f544bf42c6a7e7c8} + github.com/OpenAsar/arrpc/6960a8fd4d65d566da93dbdb8a7ca474aa0a3c9c: + resolution: {tarball: https://codeload.github.com/OpenAsar/arrpc/tar.gz/6960a8fd4d65d566da93dbdb8a7ca474aa0a3c9c} name: arrpc - version: 3.2.0 + version: 3.3.1 hasBin: true dependencies: ws: 8.13.0 diff --git a/src/main/index.ts b/src/main/index.ts index 9d6a52c..325d417 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -27,14 +27,18 @@ process.env.VENCORD_USER_DATA_DIR = DATA_DIR; function init() { const { disableSmoothScroll, hardwareAcceleration } = Settings.store; - if (hardwareAcceleration === false) app.disableHardwareAcceleration(); + if (hardwareAcceleration === false) { + app.disableHardwareAcceleration(); + } else { + app.commandLine.appendSwitch("enable-features", "VaapiVideoDecodeLinuxGL,VaapiVideoEncoder,VaapiVideoDecoder"); + } + if (disableSmoothScroll) { app.commandLine.appendSwitch("disable-smooth-scrolling"); } // work around chrome 66 disabling autoplay by default app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required"); - // WinRetrieveSuggestionsOnlyOnDemand: Work around electron 13 bug w/ async spellchecking on Windows. // HardwareMediaKeyHandling,MediaSessionService: Prevent Discord from registering as a media service. // diff --git a/src/main/mainWindow.ts b/src/main/mainWindow.ts index 7743697..c9a65f8 100644 --- a/src/main/mainWindow.ts +++ b/src/main/mainWindow.ts @@ -73,6 +73,10 @@ const [addSettingsListener, removeSettingsListeners] = makeSettingsListenerHelpe const [addVencordSettingsListener, removeVencordSettingsListeners] = makeSettingsListenerHelpers(VencordSettings); function initTray(win: BrowserWindow) { + const onTrayClick = () => { + if (Settings.store.clickTrayToShowHide && win.isVisible()) win.hide(); + else win.show(); + }; const trayMenu = Menu.buildFromTemplate([ { label: "Open", @@ -120,7 +124,7 @@ function initTray(win: BrowserWindow) { tray = new Tray(ICON_PATH); tray.setToolTip("Vesktop"); tray.setContextMenu(trayMenu); - tray.on("click", () => win.show()); + tray.on("click", onTrayClick); } async function clearData(win: BrowserWindow) { diff --git a/src/main/venmic.ts b/src/main/venmic.ts index c6f9d44..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) => { @@ -72,11 +97,12 @@ ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, targets: string[], workaround?: boo return obtainVenmic()?.link(data); }); -ipcMain.handle(IpcEvents.VIRT_MIC_START_SYSTEM, (_, workaround?: boolean) => { +ipcMain.handle(IpcEvents.VIRT_MIC_START_SYSTEM, (_, workaround?: boolean, onlyDefaultSpeakers?: boolean) => { const pid = getRendererAudioServicePid(); const data: LinkData = { - exclude: [{ key: "application.process.id", value: pid }] + exclude: [{ key: "application.process.id", value: pid }], + only_default_speakers: onlyDefaultSpeakers }; if (workaround) { diff --git a/src/preload/VesktopNative.ts b/src/preload/VesktopNative.ts index 1b7e2a4..18d11c7 100644 --- a/src/preload/VesktopNative.ts +++ b/src/preload/VesktopNative.ts @@ -77,9 +77,12 @@ 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) => invoke(IpcEvents.VIRT_MIC_START_SYSTEM, workaround), + startSystem: (workaround?: boolean, onlyDefaultSpeakers?: boolean) => + invoke(IpcEvents.VIRT_MIC_START_SYSTEM, workaround, onlyDefaultSpeakers), stop: () => invoke(IpcEvents.VIRT_MIC_STOP) }, arrpc: { diff --git a/src/renderer/components/ScreenSharePicker.tsx b/src/renderer/components/ScreenSharePicker.tsx index 9842afe..1c53b63 100644 --- a/src/renderer/components/ScreenSharePicker.tsx +++ b/src/renderer/components/ScreenSharePicker.tsx @@ -6,7 +6,7 @@ import "./screenSharePicker.css"; -import { closeModal, Margins, Modals, openModal, useAwaiter } from "@vencord/types/utils"; +import { closeModal, Logger, Margins, Modals, ModalSize, openModal, useAwaiter } from "@vencord/types/utils"; import { findStoreLazy, onceReady } from "@vencord/types/webpack"; import { Button, @@ -36,7 +36,9 @@ interface StreamSettings { fps: StreamFps; audio: boolean; audioSource?: string; + contentHint?: string; workaround?: boolean; + onlyDefaultSpeakers?: boolean; } export interface StreamPick extends StreamSettings { @@ -49,7 +51,9 @@ interface Source { url: string; } -let currentSettings: StreamSettings | null = null; +export let currentSettings: StreamSettings | null = null; + +const logger = new Logger("VesktopScreenShare"); addPatch({ patches: [ @@ -59,6 +63,20 @@ addPatch({ match: /this.localWant=/, replace: "$self.patchStreamQuality(this);$&" } + }, + { + find: "x-google-max-bitrate", + replacement: [ + { + // eslint-disable-next-line no-useless-escape + match: /"x-google-max-bitrate=".concat\(\i\)/, + replace: '"x-google-max-bitrate=".concat("80_000")' + }, + { + match: /;level-asymmetry-allowed=1/, + replace: ";b=AS:800000;level-asymmetry-allowed=1" + } + ] } ], patchStreamQuality(opts: any) { @@ -73,6 +91,14 @@ addPatch({ bitrateMax: 8000000, bitrateTarget: 600000 }); + if (opts?.encode) { + Object.assign(opts.encode, { + framerate, + width, + height, + pixelCount: height * width + }); + } Object.assign(opts.capture, { framerate, width, @@ -167,54 +193,102 @@ function StreamSettings({ ); return ( -
- What you're streaming - - - {source.name} - +
+
+ What you're streaming + + + {source.name} + - Stream Settings + Stream Settings - -
-
- Resolution -
- {StreamResolutions.map(res => ( - - ))} -
-
+ +
+
+ Resolution +
+ {StreamResolutions.map(res => ( + + ))} +
+
-
- Frame Rate -
- {StreamFps.map(fps => ( - - ))} -
-
-
+
+ Frame Rate +
+ {StreamFps.map(fps => ( + + ))} +
+
+
+
+
+ Content Type +
+
+ + +
+
+

+ Choosing "Prefer Clarity" will result in a significantly lower framerate in + exchange for a much sharper and clearer image. +

+
+
+
+
+
+
+
{isWindows && ( setSettings(s => ({ ...s, audioSource: source }))} - setWorkaround={workaround => setSettings(s => ({ ...s, workaround: workaround }))} + setWorkaround={value => setSettings(s => ({ ...s, workaround: value }))} + setOnlyDefaultSpeakers={value => setSettings(s => ({ ...s, onlyDefaultSpeakers: value }))} /> )} - +
); } @@ -242,63 +318,102 @@ function StreamSettings({ function AudioSourcePickerLinux({ audioSource, workaround, + onlyDefaultSpeakers, setAudioSource, - setWorkaround + setWorkaround, + setOnlyDefaultSpeakers }: { audioSource?: string; workaround?: boolean; + onlyDefaultSpeakers?: boolean; setAudioSource(s: string): void; setWorkaround(b: boolean): void; + 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 ( -
- Audio - {loading && Loading Audio sources...} - {!sources.ok && - (sources.isGlibcxxToOld ? ( + <> + Audio Settings + + {loading ? ( + Loading Audio Sources... + ) : ( + Audio Source + )} + + {!sources.ok && sources.isGlibCxxOutdated && ( - Failed to retrieve Audio Sources because your C++ library is too old to run venmic. If you would - like to stream with Audio, see{" "} + Failed to retrieve Audio Sources because your C++ library is too old to run + + venmic + + . See{" "} this guide - + {" "} + for possible solutions. + )} + + {hasPipewirePulse || ignorePulseWarning ? ( + allSources && ( + ({ label: s, value: s, default: s === "None" }))} - isSelected={s => s === audioSource} - select={setAudioSource} - serialize={String} - /> - )} + - + + Work around an issue that causes the microphone to be shared instead of the correct audio. + Only enable if you're experiencing this issue. + + } + > + Microphone Workaround + - - Work around an issue that causes the microphone to be shared instead of the correct audio. Only - enable if you're experiencing this issue. - - } - > - Microphone Workaround - -
+ + When sharing entire desktop audio, only share apps that play to the default speakers and + ignore apps that play to other speakers or devices. + + } + > + Only Default Speakers + + + ); } @@ -319,16 +434,16 @@ function ModalComponent({ const [settings, setSettings] = useState({ resolution: "1080", fps: "60", + contentHint: "motion", audio: true }); return ( - + ScreenShare - {!selected ? ( @@ -341,35 +456,62 @@ function ModalComponent({ /> )} -