From 1e9c70eed9c16f8102a4b8086311ef4a2e907c3f Mon Sep 17 00:00:00 2001 From: AAGaming Date: Tue, 31 Oct 2023 17:14:30 -0400 Subject: [PATCH] SteamOS: fixes & official controller layout (#194) --- package.json | 2 + pnpm-lock.yaml | 9 +++++ src/main/constants.ts | 5 +++ src/main/mainWindow.ts | 30 +++++++++----- src/main/utils/steamOS.ts | 84 +++++++++++++++++++++++++++++++++++++++ src/shared/settings.d.ts | 2 + src/shared/utils/sleep.ts | 9 +++++ 7 files changed, 131 insertions(+), 10 deletions(-) create mode 100644 src/main/utils/steamOS.ts create mode 100644 src/shared/utils/sleep.ts diff --git a/package.json b/package.json index 8425c261..09dd1a84 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "dotenv": "^16.3.1", "electron": "^27.0.0", "electron-builder": "^24.6.4", + "electron-builder-sandbox-fix": "^1.0.10", "esbuild": "^0.19.4", "eslint": "^8.51.0", "eslint-config-prettier": "^9.0.0", @@ -61,6 +62,7 @@ "build": { "appId": "dev.vencord.desktop", "productName": "Vesktop", + "afterPack": "electron-builder-sandbox-fix", "files": [ "!*", "dist/js", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a527680a..75bec413 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,6 +42,9 @@ devDependencies: electron-builder: specifier: ^24.6.4 version: 24.6.4 + electron-builder-sandbox-fix: + specifier: ^1.0.10 + version: 1.0.10 esbuild: specifier: ^0.19.4 version: 0.19.4 @@ -1822,6 +1825,12 @@ packages: jake: 10.8.7 dev: true + /electron-builder-sandbox-fix@1.0.10: + resolution: {integrity: sha512-fxZD8etQFXkXOlxe/nYEbPsDXcKJHhadxzLyXq7IEucELPE0GK8jYnNt1y577FsfRQvuhOAd4oZNdIdICVi5nQ==} + dependencies: + chalk: 4.1.2 + dev: true + /electron-builder@24.6.4: resolution: {integrity: sha512-uNWQoU7pE7qOaIQ6CJHpBi44RJFVG8OHRBIadUxrsDJVwLLo8Nma3K/EEtx5/UyWAQYdcK4nVPYKoRqBb20hbA==} engines: {node: '>=14.0.0'} diff --git a/src/main/constants.ts b/src/main/constants.ts index 4ba59d3d..18468a14 100644 --- a/src/main/constants.ts +++ b/src/main/constants.ts @@ -34,3 +34,8 @@ const UserAgents = { }; export const UserAgent = UserAgents[process.platform] || UserAgents.windows; + +export const enum MessageBoxChoice { + Default, + Cancel +} diff --git a/src/main/mainWindow.ts b/src/main/mainWindow.ts index 7cbaa87e..8fdb6ee4 100644 --- a/src/main/mainWindow.ts +++ b/src/main/mainWindow.ts @@ -28,6 +28,7 @@ import { DATA_DIR, DEFAULT_HEIGHT, DEFAULT_WIDTH, + MessageBoxChoice, MIN_HEIGHT, MIN_WIDTH, UserAgent, @@ -36,11 +37,14 @@ import { import { Settings, VencordSettings } from "./settings"; import { createSplashWindow } from "./splash"; import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally"; +import { applyDeckKeyboardFix, askToApplySteamLayout, isDeckGameMode } from "./utils/steamOS"; import { downloadVencordFiles, ensureVencordFiles } from "./utils/vencordLoader"; let isQuitting = false; let tray: Tray; +applyDeckKeyboardFix(); + app.on("before-quit", () => { isQuitting = true; }); @@ -128,11 +132,6 @@ function initTray(win: BrowserWindow) { }); } -const enum MessageBoxChoice { - Default, - Cancel -} - async function clearData(win: BrowserWindow) { const { response } = await dialog.showMessageBox(win, { message: "Are you sure you want to reset Vesktop?", @@ -266,6 +265,9 @@ function initMenuBar(win: BrowserWindow) { } function getWindowBoundsOptions(): BrowserWindowConstructorOptions { + // We want the default window behaivour to apply in game mode since it expects everything to be fullscreen and maximized. + if (isDeckGameMode) return {}; + const { x, y, width, height } = Settings.store.windowBounds ?? {}; const options = { @@ -405,7 +407,7 @@ function createMainWindow() { win.setMenuBarVisibility(false); win.on("close", e => { - const useTray = Settings.store.minimizeToTray !== false && Settings.store.tray !== false; + const useTray = !isDeckGameMode && Settings.store.minimizeToTray !== false && Settings.store.tray !== false; if (isQuitting || (process.platform !== "darwin" && !useTray)) return; e.preventDefault(); @@ -419,7 +421,7 @@ function createMainWindow() { if (Settings.store.staticTitle) win.on("page-title-updated", e => e.preventDefault()); initWindowBoundsListeners(win); - if ((Settings.store.tray ?? true) && process.platform !== "darwin") initTray(win); + if (!isDeckGameMode && (Settings.store.tray ?? true) && process.platform !== "darwin") initTray(win); initMenuBar(win); makeLinksOpenExternally(win); initSettingsListeners(win); @@ -441,19 +443,27 @@ const runVencordMain = once(() => require(join(VENCORD_FILES_DIR, "vencordDeskto export async function createWindows() { const splash = createSplashWindow(); - + // SteamOS letterboxes and scales it terribly, so just full screen it + if (isDeckGameMode) splash.setFullScreen(true); await ensureVencordFiles(); runVencordMain(); mainWin = createMainWindow(); - mainWin.once("ready-to-show", () => { + mainWin.webContents.on("did-finish-load", () => { splash.destroy(); mainWin!.show(); - if (Settings.store.maximized) { + if (Settings.store.maximized && !isDeckGameMode) { mainWin!.maximize(); } + + if (isDeckGameMode) { + // always use entire display + mainWin!.setFullScreen(true); + + askToApplySteamLayout(mainWin); + } }); initArRPC(); diff --git a/src/main/utils/steamOS.ts b/src/main/utils/steamOS.ts new file mode 100644 index 00000000..5f99139e --- /dev/null +++ b/src/main/utils/steamOS.ts @@ -0,0 +1,84 @@ +/* + * SPDX-License-Identifier: GPL-3.0 + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2023 Vendicated and Vencord contributors + */ + +import { exec as callbackExec } from "child_process"; +import { BrowserWindow, dialog } from "electron"; +import { promisify } from "util"; + +import { sleep } from "../../shared/utils/sleep"; +import { MessageBoxChoice } from "../constants"; +import { Settings } from "../settings"; + +const exec = promisify(callbackExec); + +// Bump this to re-show the prompt +const layoutVersion = 1; +// Get this from "show details" on the profile after exporting as a shared personal layout or using share with community +const layoutId = "3063409873"; // Vesktop Layout v1 +const numberRegex = /^[0-9]*$/; + +export const isDeckGameMode = process.env.SteamOS === "1" && process.env.SteamGamepadUI === "1"; + +export function applyDeckKeyboardFix() { + if (!isDeckGameMode) return; + // Prevent constant virtual keyboard spam that eventually crashes Steam. + process.env.GTK_IM_MODULE = "None"; +} + +// For some reason SteamAppId is always 0 for non-steam apps so we do this insanity instead. +function getAppId(): string | null { + // /home/deck/.local/share/Steam/steamapps/shadercache/APPID/fozmediav1 + const path = process.env.STEAM_COMPAT_MEDIA_PATH; + if (!path) return null; + const pathElems = path?.split("/"); + const appId = pathElems[pathElems.length - 2]; + if (appId.match(numberRegex)) { + console.log(`Got Steam App ID ${appId}`); + return appId; + } + return null; +} + +async function execSteamURL(url: string): Promise { + await exec(`steam -ifrunning ${url}`); +} + +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(300); + await execSteamURL(`steam://controllerconfig/${appId}/${layoutId}`); +} + +export async function askToApplySteamLayout(win: BrowserWindow) { + const appId = getAppId(); + if (!appId) return; + if (Settings.store.steamOSLayoutVersion === layoutVersion) return; + const update = Boolean(Settings.store.steamOSLayoutVersion); + + // Touch screen breaks in some menus when native touch mode is enabled on latest SteamOS beta, remove most of the update specific text once that's fixed. + const { response } = await dialog.showMessageBox(win, { + message: `${update ? "Update" : "Apply"} Vesktop Steam Input Layout?`, + detail: `Would you like to ${update ? "Update" : "Apply"} Vesktop's recommended Steam Deck controller settings? +${update ? "Click yes using the touchpad" : "Tap yes"}, then press the X button or tap Apply Layout to confirm.${ + update ? " Doing so will undo any customizations you have made." : "" + } +${update ? "Click" : "Tap"} no to keep your current layout.`, + buttons: ["Yes", "No"], + cancelId: MessageBoxChoice.Cancel, + defaultId: MessageBoxChoice.Default, + type: "question" + }); + + if (Settings.store.steamOSLayoutVersion !== layoutVersion) { + Settings.store.steamOSLayoutVersion = layoutVersion; + } + + if (response === MessageBoxChoice.Cancel) return; + + await showLayout(appId); +} diff --git a/src/shared/settings.d.ts b/src/shared/settings.d.ts index 2820a446..5b5fd4c3 100644 --- a/src/shared/settings.d.ts +++ b/src/shared/settings.d.ts @@ -31,4 +31,6 @@ export interface Settings { splashTheming?: boolean; splashColor?: string; splashBackground?: string; + + steamOSLayoutVersion?: number; } diff --git a/src/shared/utils/sleep.ts b/src/shared/utils/sleep.ts new file mode 100644 index 00000000..948c5a33 --- /dev/null +++ b/src/shared/utils/sleep.ts @@ -0,0 +1,9 @@ +/* + * SPDX-License-Identifier: GPL-3.0 + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2023 Vendicated and Vencord contributors + */ + +export function sleep(ms: number): Promise { + return new Promise(r => setTimeout(r, ms)); +}