feat: add ignore_virtual
, persist settings, re-design share menu
This commit is contained in:
parent
c8353d170f
commit
c7dc001e63
5 changed files with 310 additions and 264 deletions
|
@ -10,6 +10,8 @@ import { join } from "path";
|
||||||
import { IpcEvents } from "shared/IpcEvents";
|
import { IpcEvents } from "shared/IpcEvents";
|
||||||
import { STATIC_DIR } from "shared/paths";
|
import { STATIC_DIR } from "shared/paths";
|
||||||
|
|
||||||
|
import { Settings } from "./settings";
|
||||||
|
|
||||||
type LinkData = Parameters<PatchBayType["link"]>[0];
|
type LinkData = Parameters<PatchBayType["link"]>[0];
|
||||||
type Node = Record<string, string>;
|
type Node = Record<string, string>;
|
||||||
|
|
||||||
|
@ -67,17 +69,17 @@ function getRendererAudioServicePid() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.VIRT_MIC_LIST, (_, props?: string[]) => {
|
ipcMain.handle(IpcEvents.VIRT_MIC_LIST, () => {
|
||||||
const audioPid = getRendererAudioServicePid();
|
const audioPid = getRendererAudioServicePid();
|
||||||
|
|
||||||
const targets = obtainVenmic()
|
const targets = obtainVenmic()
|
||||||
?.list(props)
|
?.list(Settings.store.audioGranularSelect ? ["application.process.id"] : undefined)
|
||||||
.filter(s => s["application.process.id"] !== audioPid);
|
.filter(s => s["application.process.id"] !== audioPid);
|
||||||
|
|
||||||
return targets ? { ok: true, targets, hasPipewirePulse } : { ok: false, isGlibCxxOutdated };
|
return targets ? { ok: true, targets, hasPipewirePulse } : { ok: false, isGlibCxxOutdated };
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, targets: Node[], workaround?: boolean) => {
|
ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, targets: Node[]) => {
|
||||||
const pid = getRendererAudioServicePid();
|
const pid = getRendererAudioServicePid();
|
||||||
|
|
||||||
const data: LinkData = {
|
const data: LinkData = {
|
||||||
|
@ -85,22 +87,42 @@ ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, targets: Node[], workaround?: boole
|
||||||
exclude: [{ "application.process.id": pid }]
|
exclude: [{ "application.process.id": pid }]
|
||||||
};
|
};
|
||||||
|
|
||||||
if (workaround) {
|
const settings = Settings.store;
|
||||||
|
|
||||||
|
if (settings.audioIgnoreInputMedia ?? true) {
|
||||||
|
data.exclude?.push({ "media.class": "Stream/Input/Audio" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.audioIgnoreVirtual ?? true) {
|
||||||
|
data.exclude?.push({ "node.virtual": "true" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.audioWorkaround) {
|
||||||
data.workaround = [{ "application.process.id": pid, "media.name": "RecordStream" }];
|
data.workaround = [{ "application.process.id": pid, "media.name": "RecordStream" }];
|
||||||
}
|
}
|
||||||
|
|
||||||
return obtainVenmic()?.link(data);
|
return obtainVenmic()?.link(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.VIRT_MIC_START_SYSTEM, (_, workaround?: boolean, onlyDefaultSpeakers?: boolean) => {
|
ipcMain.handle(IpcEvents.VIRT_MIC_START_SYSTEM, () => {
|
||||||
const pid = getRendererAudioServicePid();
|
const pid = getRendererAudioServicePid();
|
||||||
|
|
||||||
|
const settings = Settings.store;
|
||||||
|
|
||||||
const data: LinkData = {
|
const data: LinkData = {
|
||||||
exclude: [{ "application.process.id": pid }],
|
exclude: [{ "application.process.id": pid }],
|
||||||
only_default_speakers: onlyDefaultSpeakers
|
only_default_speakers: settings.audioOnlyDefaultSpeakers
|
||||||
};
|
};
|
||||||
|
|
||||||
if (workaround) {
|
if (settings.audioIgnoreInputMedia ?? true) {
|
||||||
|
data.exclude?.push({ "media.class": "Stream/Input/Audio" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.audioIgnoreVirtual ?? true) {
|
||||||
|
data.exclude?.push({ "node.virtual": "true" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.audioWorkaround) {
|
||||||
data.workaround = [{ "application.process.id": pid, "media.name": "RecordStream" }];
|
data.workaround = [{ "application.process.id": pid, "media.name": "RecordStream" }];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,13 +62,12 @@ export const VesktopNative = {
|
||||||
},
|
},
|
||||||
/** only available on Linux. */
|
/** only available on Linux. */
|
||||||
virtmic: {
|
virtmic: {
|
||||||
list: (props?: string[]) =>
|
list: () =>
|
||||||
invoke<
|
invoke<
|
||||||
{ ok: false; isGlibCxxOutdated: boolean } | { ok: true; targets: Node[]; hasPipewirePulse: boolean }
|
{ ok: false; isGlibCxxOutdated: boolean } | { ok: true; targets: Node[]; hasPipewirePulse: boolean }
|
||||||
>(IpcEvents.VIRT_MIC_LIST, props),
|
>(IpcEvents.VIRT_MIC_LIST),
|
||||||
start: (targets: Node[], workaround?: boolean) => invoke<void>(IpcEvents.VIRT_MIC_START, targets, workaround),
|
start: (targets: Node[]) => invoke<void>(IpcEvents.VIRT_MIC_START, targets),
|
||||||
startSystem: (workaround?: boolean, onlyDefaultSpeakers?: boolean) =>
|
startSystem: () => invoke<void>(IpcEvents.VIRT_MIC_START_SYSTEM),
|
||||||
invoke<void>(IpcEvents.VIRT_MIC_START_SYSTEM, workaround, onlyDefaultSpeakers),
|
|
||||||
stop: () => invoke<void>(IpcEvents.VIRT_MIC_STOP)
|
stop: () => invoke<void>(IpcEvents.VIRT_MIC_STOP)
|
||||||
},
|
},
|
||||||
arrpc: {
|
arrpc: {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import "./screenSharePicker.css";
|
import "./screenSharePicker.css";
|
||||||
|
|
||||||
import { closeModal, Logger, Margins, Modals, ModalSize, openModal, useAwaiter } from "@vencord/types/utils";
|
import { closeModal, Logger, Modals, ModalSize, openModal, useAwaiter } from "@vencord/types/utils";
|
||||||
import { findStoreLazy, onceReady } from "@vencord/types/webpack";
|
import { findStoreLazy, onceReady } from "@vencord/types/webpack";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
@ -22,6 +22,7 @@ import {
|
||||||
import { Node } from "@vencord/venmic";
|
import { Node } from "@vencord/venmic";
|
||||||
import type { Dispatch, SetStateAction } from "react";
|
import type { Dispatch, SetStateAction } from "react";
|
||||||
import { addPatch } from "renderer/patches/shared";
|
import { addPatch } from "renderer/patches/shared";
|
||||||
|
import { useSettings } from "renderer/settings";
|
||||||
import { isLinux, isWindows } from "renderer/utils";
|
import { isLinux, isWindows } from "renderer/utils";
|
||||||
|
|
||||||
const StreamResolutions = ["480", "720", "1080", "1440"] as const;
|
const StreamResolutions = ["480", "720", "1080", "1440"] as const;
|
||||||
|
@ -48,10 +49,6 @@ interface StreamSettings {
|
||||||
audio: boolean;
|
audio: boolean;
|
||||||
audioSources?: AudioSources;
|
audioSources?: AudioSources;
|
||||||
contentHint?: string;
|
contentHint?: string;
|
||||||
workaround?: boolean;
|
|
||||||
ignoreInputMedia?: boolean;
|
|
||||||
onlyDefaultSpeakers?: boolean;
|
|
||||||
granularSelect?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StreamPick extends StreamSettings {
|
export interface StreamPick extends StreamSettings {
|
||||||
|
@ -133,9 +130,9 @@ export function openScreenSharePicker(screens: Source[], skipPicker: boolean) {
|
||||||
didSubmit = true;
|
didSubmit = true;
|
||||||
if (v.audioSources && v.audioSources !== "None") {
|
if (v.audioSources && v.audioSources !== "None") {
|
||||||
if (v.audioSources === "Entire System") {
|
if (v.audioSources === "Entire System") {
|
||||||
await VesktopNative.virtmic.startSystem(v.workaround);
|
await VesktopNative.virtmic.startSystem();
|
||||||
} else {
|
} else {
|
||||||
await VesktopNative.virtmic.start(v.audioSources, v.workaround);
|
await VesktopNative.virtmic.start(v.audioSources);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resolve(v);
|
resolve(v);
|
||||||
|
@ -172,6 +169,86 @@ function ScreenPicker({ screens, chooseScreen }: { screens: Source[]; chooseScre
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function AudioSettingsModal({
|
||||||
|
modalProps,
|
||||||
|
close,
|
||||||
|
setAudioSources
|
||||||
|
}: {
|
||||||
|
modalProps: any;
|
||||||
|
close: () => void;
|
||||||
|
setAudioSources: (s: AudioSources) => void;
|
||||||
|
}) {
|
||||||
|
const Settings = useSettings();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modals.ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
|
||||||
|
<Modals.ModalHeader className="vcd-screen-picker-header">
|
||||||
|
<Forms.FormTitle tag="h2">Venmic Settings</Forms.FormTitle>
|
||||||
|
<Modals.ModalCloseButton onClick={close} />
|
||||||
|
</Modals.ModalHeader>
|
||||||
|
<Modals.ModalContent className="vcd-screen-picker-modal">
|
||||||
|
<Switch
|
||||||
|
onChange={v => (Settings.audioWorkaround = v)}
|
||||||
|
value={Settings.audioWorkaround ?? false}
|
||||||
|
note={
|
||||||
|
<>
|
||||||
|
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
|
||||||
|
</Switch>
|
||||||
|
<Switch
|
||||||
|
hideBorder
|
||||||
|
onChange={v => (Settings.audioOnlyDefaultSpeakers = v)}
|
||||||
|
value={Settings.audioOnlyDefaultSpeakers ?? true}
|
||||||
|
note={<>When sharing entire desktop audio, only share apps that play to the default speakers.</>}
|
||||||
|
>
|
||||||
|
Only Default Speakers
|
||||||
|
</Switch>
|
||||||
|
<Switch
|
||||||
|
hideBorder
|
||||||
|
onChange={v => (Settings.audioIgnoreInputMedia = v)}
|
||||||
|
value={Settings.audioIgnoreInputMedia ?? true}
|
||||||
|
note={<>Exclude nodes that are intended to capture audio.</>}
|
||||||
|
>
|
||||||
|
Ignore Inputs
|
||||||
|
</Switch>
|
||||||
|
<Switch
|
||||||
|
hideBorder
|
||||||
|
onChange={v => (Settings.audioIgnoreVirtual = v)}
|
||||||
|
value={Settings.audioIgnoreVirtual ?? true}
|
||||||
|
note={
|
||||||
|
<>
|
||||||
|
Exclude virtual nodes, such as nodes belonging to sinks, this might be useful when using
|
||||||
|
"mix bussing"
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Ignore Virtual
|
||||||
|
</Switch>
|
||||||
|
<Switch
|
||||||
|
hideBorder
|
||||||
|
onChange={value => {
|
||||||
|
Settings.audioGranularSelect = value;
|
||||||
|
setAudioSources("None");
|
||||||
|
}}
|
||||||
|
value={Settings.audioGranularSelect ?? false}
|
||||||
|
note={<>Allow to select applications more granularly.</>}
|
||||||
|
>
|
||||||
|
Granular Selection
|
||||||
|
</Switch>
|
||||||
|
</Modals.ModalContent>
|
||||||
|
<Modals.ModalFooter className="vcd-screen-picker-footer">
|
||||||
|
<Button color={Button.Colors.TRANSPARENT} onClick={close}>
|
||||||
|
Back
|
||||||
|
</Button>
|
||||||
|
</Modals.ModalFooter>
|
||||||
|
</Modals.ModalRoot>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function StreamSettings({
|
function StreamSettings({
|
||||||
source,
|
source,
|
||||||
settings,
|
settings,
|
||||||
|
@ -183,6 +260,8 @@ function StreamSettings({
|
||||||
setSettings: Dispatch<SetStateAction<StreamSettings>>;
|
setSettings: Dispatch<SetStateAction<StreamSettings>>;
|
||||||
skipPicker: boolean;
|
skipPicker: boolean;
|
||||||
}) {
|
}) {
|
||||||
|
const Settings = useSettings();
|
||||||
|
|
||||||
const [thumb] = useAwaiter(
|
const [thumb] = useAwaiter(
|
||||||
() => (skipPicker ? Promise.resolve(source.url) : VesktopNative.capturer.getLargeThumbnail(source.id)),
|
() => (skipPicker ? Promise.resolve(source.url) : VesktopNative.capturer.getLargeThumbnail(source.id)),
|
||||||
{
|
{
|
||||||
|
@ -191,132 +270,129 @@ function StreamSettings({
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const openSettings = () => {
|
||||||
|
const key = openModal(props => (
|
||||||
|
<AudioSettingsModal
|
||||||
|
modalProps={props}
|
||||||
|
close={() => props.onClose()}
|
||||||
|
setAudioSources={sources => setSettings(s => ({ ...s, audioSources: sources }))}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={isLinux ? "vcd-screen-picker-settings-grid" : ""}>
|
<div>
|
||||||
<div>
|
<Forms.FormTitle>What you're streaming</Forms.FormTitle>
|
||||||
<Forms.FormTitle>What you're streaming</Forms.FormTitle>
|
<Card className="vcd-screen-picker-card vcd-screen-picker-preview">
|
||||||
<Card className="vcd-screen-picker-card vcd-screen-picker-preview">
|
<img
|
||||||
<img
|
src={thumb}
|
||||||
src={thumb}
|
alt=""
|
||||||
alt=""
|
className={isLinux ? "vcd-screen-picker-preview-img-linux" : "vcd-screen-picker-preview-img"}
|
||||||
className={isLinux ? "vcd-screen-picker-preview-img-linux" : "vcd-screen-picker-preview-img"}
|
/>
|
||||||
/>
|
<Text variant="text-sm/normal">{source.name}</Text>
|
||||||
<Text variant="text-sm/normal">{source.name}</Text>
|
</Card>
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Forms.FormTitle>Stream Settings</Forms.FormTitle>
|
<Forms.FormTitle>Stream Settings</Forms.FormTitle>
|
||||||
|
|
||||||
<Card className="vcd-screen-picker-card">
|
<Card className="vcd-screen-picker-card">
|
||||||
<div className="vcd-screen-picker-quality">
|
<div className="vcd-screen-picker-quality">
|
||||||
<section>
|
<section>
|
||||||
<Forms.FormTitle>Resolution</Forms.FormTitle>
|
<Forms.FormTitle>Resolution</Forms.FormTitle>
|
||||||
|
<div className="vcd-screen-picker-radios">
|
||||||
|
{StreamResolutions.map(res => (
|
||||||
|
<label className="vcd-screen-picker-radio" data-checked={settings.resolution === res}>
|
||||||
|
<Text variant="text-sm/bold">{res}</Text>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="resolution"
|
||||||
|
value={res}
|
||||||
|
checked={settings.resolution === res}
|
||||||
|
onChange={() => setSettings(s => ({ ...s, resolution: res }))}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<Forms.FormTitle>Frame Rate</Forms.FormTitle>
|
||||||
|
<div className="vcd-screen-picker-radios">
|
||||||
|
{StreamFps.map(fps => (
|
||||||
|
<label className="vcd-screen-picker-radio" data-checked={settings.fps === fps}>
|
||||||
|
<Text variant="text-sm/bold">{fps}</Text>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="fps"
|
||||||
|
value={fps}
|
||||||
|
checked={settings.fps === fps}
|
||||||
|
onChange={() => setSettings(s => ({ ...s, fps }))}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div className="vcd-screen-picker-quality">
|
||||||
|
<section>
|
||||||
|
<Forms.FormTitle>Content Type</Forms.FormTitle>
|
||||||
|
<div>
|
||||||
<div className="vcd-screen-picker-radios">
|
<div className="vcd-screen-picker-radios">
|
||||||
{StreamResolutions.map(res => (
|
<label
|
||||||
<label
|
className="vcd-screen-picker-radio"
|
||||||
className="vcd-screen-picker-radio"
|
data-checked={settings.contentHint === "motion"}
|
||||||
data-checked={settings.resolution === res}
|
|
||||||
>
|
|
||||||
<Text variant="text-sm/bold">{res}</Text>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="resolution"
|
|
||||||
value={res}
|
|
||||||
checked={settings.resolution === res}
|
|
||||||
onChange={() => setSettings(s => ({ ...s, resolution: res }))}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<Forms.FormTitle>Frame Rate</Forms.FormTitle>
|
|
||||||
<div className="vcd-screen-picker-radios">
|
|
||||||
{StreamFps.map(fps => (
|
|
||||||
<label className="vcd-screen-picker-radio" data-checked={settings.fps === fps}>
|
|
||||||
<Text variant="text-sm/bold">{fps}</Text>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="fps"
|
|
||||||
value={fps}
|
|
||||||
checked={settings.fps === fps}
|
|
||||||
onChange={() => setSettings(s => ({ ...s, fps }))}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
<div className="vcd-screen-picker-quality">
|
|
||||||
<section>
|
|
||||||
<Forms.FormTitle>Content Type</Forms.FormTitle>
|
|
||||||
<div>
|
|
||||||
<div className="vcd-screen-picker-radios">
|
|
||||||
<label
|
|
||||||
className="vcd-screen-picker-radio"
|
|
||||||
data-checked={settings.contentHint === "motion"}
|
|
||||||
>
|
|
||||||
<Text variant="text-sm/bold">Prefer Smoothness</Text>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="contenthint"
|
|
||||||
value="motion"
|
|
||||||
checked={settings.contentHint === "motion"}
|
|
||||||
onChange={() => setSettings(s => ({ ...s, contentHint: "motion" }))}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label
|
|
||||||
className="vcd-screen-picker-radio"
|
|
||||||
data-checked={settings.contentHint === "detail"}
|
|
||||||
>
|
|
||||||
<Text variant="text-sm/bold">Prefer Clarity</Text>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="contenthint"
|
|
||||||
value="detail"
|
|
||||||
checked={settings.contentHint === "detail"}
|
|
||||||
onChange={() => setSettings(s => ({ ...s, contentHint: "detail" }))}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className="vcd-screen-picker-hint-description">
|
|
||||||
<p>
|
|
||||||
Choosing "Prefer Clarity" will result in a significantly lower framerate in
|
|
||||||
exchange for a much sharper and clearer image.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{isWindows && (
|
|
||||||
<Switch
|
|
||||||
value={settings.audio}
|
|
||||||
onChange={checked => setSettings(s => ({ ...s, audio: checked }))}
|
|
||||||
hideBorder
|
|
||||||
className="vcd-screen-picker-audio"
|
|
||||||
>
|
>
|
||||||
Stream With Audio
|
<Text variant="text-sm/bold">Prefer Smoothness</Text>
|
||||||
</Switch>
|
<input
|
||||||
)}
|
type="radio"
|
||||||
</section>
|
name="contenthint"
|
||||||
</div>
|
value="motion"
|
||||||
</Card>
|
checked={settings.contentHint === "motion"}
|
||||||
</div>
|
onChange={() => setSettings(s => ({ ...s, contentHint: "motion" }))}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label
|
||||||
|
className="vcd-screen-picker-radio"
|
||||||
|
data-checked={settings.contentHint === "detail"}
|
||||||
|
>
|
||||||
|
<Text variant="text-sm/bold">Prefer Clarity</Text>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="contenthint"
|
||||||
|
value="detail"
|
||||||
|
checked={settings.contentHint === "detail"}
|
||||||
|
onChange={() => setSettings(s => ({ ...s, contentHint: "detail" }))}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="vcd-screen-picker-hint-description">
|
||||||
|
<p>
|
||||||
|
Choosing "Prefer Clarity" will result in a significantly lower framerate in exchange
|
||||||
|
for a much sharper and clearer image.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{isWindows && (
|
||||||
|
<Switch
|
||||||
|
value={settings.audio}
|
||||||
|
onChange={checked => setSettings(s => ({ ...s, audio: checked }))}
|
||||||
|
hideBorder
|
||||||
|
className="vcd-screen-picker-audio"
|
||||||
|
>
|
||||||
|
Stream With Audio
|
||||||
|
</Switch>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
|
||||||
{isLinux && (
|
{isLinux && (
|
||||||
<AudioSourcePickerLinux
|
<AudioSourcePickerLinux
|
||||||
|
openSettings={openSettings}
|
||||||
audioSources={settings.audioSources}
|
audioSources={settings.audioSources}
|
||||||
workaround={settings.workaround}
|
granularSelect={Settings.audioGranularSelect}
|
||||||
ignoreInputMedia={settings.ignoreInputMedia}
|
|
||||||
onlyDefaultSpeakers={settings.onlyDefaultSpeakers}
|
|
||||||
granularSelect={settings.granularSelect}
|
|
||||||
setAudioSources={sources => setSettings(s => ({ ...s, audioSources: sources }))}
|
setAudioSources={sources => setSettings(s => ({ ...s, audioSources: sources }))}
|
||||||
setWorkaround={value => setSettings(s => ({ ...s, workaround: value }))}
|
|
||||||
setOnlyDefaultSpeakers={value => setSettings(s => ({ ...s, onlyDefaultSpeakers: value }))}
|
|
||||||
setIgnoreInputMedia={value => setSettings(s => ({ ...s, ignoreInputMedia: value }))}
|
|
||||||
setGranularSelect={value => setSettings(s => ({ ...s, granularSelect: value }))}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -387,30 +463,16 @@ function mapToAudioItem(node: AudioSource, granularSelect?: boolean): AudioItem[
|
||||||
|
|
||||||
function AudioSourcePickerLinux({
|
function AudioSourcePickerLinux({
|
||||||
audioSources,
|
audioSources,
|
||||||
workaround,
|
|
||||||
onlyDefaultSpeakers,
|
|
||||||
ignoreInputMedia,
|
|
||||||
granularSelect,
|
granularSelect,
|
||||||
setAudioSources,
|
setAudioSources,
|
||||||
setWorkaround,
|
openSettings
|
||||||
setIgnoreInputMedia,
|
|
||||||
setOnlyDefaultSpeakers,
|
|
||||||
setGranularSelect
|
|
||||||
}: {
|
}: {
|
||||||
audioSources?: AudioSources;
|
audioSources?: AudioSources;
|
||||||
workaround?: boolean;
|
|
||||||
onlyDefaultSpeakers?: boolean;
|
|
||||||
ignoreInputMedia?: boolean;
|
|
||||||
granularSelect?: boolean;
|
granularSelect?: boolean;
|
||||||
setAudioSources(s: AudioSources): void;
|
openSettings: () => void;
|
||||||
setWorkaround(b: boolean): void;
|
setAudioSources: (s: AudioSources) => void;
|
||||||
setIgnoreInputMedia(b: boolean): void;
|
|
||||||
setOnlyDefaultSpeakers(b: boolean): void;
|
|
||||||
setGranularSelect(b: boolean): void;
|
|
||||||
}) {
|
}) {
|
||||||
const properties = granularSelect ? ["application.process.id"] : undefined;
|
const [sources, _, loading] = useAwaiter(() => VesktopNative.virtmic.list(), {
|
||||||
|
|
||||||
const [sources, _, loading] = useAwaiter(() => VesktopNative.virtmic.list(properties), {
|
|
||||||
fallbackValue: { ok: true, targets: [], hasPipewirePulse: true }
|
fallbackValue: { ok: true, targets: [], hasPipewirePulse: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -455,109 +517,71 @@ function AudioSourcePickerLinux({
|
||||||
list.findIndex(x => x.name === value.name) === index;
|
list.findIndex(x => x.name === value.name) === index;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div>
|
||||||
<Forms.FormTitle>Audio Settings</Forms.FormTitle>
|
{loading ? (
|
||||||
<Card className="vcd-screen-picker-card">
|
<Forms.FormTitle>Loading Audio Sources...</Forms.FormTitle>
|
||||||
{loading ? (
|
) : (
|
||||||
<Forms.FormTitle>Loading Audio Sources...</Forms.FormTitle>
|
<Forms.FormTitle>Audio Source</Forms.FormTitle>
|
||||||
) : (
|
)}
|
||||||
<Forms.FormTitle>Audio Source</Forms.FormTitle>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!sources.ok && sources.isGlibCxxOutdated && (
|
{!sources.ok && sources.isGlibCxxOutdated && (
|
||||||
<Forms.FormText>
|
<Forms.FormText>
|
||||||
Failed to retrieve Audio Sources because your C++ library is too old to run
|
Failed to retrieve Audio Sources because your C++ library is too old to run
|
||||||
<a href="https://github.com/Vencord/venmic" target="_blank">
|
<a href="https://github.com/Vencord/venmic" target="_blank">
|
||||||
venmic
|
venmic
|
||||||
</a>
|
</a>
|
||||||
. See{" "}
|
. See{" "}
|
||||||
<a href="https://gist.github.com/Vendicated/b655044ffbb16b2716095a448c6d827a" target="_blank">
|
<a href="https://gist.github.com/Vendicated/b655044ffbb16b2716095a448c6d827a" target="_blank">
|
||||||
this guide
|
this guide
|
||||||
</a>{" "}
|
</a>{" "}
|
||||||
for possible solutions.
|
for possible solutions.
|
||||||
</Forms.FormText>
|
</Forms.FormText>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{hasPipewirePulse || ignorePulseWarning ? (
|
{hasPipewirePulse || ignorePulseWarning ? (
|
||||||
allSources && (
|
allSources && (
|
||||||
<Select
|
<Select
|
||||||
options={allSources
|
options={allSources
|
||||||
.map(target => mapToAudioItem(target, granularSelect))
|
.map(target => mapToAudioItem(target, granularSelect))
|
||||||
.flat()
|
.flat()
|
||||||
.filter(uniqueName)
|
.filter(uniqueName)
|
||||||
.map(({ name, value }) => ({
|
.map(({ name, value }) => ({
|
||||||
label: name,
|
label: name,
|
||||||
value: value,
|
value: value,
|
||||||
default: name === "None"
|
default: name === "None"
|
||||||
}))}
|
}))}
|
||||||
isSelected={isSelected}
|
isSelected={isSelected}
|
||||||
select={update}
|
select={update}
|
||||||
serialize={String}
|
serialize={String}
|
||||||
/>
|
popoutPosition="top"
|
||||||
)
|
/>
|
||||||
) : (
|
)
|
||||||
<Text variant="text-sm/normal">
|
) : (
|
||||||
Could not find pipewire-pulse. See{" "}
|
<Text variant="text-sm/normal">
|
||||||
<a
|
Could not find pipewire-pulse. See{" "}
|
||||||
href="https://gist.github.com/the-spyke/2de98b22ff4f978ebf0650c90e82027e#install"
|
<a
|
||||||
target="_blank"
|
href="https://gist.github.com/the-spyke/2de98b22ff4f978ebf0650c90e82027e#install"
|
||||||
>
|
target="_blank"
|
||||||
this guide
|
>
|
||||||
</a>{" "}
|
this guide
|
||||||
on how to switch to pipewire. <br />
|
</a>{" "}
|
||||||
You can still continue, however, please{" "}
|
on how to switch to pipewire. <br />
|
||||||
<b>beware that you can only share audio of apps that are running under pipewire</b>.
|
You can still continue, however, please{" "}
|
||||||
<br />
|
<b>beware that you can only share audio of apps that are running under pipewire</b>.
|
||||||
<br />
|
<br />
|
||||||
<a onClick={() => setIgnorePulseWarning(true)}>I know what I'm doing</a>
|
<br />
|
||||||
</Text>
|
<a onClick={() => setIgnorePulseWarning(true)}>I know what I'm doing</a>
|
||||||
)}
|
</Text>
|
||||||
|
)}
|
||||||
|
|
||||||
<Forms.FormDivider className={Margins.top16 + " " + Margins.bottom16} />
|
<Button
|
||||||
|
color={Button.Colors.TRANSPARENT}
|
||||||
<Switch
|
onClick={openSettings}
|
||||||
onChange={setWorkaround}
|
className="vcd-screen-picker-settings-button"
|
||||||
value={workaround ?? false}
|
>
|
||||||
note={
|
Open Audio Settings
|
||||||
<>
|
</Button>
|
||||||
Work around an issue that causes the microphone to be shared instead of the correct audio.
|
</div>
|
||||||
Only enable if you're experiencing this issue.
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Microphone Workaround
|
|
||||||
</Switch>
|
|
||||||
|
|
||||||
<Switch
|
|
||||||
hideBorder
|
|
||||||
onChange={setOnlyDefaultSpeakers}
|
|
||||||
disabled={audioSources !== "Entire System"}
|
|
||||||
value={onlyDefaultSpeakers ?? true}
|
|
||||||
note={<>When sharing entire desktop audio, only share apps that play to the default speakers.</>}
|
|
||||||
>
|
|
||||||
Only Default Speakers
|
|
||||||
</Switch>
|
|
||||||
<Switch
|
|
||||||
hideBorder
|
|
||||||
onChange={setIgnoreInputMedia}
|
|
||||||
value={ignoreInputMedia ?? true}
|
|
||||||
note={<>Ignore Nodes that are intended to capture audio.</>}
|
|
||||||
>
|
|
||||||
Ignore Input Media
|
|
||||||
</Switch>
|
|
||||||
<Switch
|
|
||||||
hideBorder
|
|
||||||
onChange={value => {
|
|
||||||
setGranularSelect(value);
|
|
||||||
setAudioSources("None");
|
|
||||||
}}
|
|
||||||
value={granularSelect ?? false}
|
|
||||||
note={<>Allow to select applications more granularly.</>}
|
|
||||||
>
|
|
||||||
Granular Selection
|
|
||||||
</Switch>
|
|
||||||
</Card>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,17 +11,6 @@
|
||||||
gap: 1em;
|
gap: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vcd-screen-picker-settings-grid {
|
|
||||||
gap: 1em;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vcd-screen-picker-settings-grid>div {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vcd-screen-picker-card {
|
.vcd-screen-picker-card {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
@ -68,7 +57,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.vcd-screen-picker-preview-img-linux {
|
.vcd-screen-picker-preview-img-linux {
|
||||||
width: 100%;
|
width: 60%;
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,6 +109,11 @@
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vcd-screen-picker-settings-button {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-top: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
.vcd-screen-picker-radios {
|
.vcd-screen-picker-radios {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
7
src/shared/settings.d.ts
vendored
7
src/shared/settings.d.ts
vendored
|
@ -30,6 +30,13 @@ export interface Settings {
|
||||||
splashBackground?: string;
|
splashBackground?: string;
|
||||||
|
|
||||||
spellCheckLanguages?: string[];
|
spellCheckLanguages?: string[];
|
||||||
|
|
||||||
|
audioWorkaround?: boolean;
|
||||||
|
audioGranularSelect?: boolean;
|
||||||
|
|
||||||
|
audioIgnoreVirtual?: boolean;
|
||||||
|
audioIgnoreInputMedia?: boolean;
|
||||||
|
audioOnlyDefaultSpeakers?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
|
|
Reference in a new issue