From 2eeb29ac80fcfdf59a8b600185055e935f0304b3 Mon Sep 17 00:00:00 2001 From: rope Date: Mon, 12 Oct 2026 20:19:45 +0100 Subject: [PATCH] Vesktop: Pick Another Window button + shift-click for full picker Co-Authored-By: Claude Opus 5.5 --- hosts/FredOS-Gaming.nix | 8 +- patches/vesktop-cancel-shows-picker.patch | 30 ----- patches/vesktop-pick-another-window.patch | 131 ++++++++++++++++++++++ settings/hyprland.nix | 10 +- 4 files changed, 140 insertions(+), 39 deletions(-) delete mode 100644 patches/vesktop-cancel-shows-picker.patch create mode 100644 patches/vesktop-pick-another-window.patch diff --git a/hosts/FredOS-Gaming.nix b/hosts/FredOS-Gaming.nix index 265661c..8c71b2c 100644 --- a/hosts/FredOS-Gaming.nix +++ b/hosts/FredOS-Gaming.nix @@ -7,13 +7,13 @@ gamescope spotify # Preselect the last stream's audio source (e.g. Hearthstone) when it's - # running again, instead of resetting to "None" every Go Live; Cancel in - # the stream dialog makes the next share show the full window picker - # (see xdph.conf in settings/hyprland.nix). + # running again, instead of resetting to "None" every Go Live. + # "Pick Another Window" in the stream dialog or shift-clicking share + # bypasses the game auto-pick (see xdph.conf in settings/hyprland.nix). (vesktop.overrideAttrs (old: { patches = (old.patches or [ ]) ++ [ ../patches/vesktop-remember-stream-audio.patch - ../patches/vesktop-cancel-shows-picker.patch + ../patches/vesktop-pick-another-window.patch ]; })) lsfg-vk diff --git a/patches/vesktop-cancel-shows-picker.patch b/patches/vesktop-cancel-shows-picker.patch deleted file mode 100644 index 330e708..0000000 --- a/patches/vesktop-cancel-shows-picker.patch +++ /dev/null @@ -1,30 +0,0 @@ ---- a/src/main/screenShare.ts -+++ b/src/main/screenShare.ts -@@ -5,6 +5,8 @@ - */ - - import { 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"; - -@@ -55,7 +57,17 @@ - skipPicker: true - }).catch(() => null); - -- if (stream === null) return callback({}); -+ if (stream === null) { -+ // Cancelled: tell the auto-picking XDPH picker (FredOS settings/hyprland.nix) -+ // to show the full picker on the next share. -+ try { -+ writeFileSync( -+ join(process.env.XDG_RUNTIME_DIR ?? "/tmp", "xdph-pick-manually"), -+ String(Math.floor(Date.now() / 1000)) -+ ); -+ } catch {} -+ return callback({}); -+ } - } - - callback(video ? { video: sources[0] } : {}); diff --git a/patches/vesktop-pick-another-window.patch b/patches/vesktop-pick-another-window.patch new file mode 100644 index 0000000..639b753 --- /dev/null +++ b/patches/vesktop-pick-another-window.patch @@ -0,0 +1,131 @@ +--- 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(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 + + ++ {skipPicker && ( ++ ++ )} ++ + {selected && !skipPicker ? ( +