diff --git a/src/native/window.ts b/src/native/window.ts index b73698a..d1b3e05 100644 --- a/src/native/window.ts +++ b/src/native/window.ts @@ -5,8 +5,10 @@ import { Menu, MenuItem, app, + desktopCapturer, ipcMain, nativeImage, + session, } from "electron"; import windowIconAsset from "../../assets/desktop/icon.png?asset"; @@ -187,6 +189,59 @@ export function createMainWindow() { } }); + // Create display media request handler + session.defaultSession.setDisplayMediaRequestHandler( + (request, callback) => { + desktopCapturer + .getSources({ types: ["screen", "window"], fetchWindowIcons: true }) + .then((sources) => { + // Shortcut for linux wayland. + if (sources.length == 1) { + // TODO: Get audio to work with wayland + // See vencord for an implementation using a virtual microphone. + callback({ + video: sources[0], + audio: request.audioRequested ? "loopbackWithMute" : undefined, + }); + return; + } + ipcMain.once( + "screenPickerCallback", + (_, idx: number, audio: boolean) => { + if (idx < 0 || idx > sources.length) { + callback({}); + } else { + callback({ + video: sources[idx], + audio: audio ? "loopbackWithMute" : undefined, + }); + } + }, + ); + mainWindow.webContents.send( + "screenPicker", + sources.map((source, idx) => { + const image = source.appIcon; + if (image) { + if (image.getAspectRatio() > 1) { + image.resize({ width: 256 }); + } else { + image.resize({ height: 256 }); + } + } + return { + idx: idx, + name: source.name, + isFullScreen: source.id.startsWith("screen"), + image: image?.toDataURL(), + }; + }), + ); + }); + }, + { useSystemPicker: true }, + ); + // push world events to the window ipcMain.on("minimise", () => mainWindow.minimize()); ipcMain.on("maximise", () => diff --git a/src/world/window.ts b/src/world/window.ts index 2e9957b..9995b6d 100644 --- a/src/world/window.ts +++ b/src/world/window.ts @@ -15,4 +15,21 @@ contextBridge.exposeInMainWorld("native", { close: () => ipcRenderer.send("close"), setBadgeCount: (count: number) => ipcRenderer.send("setBadgeCount", count), + + onceScreenPicker: ( + onScreenPick: ( + sources: { + idx: number; + name: string; + isFullScreen: boolean; + image?: string; + }[], + ) => void, + ) => { + const eventName = "screenPicker"; + ipcRenderer.removeAllListeners(eventName); + ipcRenderer.once(eventName, (_, sources) => onScreenPick(sources)); + }, + screenPickerCallback: (idx: number, audio: boolean) => + ipcRenderer.send("screenPickerCallback", idx, audio), });