summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAAGaming <aa@mail.catvibers.me>2023-01-07 20:33:28 -0500
committerGitHub <noreply@github.com>2023-01-07 17:33:28 -0800
commitb72b32761058767d143e9ff08dc238c5ac9b777c (patch)
treeab3cb1d855a20c37355f1b0c650647cd733a1d5d
parentb8fdff80933b0164096c94358ab8123722906e0d (diff)
downloaddecky-loader-b72b32761058767d143e9ff08dc238c5ac9b777c.tar.gz
decky-loader-b72b32761058767d143e9ff08dc238c5ac9b777c.zip
Fix reloading UI on updates and restarting steam (#303)v2.4.9-pre1
-rw-r--r--backend/main.py18
-rw-r--r--frontend/rollup.config.js27
-rw-r--r--frontend/src/components/modals/filepicker/patches/library.ts42
-rw-r--r--frontend/src/plugin-loader.tsx8
-rw-r--r--frontend/src/steamfixes/README.md13
-rw-r--r--frontend/src/steamfixes/index.ts12
-rw-r--r--frontend/src/steamfixes/reload.ts14
-rw-r--r--frontend/src/steamfixes/restart.ts60
-rw-r--r--frontend/src/tabs-hook.tsx1
9 files changed, 151 insertions, 44 deletions
diff --git a/backend/main.py b/backend/main.py
index b6cab774..b99e7df1 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -168,15 +168,15 @@ class PluginManager:
async def inject_javascript(self, tab: Tab, first=False, request=None):
logger.info("Loading Decky frontend!")
try:
- # if first:
- # if await tab.has_global_var("deckyHasLoaded", False):
- # tabs = await get_tabs()
- # for t in tabs:
- # if t.title != "Steam" and t.title != "SP":
- # logger.debug("Closing tab: " + getattr(t, "title", "Untitled"))
- # await t.close()
- # await sleep(0.5)
- await tab.evaluate_js("try{if (window.deckyHasLoaded){setTimeout(() => SteamClient.User.StartShutdown(false), 100)}else{window.deckyHasLoaded = true;(async()=>{try{while(!window.SP_REACT){await new Promise(r => setTimeout(r, 10))};await import('http://localhost:1337/frontend/index.js')}catch(e){console.error(e)};})();}}catch(e){console.error(e)}", False, False, False)
+ if first:
+ if await tab.has_global_var("deckyHasLoaded", False):
+ tabs = await get_tabs()
+ for t in tabs:
+ if not t.title or (t.title != "Steam" and t.title != "SP"):
+ logger.debug("Closing tab: " + getattr(t, "title", "Untitled"))
+ await t.close()
+ await sleep(0.5)
+ await tab.evaluate_js("try{if (window.deckyHasLoaded){setTimeout(() => location.reload(), 100)}else{window.deckyHasLoaded = true;(async()=>{try{while(!window.SP_REACT){await new Promise(r => setTimeout(r, 10))};await import('http://localhost:1337/frontend/index.js')}catch(e){console.error(e)};})();}}catch(e){console.error(e)}", False, False, False)
except:
logger.info("Failed to inject JavaScript into tab\n" + format_exc())
pass
diff --git a/frontend/rollup.config.js b/frontend/rollup.config.js
index 00487d72..fc924c36 100644
--- a/frontend/rollup.config.js
+++ b/frontend/rollup.config.js
@@ -1,30 +1,27 @@
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import { nodeResolve } from '@rollup/plugin-node-resolve';
-import externalGlobals from "rollup-plugin-external-globals";
-import del from 'rollup-plugin-delete'
import replace from '@rollup/plugin-replace';
import typescript from '@rollup/plugin-typescript';
import { defineConfig } from 'rollup';
+import del from 'rollup-plugin-delete';
+import externalGlobals from 'rollup-plugin-external-globals';
-const hiddenWarnings = [
- "THIS_IS_UNDEFINED",
- "EVAL"
-];
+const hiddenWarnings = ['THIS_IS_UNDEFINED', 'EVAL'];
export default defineConfig({
input: 'src/index.tsx',
plugins: [
- del({ targets: "../backend/static/*", force: true }),
+ del({ targets: '../backend/static/*', force: true }),
commonjs(),
nodeResolve(),
externalGlobals({
react: 'SP_REACT',
'react-dom': 'SP_REACTDOM',
// hack to shut up react-markdown
- 'process': '{cwd: () => {}}',
- 'path': '{dirname: () => {}, join: () => {}, basename: () => {}, extname: () => {}}',
- 'url': '{fileURLToPath: (f) => f}'
+ process: '{cwd: () => {}}',
+ path: '{dirname: () => {}, join: () => {}, basename: () => {}, extname: () => {}}',
+ url: '{fileURLToPath: (f) => f}',
}),
typescript(),
json(),
@@ -38,11 +35,11 @@ export default defineConfig({
dir: '../backend/static',
format: 'esm',
chunkFileNames: (chunkInfo) => {
- return 'chunk-[hash].js'
- }
+ return 'chunk-[hash].js';
+ },
},
- onwarn: function ( message, handleWarning ) {
- if (hiddenWarnings.some(warning => message.code === warning)) return;
+ onwarn: function (message, handleWarning) {
+ if (hiddenWarnings.some((warning) => message.code === warning)) return;
handleWarning(message);
- }
+ },
});
diff --git a/frontend/src/components/modals/filepicker/patches/library.ts b/frontend/src/components/modals/filepicker/patches/library.ts
index c9c7d53c..3abf824b 100644
--- a/frontend/src/components/modals/filepicker/patches/library.ts
+++ b/frontend/src/components/modals/filepicker/patches/library.ts
@@ -1,4 +1,8 @@
-import { Patch, findModuleChild, replacePatch } from 'decky-frontend-lib';
+import { Patch, findModuleChild, replacePatch, sleep } from 'decky-frontend-lib';
+
+import Logger from '../../../../logger';
+
+const logger = new Logger('LibraryPatch');
declare global {
interface Window {
@@ -10,36 +14,44 @@ declare global {
let patch: Patch;
function rePatch() {
- // If you patch anything on SteamClient within the first few seconds of the client having loaded it will get redefined for some reason, so repatch any of these changes that occur within the first 20s of the last patch
+ // If you patch anything on SteamClient within the first few seconds of the client having loaded it will get redefined for some reason, so repatch any of these changes that occur with History.listen or an interval
patch = replacePatch(window.SteamClient.Apps, 'PromptToChangeShortcut', async ([appid]: number[]) => {
try {
const details = window.appDetailsStore.GetAppDetails(appid);
- console.log(details);
+ logger.debug('game details', details);
// strShortcutStartDir
const file = await window.DeckyPluginLoader.openFilePicker(details.strShortcutStartDir.replaceAll('"', ''));
- console.log('user selected', file);
+ logger.debug('user selected', file);
window.SteamClient.Apps.SetShortcutExe(appid, JSON.stringify(file.path));
const pathArr = file.path.split('/');
pathArr.pop();
const folder = pathArr.join('/');
window.SteamClient.Apps.SetShortcutStartDir(appid, JSON.stringify(folder));
} catch (e) {
- console.error(e);
+ logger.error(e);
}
});
}
-// TODO type and add to frontend-lib
-const History = findModuleChild((m) => {
- if (typeof m !== 'object') return undefined;
- for (let prop in m) {
- if (m[prop]?.m_history) return m[prop].m_history;
- }
-});
-
export default async function libraryPatch() {
try {
rePatch();
+ // TODO type and add to frontend-lib
+ let History: any;
+
+ while (!History) {
+ History = findModuleChild((m) => {
+ if (typeof m !== 'object') return undefined;
+ for (let prop in m) {
+ if (m[prop]?.m_history) return m[prop].m_history;
+ }
+ });
+ if (!History) {
+ logger.debug('Waiting 5s for history to become available.');
+ await sleep(5000);
+ }
+ }
+
const unlisten = History.listen(() => {
if (window.SteamClient.Apps.PromptToChangeShortcut !== patch.patchedFunction) {
rePatch();
@@ -47,11 +59,11 @@ export default async function libraryPatch() {
});
return () => {
- patch.unpatch();
unlisten();
+ patch.unpatch();
};
} catch (e) {
- console.error('Error patching library file picker', e);
+ logger.error('Error patching library file picker', e);
}
return () => {};
}
diff --git a/frontend/src/plugin-loader.tsx b/frontend/src/plugin-loader.tsx
index 381d7954..c37e168c 100644
--- a/frontend/src/plugin-loader.tsx
+++ b/frontend/src/plugin-loader.tsx
@@ -21,6 +21,7 @@ import WithSuspense from './components/WithSuspense';
import Logger from './logger';
import { Plugin } from './plugin';
import RouterHook from './router-hook';
+import { deinitSteamFixes, initSteamFixes } from './steamfixes';
import { checkForUpdates } from './store';
import TabsHook from './tabs-hook';
import OldTabsHook from './tabs-hook.old';
@@ -33,10 +34,6 @@ const SettingsPage = lazy(() => import('./components/settings'));
const FilePicker = lazy(() => import('./components/modals/filepicker'));
-declare global {
- interface Window {}
-}
-
class PluginLoader extends Logger {
private plugins: Plugin[] = [];
private tabsHook: TabsHook | OldTabsHook = document.title == 'SP' ? new OldTabsHook() : new TabsHook();
@@ -92,6 +89,8 @@ class PluginLoader extends Logger {
);
});
+ initSteamFixes();
+
initFilepickerPatches();
this.updateVersion();
@@ -184,6 +183,7 @@ class PluginLoader extends Logger {
public deinit() {
this.routerHook.removeRoute('/decky/store');
this.routerHook.removeRoute('/decky/settings');
+ deinitSteamFixes();
deinitFilepickerPatches();
this.focusWorkaroundPatch?.unpatch();
}
diff --git a/frontend/src/steamfixes/README.md b/frontend/src/steamfixes/README.md
new file mode 100644
index 00000000..97098889
--- /dev/null
+++ b/frontend/src/steamfixes/README.md
@@ -0,0 +1,13 @@
+## What's this?
+
+`steamfixes` contains various fixes and workaround for things Valve has broken that cause Decky issues.
+
+## Current fixes:
+
+- StartRestart() -> StartShutdown(false) override:
+
+ StartRestart() breaks CEF debugging, StartShutdown(false) doesn't. We can safely replace StartRestart() with StartShutdown(false) as gamescope-session will automatically restart the steam client anyway if it shuts down, bypassing the broken restart codepath. Added 12/29/2022
+
+- ExecuteSteamURL UI reload fix:
+
+ Starting sometime in November 2022, Valve broke reloading the Steam UI pages via location.reload, as it won't properly start the UI. We can manually trigger UI startup if we detect no active input contexts by calling `SteamClient.URL.ExecuteSteamURL("steam://open/settings/")` Added 12/29/2022
diff --git a/frontend/src/steamfixes/index.ts b/frontend/src/steamfixes/index.ts
new file mode 100644
index 00000000..988f3bd7
--- /dev/null
+++ b/frontend/src/steamfixes/index.ts
@@ -0,0 +1,12 @@
+import reloadFix from './reload';
+import restartFix from './restart';
+let fixes: Function[] = [];
+
+export function deinitSteamFixes() {
+ fixes.forEach((deinit) => deinit());
+}
+
+export async function initSteamFixes() {
+ fixes.push(reloadFix());
+ fixes.push(await restartFix());
+}
diff --git a/frontend/src/steamfixes/reload.ts b/frontend/src/steamfixes/reload.ts
new file mode 100644
index 00000000..e31f78fc
--- /dev/null
+++ b/frontend/src/steamfixes/reload.ts
@@ -0,0 +1,14 @@
+import Logger from '../logger';
+
+const logger = new Logger('ReloadSteamFix');
+
+export default function reloadFix() {
+ // Hack to unbreak the ui when reloading it
+ if (window.FocusNavController?.m_rgAllContexts?.length == 0) {
+ SteamClient.URL.ExecuteSteamURL('steam://open/settings');
+ logger.log('Applied UI reload fix.');
+ }
+
+ // This steamfix does not need to deinit.
+ return () => {};
+}
diff --git a/frontend/src/steamfixes/restart.ts b/frontend/src/steamfixes/restart.ts
new file mode 100644
index 00000000..467efd6a
--- /dev/null
+++ b/frontend/src/steamfixes/restart.ts
@@ -0,0 +1,60 @@
+import { Patch, findModuleChild, replacePatch, sleep } from 'decky-frontend-lib';
+
+import Logger from '../logger';
+
+const logger = new Logger('RestartSteamFix');
+
+declare global {
+ interface Window {
+ SteamClient: any;
+ appDetailsStore: any;
+ }
+}
+
+let patch: Patch;
+
+function rePatch() {
+ // If you patch anything on SteamClient within the first few seconds of the client having loaded it will get redefined for some reason, so repatch any of these changes that occur with History.listen or an interval
+ patch = replacePatch(window.SteamClient.User, 'StartRestart', () => SteamClient.User.StartShutdown(false));
+}
+
+export default async function restartFix() {
+ try {
+ rePatch();
+ // TODO type and add to frontend-lib
+ let History: any;
+
+ while (!History) {
+ History = findModuleChild((m) => {
+ if (typeof m !== 'object') return undefined;
+ for (let prop in m) {
+ if (m[prop]?.m_history) return m[prop].m_history;
+ }
+ });
+ if (!History) {
+ logger.debug('Waiting 5s for history to become available.');
+ await sleep(5000);
+ }
+ }
+
+ function repatchIfNeeded() {
+ if (window.SteamClient.User.StartRestart !== patch.patchedFunction) {
+ rePatch();
+ }
+ }
+
+ const unlisten = History.listen(repatchIfNeeded);
+
+ // Just in case
+ setTimeout(repatchIfNeeded, 5000);
+ setTimeout(repatchIfNeeded, 10000);
+
+ return () => {
+ unlisten();
+ patch.unpatch();
+ };
+ } catch (e) {
+ logger.error('Error patching StartRestart', e);
+ }
+ return () => {};
+}
diff --git a/frontend/src/tabs-hook.tsx b/frontend/src/tabs-hook.tsx
index 00adf206..d9c76ca6 100644
--- a/frontend/src/tabs-hook.tsx
+++ b/frontend/src/tabs-hook.tsx
@@ -7,7 +7,6 @@ import Logger from './logger';
declare global {
interface Window {
__TABS_HOOK_INSTANCE: any;
- securitystore: any;
}
}