From aa397d003cbb0e897020d36bc5375551c9f7cf5f Mon Sep 17 00:00:00 2001 From: AAGaming Date: Fri, 22 Dec 2023 08:56:11 -0500 Subject: [PATCH 1/3] SteamOS: add flatpak support using steam.pipe (#283) --- src/main/ipc.ts | 8 ++++-- src/main/utils/makeLinksOpenExternally.ts | 16 +++++++++-- src/main/utils/steamOS.ts | 33 ++++++++++++++--------- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 84755f4..504bd10 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -21,6 +21,7 @@ import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE, VENCORD_THEMES_DIR } from "./ import { mainWin } from "./mainWindow"; import { Settings } from "./settings"; import { handle, handleSync } from "./utils/ipcWrappers"; +import { isDeckGameMode, showGamePage } from "./utils/steamOS"; import { isValidVencordInstall } from "./utils/vencordLoader"; handleSync(IpcEvents.GET_VENCORD_PRELOAD_FILE, () => join(VENCORD_FILES_DIR, "vencordDesktopPreload.js")); @@ -47,11 +48,14 @@ handle(IpcEvents.SET_SETTINGS, (_, settings: typeof Settings.store, path?: strin Settings.setData(settings, path); }); -handle(IpcEvents.RELAUNCH, () => { +handle(IpcEvents.RELAUNCH, async () => { const options: RelaunchOptions = { args: process.argv.slice(1).concat(["--relaunch"]) }; - if (app.isPackaged && process.env.APPIMAGE) { + if (isDeckGameMode) { + // We can't properly relaunch when running under gamescope, but we can at least navigate to our page in Steam. + await showGamePage(); + } else if (app.isPackaged && process.env.APPIMAGE) { execFile(process.env.APPIMAGE, options.args); } else { app.relaunch(options); diff --git a/src/main/utils/makeLinksOpenExternally.ts b/src/main/utils/makeLinksOpenExternally.ts index b30b865..5197261 100644 --- a/src/main/utils/makeLinksOpenExternally.ts +++ b/src/main/utils/makeLinksOpenExternally.ts @@ -7,6 +7,7 @@ import { BrowserWindow, shell } from "electron"; import { Settings } from "../settings"; +import { execSteamURL, isDeckGameMode, steamOpenURL } from "./steamOS"; export function makeLinksOpenExternally(win: BrowserWindow) { win.webContents.setWindowOpenHandler(({ url }) => { @@ -30,9 +31,20 @@ export function makeLinksOpenExternally(win: BrowserWindow) { } // eslint-disable-next-line no-fallthrough case "mailto:": - case "steam:": case "spotify:": - shell.openExternal(url); + if (isDeckGameMode) { + steamOpenURL(url); + } else { + shell.openExternal(url); + } + break; + case "steam:": + if (isDeckGameMode) { + execSteamURL(url); + } else { + shell.openExternal(url); + } + break; } return { action: "deny" }; diff --git a/src/main/utils/steamOS.ts b/src/main/utils/steamOS.ts index feeafe4..44eadac 100644 --- a/src/main/utils/steamOS.ts +++ b/src/main/utils/steamOS.ts @@ -4,16 +4,13 @@ * Copyright (c) 2023 Vendicated and Vencord contributors */ -import { exec as callbackExec } from "child_process"; import { BrowserWindow, dialog } from "electron"; -import { sleep } from "shared/utils/sleep"; -import { promisify } from "util"; +import { writeFile } from "fs/promises"; +import { join } from "path"; import { MessageBoxChoice } from "../constants"; import { Settings } from "../settings"; -const exec = promisify(callbackExec); - // Bump this to re-show the prompt const layoutVersion = 2; // Get this from "show details" on the profile after exporting as a shared personal layout or using share with community @@ -42,16 +39,28 @@ function getAppId(): string | null { return null; } -async function execSteamURL(url: string): Promise { - await exec(`steam -ifrunning ${url}`); +export async function execSteamURL(url: string): Promise { + // This doesn't allow arbitrary execution despite the weird syntax. + await writeFile( + join(process.env.HOME || "/home/deck", ".steam", "steam.pipe"), + // replace ' to prevent argument injection + `'${process.env.HOME}/.local/share/Steam/ubuntu12_32/steam' '-ifrunning' '${url.replaceAll("'", "%27")}'\n`, + "utf-8" + ); +} + +export async function steamOpenURL(url: string) { + await execSteamURL(`steam://openurl/${url}`); +} + +export async function showGamePage() { + const appId = getAppId(); + if (!appId) return; + await execSteamURL(`steam://nav/games/details/${appId}`); } async function showLayout(appId: string) { - await execSteamURL(`steam://controllerconfig/${appId}/${layoutId}`); - // because the UI doesn't consistently reload after the data for the config has loaded... - // HOW HAS NOBODY AT VALVE RUN INTO THIS YET - await sleep(100); - await execSteamURL(`steam://controllerconfig/${appId}/${layoutId}`); + execSteamURL(`steam://controllerconfig/${appId}/${layoutId}`); } export async function askToApplySteamLayout(win: BrowserWindow) { From 1429815fd126d802235b1dba041d6509ea6cd312 Mon Sep 17 00:00:00 2001 From: V Date: Sun, 24 Dec 2023 00:15:26 +0100 Subject: [PATCH 2/3] README: remove outdated section (#303) --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 9c941dd..c63c163 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,3 @@ pnpm package:dir ## Motivation The official Discord Desktop app is very resource heavy compared to Discord in your Browser. There are multiple alternative Electron apps (ArmCord, WebCord, probably more) that prove how much of a performance gain you can gain by using a custom app. ArmCord already supports Vencord but makes it pretty limited for us. Making our own standalone app gives us much more control. - -This is just a random idea I (V) got, and might not actually ever be finished heh - -Gluon also seems very attractive for this because of how lightweight it can be and because unlike electron, streaming just works out of the box like in any chromium browser. However, at the time of writing this, it still lacks some features necessary to make it work (synchronous ipc or a way to get node process variables into the onLoad function for instance, plus onLoad seems to load a little too late sometimes) From dfc6970756074f3a9e65b6364b08824444b1b7d5 Mon Sep 17 00:00:00 2001 From: Lewis Crichton Date: Thu, 28 Dec 2023 00:38:31 +0000 Subject: [PATCH 3/3] fix(security): use promise queue for steam pipe (#300) this prevents an (unlikely) race condition where writing multiple large payloads to the pipe simultaneously could lead to jambled data => argument injection --- src/main/utils/steamOS.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/utils/steamOS.ts b/src/main/utils/steamOS.ts index 44eadac..8145e5a 100644 --- a/src/main/utils/steamOS.ts +++ b/src/main/utils/steamOS.ts @@ -17,6 +17,8 @@ const layoutVersion = 2; const layoutId = "3080264545"; // Vesktop Layout v2 const numberRegex = /^[0-9]*$/; +let steamPipeQueue = Promise.resolve(); + export const isDeckGameMode = process.env.SteamOS === "1" && process.env.SteamGamepadUI === "1"; export function applyDeckKeyboardFix() { @@ -39,18 +41,20 @@ function getAppId(): string | null { return null; } -export async function execSteamURL(url: string): Promise { +export function execSteamURL(url: string) { // This doesn't allow arbitrary execution despite the weird syntax. - await writeFile( - join(process.env.HOME || "/home/deck", ".steam", "steam.pipe"), - // replace ' to prevent argument injection - `'${process.env.HOME}/.local/share/Steam/ubuntu12_32/steam' '-ifrunning' '${url.replaceAll("'", "%27")}'\n`, - "utf-8" + steamPipeQueue = steamPipeQueue.then(() => + writeFile( + join(process.env.HOME || "/home/deck", ".steam", "steam.pipe"), + // replace ' to prevent argument injection + `'${process.env.HOME}/.local/share/Steam/ubuntu12_32/steam' '-ifrunning' '${url.replaceAll("'", "%27")}'\n`, + "utf-8" + ) ); } -export async function steamOpenURL(url: string) { - await execSteamURL(`steam://openurl/${url}`); +export function steamOpenURL(url: string) { + execSteamURL(`steam://openurl/${url}`); } export async function showGamePage() {