131 lines
4.8 KiB
Diff
131 lines
4.8 KiB
Diff
--- a/src/main/screenShare.ts
|
|
+++ b/src/main/screenShare.ts
|
|
@@ -4,7 +4,9 @@
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*/
|
|
|
|
-import { desktopCapturer, session, Streams } from "electron";
|
|
+import { app, desktopCapturer, session, Streams } from "electron";
|
|
+import { writeFileSync } from "fs";
|
|
+import { join } from "path";
|
|
import type { StreamPick } from "renderer/components/ScreenSharePicker";
|
|
import { IpcCommands, IpcEvents } from "shared/IpcEvents";
|
|
|
|
@@ -14,7 +16,31 @@
|
|
const isWayland =
|
|
process.platform === "linux" && (process.env.XDG_SESSION_TYPE === "wayland" || !!process.env.WAYLAND_DISPLAY);
|
|
|
|
+// renderer's PICK_ANOTHER ("Pick Another Window" in the stream dialog)
|
|
+const PICK_ANOTHER = "vesktop-pick-another";
|
|
+
|
|
+// FredOS: the XDPH custom picker (settings/hyprland.nix) auto-picks a running
|
|
+// game unless this file holds a fresh timestamp, then shows the full picker.
|
|
+function forceFullPicker() {
|
|
+ try {
|
|
+ writeFileSync(
|
|
+ join(process.env.XDG_RUNTIME_DIR ?? "/tmp", "xdph-pick-manually"),
|
|
+ String(Math.floor(Date.now() / 1000))
|
|
+ );
|
|
+ } catch {}
|
|
+}
|
|
+
|
|
+// Shift held while clicking share = full picker
|
|
+let shiftDown = false;
|
|
+
|
|
export function registerScreenShareHandler() {
|
|
+ app.on("browser-window-created", (_, win) => {
|
|
+ win.webContents.on("before-input-event", (_, input) => {
|
|
+ if (input.key === "Shift") shiftDown = input.type === "keyDown";
|
|
+ });
|
|
+ win.on("blur", () => (shiftDown = false));
|
|
+ });
|
|
+
|
|
handle(IpcEvents.CAPTURER_GET_LARGE_THUMBNAIL, async (_, id: string) => {
|
|
const sources = await desktopCapturer.getSources({
|
|
types: ["window", "screen"],
|
|
@@ -29,15 +55,19 @@
|
|
session.defaultSession.setDisplayMediaRequestHandler(async (request, callback) => {
|
|
// request full resolution on wayland right away because we always only end up with one result anyway
|
|
const width = isWayland ? 1920 : 176;
|
|
- const sources = await desktopCapturer
|
|
- .getSources({
|
|
- types: ["window", "screen"],
|
|
- thumbnailSize: {
|
|
- width,
|
|
- height: width * (9 / 16)
|
|
- }
|
|
- })
|
|
- .catch(err => console.error("Error during screenshare picker", err));
|
|
+ const getSources = () =>
|
|
+ desktopCapturer
|
|
+ .getSources({
|
|
+ types: ["window", "screen"],
|
|
+ thumbnailSize: {
|
|
+ width,
|
|
+ height: width * (9 / 16)
|
|
+ }
|
|
+ })
|
|
+ .catch(err => console.error("Error during screenshare picker", err));
|
|
+
|
|
+ if (isWayland && shiftDown) forceFullPicker();
|
|
+ let sources = await getSources();
|
|
|
|
if (!sources) return callback({});
|
|
|
|
@@ -48,14 +78,22 @@
|
|
}));
|
|
|
|
if (isWayland) {
|
|
- const video = data[0];
|
|
- if (video) {
|
|
+ let video = data[0];
|
|
+ while (video) {
|
|
const stream = await sendRendererCommand<StreamPick>(IpcCommands.SCREEN_SHARE_PICKER, {
|
|
screens: [video],
|
|
skipPicker: true
|
|
}).catch(() => null);
|
|
|
|
if (stream === null) return callback({});
|
|
+ if (stream.id !== PICK_ANOTHER) break;
|
|
+
|
|
+ // re-run the portal, this time with the full picker
|
|
+ forceFullPicker();
|
|
+ sources = await getSources();
|
|
+ if (!sources) return callback({});
|
|
+ const [source] = sources;
|
|
+ video = source && { id: source.id, name: source.name, url: source.thumbnail.toDataURL() };
|
|
}
|
|
|
|
callback(video ? { video: sources[0] } : {});
|
|
--- a/src/renderer/components/ScreenSharePicker.tsx
|
|
+++ b/src/renderer/components/ScreenSharePicker.tsx
|
|
@@ -82,6 +82,10 @@
|
|
url: string;
|
|
}
|
|
|
|
+// Returned instead of a source id when "Pick Another Window" is clicked; main
|
|
+// re-runs the portal with the full picker (keep in sync with main/screenShare.ts).
|
|
+export const PICK_ANOTHER = "vesktop-pick-another";
|
|
+
|
|
export let currentSettings: StreamSettings | null = null;
|
|
|
|
const logger = new Logger("VesktopScreenShare");
|
|
@@ -829,6 +833,18 @@
|
|
Go Live
|
|
</Button>
|
|
|
|
+ {skipPicker && (
|
|
+ <Button
|
|
+ variant="secondary"
|
|
+ onClick={() => {
|
|
+ submit({ id: PICK_ANOTHER, audio: false });
|
|
+ close();
|
|
+ }}
|
|
+ >
|
|
+ Pick Another Window
|
|
+ </Button>
|
|
+ )}
|
|
+
|
|
{selected && !skipPicker ? (
|
|
<Button variant="secondary" onClick={() => setSelected(void 0)}>
|
|
Back
|