mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-06-25 20:49:12 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b72b327610 | |||
| b8fdff8093 | |||
| 880b4c2f8f | |||
| 34af340009 | |||
| 80b6115f6f | |||
| 3bed83697e | |||
| 0ffef6e4bf |
@@ -1,36 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Description**
|
||||
[A clear and concise description of what the bug is.]
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
[A clear and concise description of what you expected to happen.]
|
||||
|
||||
**Screenshots**
|
||||
[If applicable, add screenshots to help explain your problem.]
|
||||
|
||||
**Version information**
|
||||
- SteamOS Version: ``[Run ``uname -a`` and place the output here. Leave the single quotations outside.]``
|
||||
- Selected Update Channel: [Stable, Beta or Preview.]
|
||||
|
||||
**Logs**
|
||||
[Please reboot your deck (if possible) when attempting to recreate the issue, then run
|
||||
``cd ~ && journalctl -b0 -u plugin_loader.service > backendlog.txt``. This will save the log file to ``~`` aka ``/home/deck``. Please upload the file here in place of this textblock.]
|
||||
|
||||
**Additional context**
|
||||
Have you modified the read-only filesystem at any point?
|
||||
[Yes or No.]
|
||||
@@ -0,0 +1,73 @@
|
||||
name: Bug report
|
||||
description: File a bug/issue
|
||||
title: "[BUG] <title>"
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: low-effort-checks
|
||||
attributes:
|
||||
label: Please confirm
|
||||
description: Issues without all checks may be ignored/closed.
|
||||
options:
|
||||
- label: I have searched existing issues
|
||||
- label: This issue is not a duplicate of an existing one
|
||||
- label: I have checked the [common issues section in the readme file](https://github.com/SteamDeckHomebrew/decky-loader#-common-issues)
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Bug Report Description
|
||||
description: A clear and concise description of what the bug is and if possible, the steps you used to get to the bug. If appropriate, include screenshots or videos.
|
||||
placeholder: |
|
||||
When I try to use ...
|
||||
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Behaviour
|
||||
description: A brief description of the expected behavior.
|
||||
placeholder: It should be ...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: SteamOS version
|
||||
# description: Can be found with `uname -a`
|
||||
# placeholder: "Linux steamdeck 5.13.0-valve36-1-neptune #1 SMP PREEMPT Mon, 19 Dec 2022 23:39:41 +0000 x86_64 GNU/Linux"
|
||||
placeholder: "SteamOS 3.4.3 Stable"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Selected Update Channel
|
||||
description: Which branch of Decky are you on?
|
||||
multiple: false
|
||||
options:
|
||||
- Stable
|
||||
- Prerelease
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Have you modified the read-only filesystem at any point?
|
||||
description: Describe how here, if you haven't done anything you can leave this blank
|
||||
placeholder: Yes, I've installed neofetch via pacman.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Logs
|
||||
description: Please reboot your deck (if possible) when attempting to recreate the issue, then run ``cd ~ && journalctl -b0 -u plugin_loader.service > deckylog.txt``. This will save the log file to ``~`` aka ``/home/deck``. Please upload the file here
|
||||
placeholder: deckylog.txt
|
||||
validations:
|
||||
required: false
|
||||
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Steam Deck Homebrew Discord Server
|
||||
url: https://discord.gg/ZU74G2NJzk
|
||||
about: Please ask and answer questions here.
|
||||
@@ -0,0 +1,35 @@
|
||||
name: Feature request
|
||||
description: Request a new feature (NOT A PLUGIN)
|
||||
title: "[Request] <title>"
|
||||
labels: [feature request]
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: low-effort-checks
|
||||
attributes:
|
||||
label: Please confirm
|
||||
description: Issues without all checks may be ignored/closed.
|
||||
options:
|
||||
- label: I have searched existing issues
|
||||
- label: This issue is not a duplicate of an existing one
|
||||
- label: This is not a request for a plugin
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Feature Request Description
|
||||
description: A clear and concise description of what the new feature.
|
||||
placeholder: |
|
||||
Decky plugins should be sortable in the quick access menu
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Further Description
|
||||
description: A further explanation of the feature. If appropriate, include screenshots or videos.
|
||||
placeholder: |
|
||||
This would help make the UI clearer and easier to use as there is less clutter in the QAM.
|
||||
It would also make it faster to access plugins that are used more.
|
||||
|
||||
This could be implemented by adding ...
|
||||
validations:
|
||||
required: false
|
||||
+1
-1
@@ -396,7 +396,7 @@ async def get_tab_lambda(test) -> Tab:
|
||||
|
||||
async def get_gamepadui_tab() -> Tab:
|
||||
tabs = await get_tabs()
|
||||
tab = next((i for i in tabs if ("https://steamloopback.host/routes/" in i.url and (i.title == "Steam" or i.title == "SP"))), None)
|
||||
tab = next((i for i in tabs if ("https://steamloopback.host/routes/" in i.url and (i.title == "Steam Shared Context presented by Valve™" or i.title == "Steam" or i.title == "SP"))), None)
|
||||
if not tab:
|
||||
raise ValueError(f"GamepadUI Tab not found")
|
||||
return tab
|
||||
|
||||
+9
-9
@@ -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
|
||||
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env xdg-open
|
||||
[Desktop Entry]
|
||||
Name=Install Decky
|
||||
Exec=sh -c 'curl -O --output-dir /tmp/ https://raw.githubusercontent.com/SteamDeckHomebrew/decky-loader/main/dist/user_install_script.sh 2> /dev/null && bash /tmp/user_install_script.sh'
|
||||
Icon=steamdeck-gaming-return
|
||||
Terminal=true
|
||||
Type=Application
|
||||
StartupNotify=false
|
||||
Vendored
+1
@@ -11,6 +11,7 @@ HOMEBREW_FOLDER="${USER_DIR}/homebrew"
|
||||
rm -rf "${HOMEBREW_FOLDER}/services"
|
||||
sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/services"
|
||||
sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/plugins"
|
||||
touch "${USER_DIR}/.steam/steam/.cef-enable-remote-debugging"
|
||||
|
||||
# Download latest release and install it
|
||||
RELEASE=$(curl -s 'https://api.github.com/repos/SteamDeckHomebrew/decky-loader/releases' | jq -r "first(.[] | select(.prerelease == "true"))")
|
||||
|
||||
Vendored
+1
@@ -11,6 +11,7 @@ HOMEBREW_FOLDER="${USER_DIR}/homebrew"
|
||||
rm -rf "${HOMEBREW_FOLDER}/services"
|
||||
sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/services"
|
||||
sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/plugins"
|
||||
touch "${USER_DIR}/.steam/steam/.cef-enable-remote-debugging"
|
||||
|
||||
# Download latest release and install it
|
||||
RELEASE=$(curl -s 'https://api.github.com/repos/SteamDeckHomebrew/decky-loader/releases' | jq -r "first(.[] | select(.prerelease == "false"))")
|
||||
|
||||
Vendored
+164
@@ -0,0 +1,164 @@
|
||||
#!/bin/sh
|
||||
|
||||
# if a password was set by decky, this will run when the program closes
|
||||
temp_pass_cleanup() {
|
||||
echo $PASS | sudo -S -k passwd -d deck
|
||||
}
|
||||
|
||||
# if the script is not root yet, get the password and rerun as root
|
||||
if (( $EUID != 0 )); then
|
||||
PASS_STATUS=$(passwd -S deck 2> /dev/null)
|
||||
if [ "$PASS_STATUS" = "" ]; then
|
||||
echo "Deck user not found. Continuing anyway, as it probably just means user is on a non-steamos system."
|
||||
fi
|
||||
|
||||
if [ "${PASS_STATUS:5:2}" = "NP" ]; then # if no password is set
|
||||
if ( zenity --title="Decky Installer" --width=300 --height=200 --question --text="You appear to have not set an admin password.\nDecky can still install by temporarily setting your password to 'Decky!' and continuing, then removing it when the installer finishes\nAre you okay with that?" ); then
|
||||
yes "Decky!" | passwd deck
|
||||
trap temp_pass_cleanup EXIT # make sure password is removed when application closes
|
||||
PASS="Decky!"
|
||||
else exit 1; fi
|
||||
else
|
||||
# get password
|
||||
FINISHED="false"
|
||||
while [ "$FINISHED" != "true" ]; do
|
||||
PASS=$(zenity --title="Decky Installer" --width=300 --height=100 --entry --hide-text --text="Enter your sudo/admin password")
|
||||
if [[ $? -eq 1 ]] || [[ $? -eq 5 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
if ( echo "$PASS" | sudo -S -k true ); then
|
||||
FINISHED="true"
|
||||
else
|
||||
zenity --title="Decky Installer" --width=150 --height=40 --info --text "Incorrect Password"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if ! [ $USER = "deck" ]; then
|
||||
zenity --title="Decky Installer" --width=300 --height=100 --warning --text "You appear to not be on a deck.\nDecky should still mostly work, but you may not get full functionality."
|
||||
fi
|
||||
|
||||
# get user dir before rerunning as root, otherwise it'll just be 'home/root'
|
||||
USER_DIR="$(getent passwd $USER | cut -d: -f6)"
|
||||
HOMEBREW_FOLDER="${USER_DIR}/homebrew"
|
||||
echo "$PASS" | sudo -S -k sh "$0" "$USER_DIR" "$HOMEBREW_FOLDER" # rerun script as root
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# all code below should be run as root
|
||||
USER_DIR=$1
|
||||
HOMEBREW_FOLDER=$2
|
||||
|
||||
# if decky is already installed, then also add an 'uninstall' prompt
|
||||
if [[ -f "${USER_DIR}/homebrew/services/PluginLoader" ]] ; then
|
||||
BRANCH=$(zenity --title="Decky Installer" --width=360 --height=170 --list --radiolist --text "Select Option:" --hide-header --column "Buttons" --column "Choice" --column "Info" TRUE "release" "(Recommended option)" FALSE "prerelease" "(May be unstable)" FALSE "uninstall decky loader" "")
|
||||
else
|
||||
BRANCH=$(zenity --title="Decky Installer" --width=300 --height=100 --list --radiolist --text "Select Branch:" --hide-header --column "Buttons" --column "Choice" --column "Info" TRUE "release" "(Recommended option)" FALSE "prerelease" "(May be unstable)" )
|
||||
fi
|
||||
if [[ $? -eq 1 ]] || [[ $? -eq 5 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# uninstall if uninstall option was selected
|
||||
if [ "$BRANCH" == "uninstall decky loader" ] ; then
|
||||
(
|
||||
echo "30" ; echo "# Disabling and removing services" ;
|
||||
sudo systemctl disable --now plugin_loader.service > /dev/null
|
||||
sudo rm -f "${USER_DIR}/.config/systemd/user/plugin_loader.service"
|
||||
sudo rm -f "/etc/systemd/system/plugin_loader.service"
|
||||
|
||||
echo "60" ; echo "# Removing Temporary Files" ;
|
||||
rm -rf "/tmp/plugin_loader"
|
||||
rm -rf "/tmp/user_install_script.sh"
|
||||
|
||||
echo "90" ; echo "# Cleaning services folder" ;
|
||||
sudo rm "${HOMEBREW_FOLDER}/services/PluginLoader"
|
||||
echo "100" ; echo "# Uninstall finished, installer can now be closed";
|
||||
) |
|
||||
zenity --progress \
|
||||
--title="Decky Installer" \
|
||||
--width=300 --height=100 \
|
||||
--text="Uninstalling..." \
|
||||
--percentage=0 \
|
||||
--no-cancel
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# otherwise install decky loader
|
||||
(
|
||||
echo "15" ; echo "# Creating file structure" ;
|
||||
rm -rf "${HOMEBREW_FOLDER}/services"
|
||||
sudo mkdir -p "${HOMEBREW_FOLDER}/services"
|
||||
sudo mkdir -p "${HOMEBREW_FOLDER}/plugins"
|
||||
touch "${USER_DIR}/.steam/steam/.cef-enable-remote-debugging"
|
||||
|
||||
echo "30" ; echo "# Finding latest $BRANCH";
|
||||
if [ $BRANCH = 'prerelease' ] ; then
|
||||
RELEASE=$(curl -s 'https://api.github.com/repos/SteamDeckHomebrew/decky-loader/releases' | jq -r "first(.[] | select(.prerelease == "true"))")
|
||||
else
|
||||
RELEASE=$(curl -s 'https://api.github.com/repos/SteamDeckHomebrew/decky-loader/releases' | jq -r "first(.[] | select(.prerelease == "false"))")
|
||||
fi
|
||||
read VERSION DOWNLOADURL < <(echo $(jq -r '.tag_name, .assets[].browser_download_url' <<< ${RELEASE}))
|
||||
|
||||
echo "45" ; echo "# Installing version $VERSION" ;
|
||||
curl -L $DOWNLOADURL -o ${HOMEBREW_FOLDER}/services/PluginLoader 2>&1 | stdbuf -oL tr '\r' '\n' | sed -u 's/^ *\([0-9][0-9]*\).*\( [0-9].*$\)/\1\n#Download Speed\:\2/' | zenity --progress --title "Downloading Decky" --text="Download Speed: 0" --width=300 --height=100 --auto-close --no-cancel 2>/dev/null
|
||||
chmod +x ${HOMEBREW_FOLDER}/services/PluginLoader
|
||||
echo $VERSION > ${HOMEBREW_FOLDER}/services/.loader.version
|
||||
|
||||
echo "70" ; echo "# Kiling plugin_loader if it exists" ;
|
||||
systemctl --user stop plugin_loader 2> /dev/null
|
||||
systemctl --user disable plugin_loader 2> /dev/null
|
||||
systemctl stop plugin_loader 2> /dev/null
|
||||
systemctl disable plugin_loader 2> /dev/null
|
||||
|
||||
echo "85" ; echo "# Setting up systemd" ;
|
||||
curl -L https://raw.githubusercontent.com/SteamDeckHomebrew/decky-loader/main/dist/plugin_loader-${BRANCH}.service --output ${HOMEBREW_FOLDER}/services/plugin_loader-${BRANCH}.service
|
||||
cat > "${HOMEBREW_FOLDER}/services/plugin_loader-backup.service" <<- EOM
|
||||
[Unit]
|
||||
Description=SteamDeck Plugin Loader
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
Restart=always
|
||||
ExecStart=${HOMEBREW_FOLDER}/services/PluginLoader
|
||||
WorkingDirectory=${HOMEBREW_FOLDER}/services
|
||||
KillSignal=SIGKILL
|
||||
Environment=PLUGIN_PATH=${HOMEBREW_FOLDER}/plugins
|
||||
Environment=LOG_LEVEL=INFO
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOM
|
||||
|
||||
# if .service file doesn't exist for whatever reason, use backup file instead
|
||||
if [[ -f "${HOMEBREW_FOLDER}/services/plugin_loader-${BRANCH}.service" ]]; then
|
||||
printf "Grabbed latest ${BRANCH} service.\n"
|
||||
sed -i -e "s|\${HOMEBREW_FOLDER}|${HOMEBREW_FOLDER}|" "${HOMEBREW_FOLDER}/services/plugin_loader-${BRANCH}.service"
|
||||
cp -f "${HOMEBREW_FOLDER}/services/plugin_loader-${BRANCH}.service" "/etc/systemd/system/plugin_loader.service"
|
||||
else
|
||||
printf "Could not curl latest ${BRANCH} systemd service, using built-in service as a backup!\n"
|
||||
rm -f "/etc/systemd/system/plugin_loader.service"
|
||||
cp "${HOMEBREW_FOLDER}/services/plugin_loader-backup.service" "/etc/systemd/system/plugin_loader.service"
|
||||
fi
|
||||
|
||||
mkdir -p ${HOMEBREW_FOLDER}/services/.systemd
|
||||
cp ${HOMEBREW_FOLDER}/services/plugin_loader-${BRANCH}.service ${HOMEBREW_FOLDER}/services/.systemd/plugin_loader-${BRANCH}.service
|
||||
cp ${HOMEBREW_FOLDER}/services/plugin_loader-backup.service ${HOMEBREW_FOLDER}/services/.systemd/plugin_loader-backup.service
|
||||
rm ${HOMEBREW_FOLDER}/services/plugin_loader-backup.service ${HOMEBREW_FOLDER}/services/plugin_loader-${BRANCH}.service
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl start plugin_loader
|
||||
systemctl enable plugin_loader
|
||||
echo "100" ; echo "# Install finished, installer can now be closed";
|
||||
) |
|
||||
zenity --progress \
|
||||
--title="Decky Installer" \
|
||||
--width=300 --height=100 \
|
||||
--text="Installing..." \
|
||||
--percentage=0 \
|
||||
--no-cancel # not actually sure how to make the cancel work properly, so it's just not there unless someone else can figure it out
|
||||
|
||||
if [ "$?" = -1 ] ; then
|
||||
zenity --title="Decky Installer" --width=150 --height=70 --error --text="Download interrupted."
|
||||
fi
|
||||
+12
-15
@@ -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);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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 () => {};
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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 () => {};
|
||||
}
|
||||
@@ -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 () => {};
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import Logger from './logger';
|
||||
declare global {
|
||||
interface Window {
|
||||
__TABS_HOOK_INSTANCE: any;
|
||||
securitystore: any;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user