feat(ScreenShare): allow additional exclusions
This commit is contained in:
parent
379861616b
commit
70b530d2e5
3 changed files with 138 additions and 105 deletions
|
@ -78,12 +78,12 @@ ipcMain.handle(IpcEvents.VIRT_MIC_LIST, () => {
|
||||||
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[]) => {
|
ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, include: Node[]) => {
|
||||||
const pid = getRendererAudioServicePid();
|
const pid = getRendererAudioServicePid();
|
||||||
const settings = Settings.store;
|
const settings = Settings.store;
|
||||||
|
|
||||||
const data: LinkData = {
|
const data: LinkData = {
|
||||||
include: targets,
|
include: include,
|
||||||
exclude: [{ "application.process.id": pid }],
|
exclude: [{ "application.process.id": pid }],
|
||||||
ignore_devices: settings.audioIgnoreDevices
|
ignore_devices: settings.audioIgnoreDevices
|
||||||
};
|
};
|
||||||
|
@ -103,12 +103,12 @@ ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, targets: Node[]) => {
|
||||||
return obtainVenmic()?.link(data);
|
return obtainVenmic()?.link(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.VIRT_MIC_START_SYSTEM, () => {
|
ipcMain.handle(IpcEvents.VIRT_MIC_START_SYSTEM, (_, exclude: Node[]) => {
|
||||||
const pid = getRendererAudioServicePid();
|
const pid = getRendererAudioServicePid();
|
||||||
const settings = Settings.store;
|
const settings = Settings.store;
|
||||||
|
|
||||||
const data: LinkData = {
|
const data: LinkData = {
|
||||||
exclude: [{ "application.process.id": pid }],
|
exclude: [{ "application.process.id": pid }, ...exclude],
|
||||||
only_speakers: settings.audioOnlySpeakers,
|
only_speakers: settings.audioOnlySpeakers,
|
||||||
ignore_devices: settings.audioIgnoreDevices,
|
ignore_devices: settings.audioIgnoreDevices,
|
||||||
only_default_speakers: settings.audioOnlyDefaultSpeakers
|
only_default_speakers: settings.audioOnlyDefaultSpeakers
|
||||||
|
|
|
@ -66,8 +66,8 @@ export const VesktopNative = {
|
||||||
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),
|
>(IpcEvents.VIRT_MIC_LIST),
|
||||||
start: (targets: Node[]) => invoke<void>(IpcEvents.VIRT_MIC_START, targets),
|
start: (include: Node[]) => invoke<void>(IpcEvents.VIRT_MIC_START, include),
|
||||||
startSystem: () => invoke<void>(IpcEvents.VIRT_MIC_START_SYSTEM),
|
startSystem: (exclude: Node[]) => invoke<void>(IpcEvents.VIRT_MIC_START_SYSTEM, exclude),
|
||||||
stop: () => invoke<void>(IpcEvents.VIRT_MIC_STOP)
|
stop: () => invoke<void>(IpcEvents.VIRT_MIC_STOP)
|
||||||
},
|
},
|
||||||
arrpc: {
|
arrpc: {
|
||||||
|
|
|
@ -47,8 +47,9 @@ interface StreamSettings {
|
||||||
resolution: StreamResolution;
|
resolution: StreamResolution;
|
||||||
fps: StreamFps;
|
fps: StreamFps;
|
||||||
audio: boolean;
|
audio: boolean;
|
||||||
audioSources?: AudioSources;
|
|
||||||
contentHint?: string;
|
contentHint?: string;
|
||||||
|
includeSources?: AudioSources;
|
||||||
|
excludeSources?: AudioSources;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StreamPick extends StreamSettings {
|
export interface StreamPick extends StreamSettings {
|
||||||
|
@ -128,13 +129,17 @@ export function openScreenSharePicker(screens: Source[], skipPicker: boolean) {
|
||||||
modalProps={props}
|
modalProps={props}
|
||||||
submit={async v => {
|
submit={async v => {
|
||||||
didSubmit = true;
|
didSubmit = true;
|
||||||
if (v.audioSources && v.audioSources !== "None") {
|
|
||||||
if (v.audioSources === "Entire System") {
|
if (v.includeSources && v.includeSources !== "None") {
|
||||||
await VesktopNative.virtmic.startSystem();
|
if (v.includeSources === "Entire System") {
|
||||||
|
await VesktopNative.virtmic.startSystem(
|
||||||
|
!v.excludeSources || isSpecialSource(v.excludeSources) ? [] : v.excludeSources
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
await VesktopNative.virtmic.start(v.audioSources);
|
await VesktopNative.virtmic.start(v.includeSources);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(v);
|
resolve(v);
|
||||||
}}
|
}}
|
||||||
close={() => {
|
close={() => {
|
||||||
|
@ -302,7 +307,9 @@ function StreamSettings({
|
||||||
<AudioSettingsModal
|
<AudioSettingsModal
|
||||||
modalProps={props}
|
modalProps={props}
|
||||||
close={() => props.onClose()}
|
close={() => props.onClose()}
|
||||||
setAudioSources={sources => setSettings(s => ({ ...s, audioSources: sources }))}
|
setAudioSources={sources =>
|
||||||
|
setSettings(s => ({ ...s, includeSources: sources, excludeSources: sources }))
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
@ -414,9 +421,11 @@ function StreamSettings({
|
||||||
{isLinux && (
|
{isLinux && (
|
||||||
<AudioSourcePickerLinux
|
<AudioSourcePickerLinux
|
||||||
openSettings={openSettings}
|
openSettings={openSettings}
|
||||||
audioSources={settings.audioSources}
|
includeSources={settings.includeSources}
|
||||||
|
excludeSources={settings.excludeSources}
|
||||||
granularSelect={Settings.audioGranularSelect}
|
granularSelect={Settings.audioGranularSelect}
|
||||||
setAudioSources={sources => setSettings(s => ({ ...s, audioSources: sources }))}
|
setIncludeSources={sources => setSettings(s => ({ ...s, includeSources: sources }))}
|
||||||
|
setExcludeSources={sources => setSettings(s => ({ ...s, excludeSources: sources }))}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
|
@ -488,128 +497,152 @@ function mapToAudioItem(node: AudioSource, granularSelect?: boolean): AudioItem[
|
||||||
return rtn;
|
return rtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isItemSelected(sources?: AudioSources) {
|
||||||
|
return (value: AudioSource) => {
|
||||||
|
if (!sources) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSpecialSource(sources) || isSpecialSource(value)) {
|
||||||
|
return sources === value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sources.some(source => hasMatchingProps(source, value));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateItems(setSources: (s: AudioSources) => void, sources?: AudioSources) {
|
||||||
|
return (value: AudioSource) => {
|
||||||
|
if (isSpecialSource(value)) {
|
||||||
|
setSources(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSpecialSource(sources)) {
|
||||||
|
setSources([value]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isItemSelected(sources)(value)) {
|
||||||
|
setSources(sources?.filter(x => !hasMatchingProps(x, value)) ?? "None");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSources([...(sources || []), value]);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function AudioSourcePickerLinux({
|
function AudioSourcePickerLinux({
|
||||||
audioSources,
|
includeSources,
|
||||||
|
excludeSources,
|
||||||
granularSelect,
|
granularSelect,
|
||||||
setAudioSources,
|
openSettings,
|
||||||
openSettings
|
setIncludeSources,
|
||||||
|
setExcludeSources
|
||||||
}: {
|
}: {
|
||||||
audioSources?: AudioSources;
|
includeSources?: AudioSources;
|
||||||
|
excludeSources?: AudioSources;
|
||||||
granularSelect?: boolean;
|
granularSelect?: boolean;
|
||||||
openSettings: () => void;
|
openSettings: () => void;
|
||||||
setAudioSources: (s: AudioSources) => void;
|
setIncludeSources: (s: AudioSources) => void;
|
||||||
|
setExcludeSources: (s: AudioSources) => void;
|
||||||
}) {
|
}) {
|
||||||
const [sources, _, loading] = useAwaiter(() => VesktopNative.virtmic.list(), {
|
const [sources, _, loading] = useAwaiter(() => VesktopNative.virtmic.list(), {
|
||||||
fallbackValue: { ok: true, targets: [], hasPipewirePulse: true }
|
fallbackValue: { ok: true, targets: [], hasPipewirePulse: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
const specialSources: SpecialSource[] = ["None", "Entire System"];
|
|
||||||
const allSources = sources.ok ? [...specialSources, ...sources.targets] : null;
|
|
||||||
|
|
||||||
const hasPipewirePulse = sources.ok ? sources.hasPipewirePulse : true;
|
const hasPipewirePulse = sources.ok ? sources.hasPipewirePulse : true;
|
||||||
const [ignorePulseWarning, setIgnorePulseWarning] = useState(false);
|
const [ignorePulseWarning, setIgnorePulseWarning] = useState(false);
|
||||||
|
|
||||||
const isSelected = (value: AudioSource) => {
|
if (!sources.ok && sources.isGlibCxxOutdated) {
|
||||||
if (!audioSources) {
|
return (
|
||||||
return false;
|
<Forms.FormText>
|
||||||
}
|
Failed to retrieve Audio Sources because your C++ library is too old to run
|
||||||
|
<a href="https://github.com/Vencord/venmic" target="_blank">
|
||||||
|
venmic
|
||||||
|
</a>
|
||||||
|
. See{" "}
|
||||||
|
<a href="https://gist.github.com/Vendicated/b655044ffbb16b2716095a448c6d827a" target="_blank">
|
||||||
|
this guide
|
||||||
|
</a>{" "}
|
||||||
|
for possible solutions.
|
||||||
|
</Forms.FormText>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (isSpecialSource(audioSources) || isSpecialSource(value)) {
|
if (!hasPipewirePulse && !ignorePulseWarning) {
|
||||||
return audioSources === value;
|
return (
|
||||||
}
|
<Text variant="text-sm/normal">
|
||||||
|
Could not find pipewire-pulse. See{" "}
|
||||||
|
<a href="https://gist.github.com/the-spyke/2de98b22ff4f978ebf0650c90e82027e#install" target="_blank">
|
||||||
|
this guide
|
||||||
|
</a>{" "}
|
||||||
|
on how to switch to pipewire. <br />
|
||||||
|
You can still continue, however, please{" "}
|
||||||
|
<b>beware that you can only share audio of apps that are running under pipewire</b>.{" "}
|
||||||
|
<a onClick={() => setIgnorePulseWarning(true)}>I know what I'm doing!</a>
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return audioSources.some(source => hasMatchingProps(source, value));
|
const specialSources: SpecialSource[] = ["None", "Entire System"] as const;
|
||||||
};
|
|
||||||
|
|
||||||
const update = (value: SpecialSource | Node) => {
|
|
||||||
if (isSpecialSource(value)) {
|
|
||||||
setAudioSources(value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSpecialSource(audioSources)) {
|
|
||||||
setAudioSources([value]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSelected(value)) {
|
|
||||||
setAudioSources(audioSources?.filter(x => !hasMatchingProps(x, value)) ?? "None");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setAudioSources([...(audioSources || []), value]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const uniqueName = (value: AudioItem, index: number, list: AudioItem[]) =>
|
const uniqueName = (value: AudioItem, index: number, list: AudioItem[]) =>
|
||||||
list.findIndex(x => x.name === value.name) === index;
|
list.findIndex(x => x.name === value.name) === index;
|
||||||
|
|
||||||
|
const allSources = sources.ok
|
||||||
|
? [...specialSources, ...sources.targets]
|
||||||
|
.map(target => mapToAudioItem(target, granularSelect))
|
||||||
|
.flat()
|
||||||
|
.filter(uniqueName)
|
||||||
|
: [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
{loading ? (
|
<div className={includeSources === "Entire System" ? "vcd-screen-picker-quality" : undefined}>
|
||||||
<Forms.FormTitle>Loading Audio Sources...</Forms.FormTitle>
|
<section>
|
||||||
) : (
|
<Forms.FormTitle>{loading ? "Loading Sources..." : "Audio Sources"}</Forms.FormTitle>
|
||||||
<Forms.FormTitle>Audio Source</Forms.FormTitle>
|
<Select
|
||||||
)}
|
options={allSources.map(({ name, value }) => ({
|
||||||
|
label: name,
|
||||||
{!sources.ok && sources.isGlibCxxOutdated && (
|
value: value,
|
||||||
<Forms.FormText>
|
default: name === "None"
|
||||||
Failed to retrieve Audio Sources because your C++ library is too old to run
|
}))}
|
||||||
<a href="https://github.com/Vencord/venmic" target="_blank">
|
isSelected={isItemSelected(includeSources)}
|
||||||
venmic
|
select={updateItems(setIncludeSources, includeSources)}
|
||||||
</a>
|
serialize={String}
|
||||||
. See{" "}
|
popoutPosition="top"
|
||||||
<a href="https://gist.github.com/Vendicated/b655044ffbb16b2716095a448c6d827a" target="_blank">
|
closeOnSelect={false}
|
||||||
this guide
|
/>
|
||||||
</a>{" "}
|
</section>
|
||||||
for possible solutions.
|
{includeSources === "Entire System" && (
|
||||||
</Forms.FormText>
|
<section>
|
||||||
)}
|
<Forms.FormTitle>Exclude Sources</Forms.FormTitle>
|
||||||
|
|
||||||
{hasPipewirePulse || ignorePulseWarning ? (
|
|
||||||
allSources && (
|
|
||||||
<>
|
|
||||||
<Select
|
<Select
|
||||||
options={allSources
|
options={allSources
|
||||||
.map(target => mapToAudioItem(target, granularSelect))
|
.filter(x => x.name !== "Entire System")
|
||||||
.flat()
|
|
||||||
.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={isItemSelected(excludeSources)}
|
||||||
select={update}
|
select={updateItems(setExcludeSources, excludeSources)}
|
||||||
serialize={String}
|
serialize={String}
|
||||||
popoutPosition="top"
|
popoutPosition="top"
|
||||||
closeOnSelect={false}
|
closeOnSelect={false}
|
||||||
/>
|
/>
|
||||||
|
</section>
|
||||||
<Button
|
)}
|
||||||
color={Button.Colors.TRANSPARENT}
|
</div>
|
||||||
onClick={openSettings}
|
<Button
|
||||||
className="vcd-screen-picker-settings-button"
|
color={Button.Colors.TRANSPARENT}
|
||||||
>
|
onClick={openSettings}
|
||||||
Open Audio Settings
|
className="vcd-screen-picker-settings-button"
|
||||||
</Button>
|
>
|
||||||
</>
|
Open Audio Settings
|
||||||
)
|
</Button>
|
||||||
) : (
|
</>
|
||||||
<Text variant="text-sm/normal">
|
|
||||||
Could not find pipewire-pulse. See{" "}
|
|
||||||
<a
|
|
||||||
href="https://gist.github.com/the-spyke/2de98b22ff4f978ebf0650c90e82027e#install"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
this guide
|
|
||||||
</a>{" "}
|
|
||||||
on how to switch to pipewire. <br />
|
|
||||||
You can still continue, however, please{" "}
|
|
||||||
<b>beware that you can only share audio of apps that are running under pipewire</b>.{" "}
|
|
||||||
<a onClick={() => setIgnorePulseWarning(true)}>I know what I'm doing!</a>
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in a new issue