Compare commits

..

24 Commits

Author SHA1 Message Date
AAGaming 880b4c2f8f maybe working fix for jan 05 beta (#316) 2023-01-05 20:00:48 -08:00
TrainDoctor 34af340009 Update config.yml 2023-01-05 18:50:10 -08:00
Party Wumpus 80b6115f6f User Friendlier Installer (#297)
* Add files via upload

* Rename EasierInstallScript.sh to user_install_script.sh

* Add files via upload

* change so it works on deck instead of my desktop

* Update decky_installer.desktop

* make auto password setter work without the password

* Update user_install_script.sh

* make installer exit properly if user does not accept temp password

* Update user_install_script.sh

* add uninstall option

* Update user_install_script.sh

* Update user_install_script.sh

* Update user_install_script.sh

* "optimisation"

* Update user_install_script.sh

* Add sizing to all zenity prompts

* "optimization" part 2

* "Program now runs 50% faster"

:)

* Update user_install_script.sh

* Update user_install_script.sh

* Update user_install_script.sh

* Change text in branch selection in installer

'Select Branch' if choosing between release and prerelease
'Select Option' if choosing between release, prerelease and uninstall

* .desktop file points at where script is going to be

* add comments

* Change "installing" to "uninstalling"

* change it to ask for "sudo/admin" password

* Add secondary loading bar for download progress

Shamelessly stolen (with permission) from emudeck, who stole it from a random blog
No I don't know how that line works, and I don't think I want to.

* Make uninstaller tell user they can exit

* add default text to the download bar just in case

* silence script download

* silence password check
2023-01-02 08:52:11 -08:00
Party Wumpus 3bed83697e Add cef debugging to the installer scripts (#310)
* Update install_prerelease.sh

* Update install_release.sh
2023-01-02 08:46:46 -08:00
Party Wumpus 0ffef6e4bf Better bug report format (#312)
* Add files via upload

* Delete bug_report.md

* Update bug_report.yml

* Update bug_report.yml

* Update bug_report.yml

* Update bug_report.yml

* Update bug_report.yml

* Update bug_report.yml

* Update bug_report.yml
2023-01-02 08:45:42 -08:00
AAGaming 8810a014f3 somehow accidentally left this in 2022-12-29 13:11:11 -05:00
AAGaming 385552451b shut down steam instead of restarting it to avoid broken CEF debugger (gamescope will restart stean for us instead) 2022-12-28 12:24:28 -05:00
AAGaming c2c9d11c66 fix broken valveInternal when on a multi-user deck 2022-12-28 12:23:42 -05:00
Nik 0474095a40 Potentially fix locale issues (#284) 2022-12-16 06:23:04 -08:00
AAGaming 346f80beb3 bump DFL to fix modals, Router -> Navigation in some places 2022-12-15 21:16:22 -05:00
TrainDoctor 2a6bf75f02 Move back to python 3.10.2 in CI 2022-12-10 15:26:51 -08:00
jurassicplayer f73918c902 feat(MoreCustomizableToasts): Allow plugin developers to customize some toast properties (#268)
* Use settingsStore directly

* Change toast etype, add showToast/playSound

* Update DFL and rebase
2022-12-10 15:09:21 -08:00
TrainDoctor ea35af2050 Update build.yml 2022-12-08 15:18:44 -08:00
NGnius (Graham) 6232e3da58 Add custom CDN support for custom stores (#269)
* Add custom CDN support for custom stores

* Update Python for CI
2022-12-07 16:27:32 -08:00
TrainDoctor 35e46f9ccb Update build.yml 2022-12-07 14:31:09 -08:00
TrainDoctor 2b9a80c151 Update install_prerelease.sh 2022-12-04 19:05:29 -08:00
TrainDoctor a90ed38c89 Update install_release.sh 2022-12-04 19:05:16 -08:00
TrainDoctor 3653cf5640 Update plugin_loader-release.service 2022-12-04 19:05:01 -08:00
TrainDoctor 0db45ca71e Update plugin_loader-prerelease.service 2022-12-04 19:04:46 -08:00
AAGaming 16681fabb5 fix http requests 2022-11-19 22:33:51 -05:00
AAGaming c210523a22 fix handleWarning in rollup config 2022-11-19 20:07:08 -05:00
Marco Rodolfi 5d8601347a Fix for wrong path for settings json files (#258)
Co-authored-by: AAGaming <aa@mail.catvibers.me>
2022-11-19 19:34:38 -05:00
AAGaming 1e02fcf394 fix broken trycatch causing occasional injection failures 2022-11-19 19:22:30 -05:00
TrainDoctor f923306a7f Update issue templates 2022-11-19 14:19:33 -08:00
27 changed files with 372 additions and 60 deletions
+73
View File
@@ -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
+5
View File
@@ -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.
+1 -1
View File
@@ -31,7 +31,7 @@ permissions:
jobs:
build:
name: Build PluginLoader
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
steps:
- name: Print input
+2 -2
View File
@@ -55,7 +55,7 @@ class PluginBrowser:
pluginBinPath = path.join(pluginBasePath, 'bin')
if access(packageJsonPath, R_OK):
with open(packageJsonPath, 'r') as f:
with open(packageJsonPath, "r", encoding="utf-8") as f:
packageJson = json.load(f)
if "remote_binary" in packageJson and len(packageJson["remote_binary"]) > 0:
# create bin directory if needed.
@@ -93,7 +93,7 @@ class PluginBrowser:
def find_plugin_folder(self, name):
for folder in listdir(self.plugin_path):
try:
with open(path.join(self.plugin_path, folder, 'plugin.json'), 'r') as f:
with open(path.join(self.plugin_path, folder, 'plugin.json'), "r", encoding="utf-8") as f:
plugin = json.load(f)
if plugin['name'] == name:
+10
View File
@@ -1,3 +1,5 @@
import grp
import pwd
import re
import ssl
import subprocess
@@ -56,6 +58,14 @@ def get_user() -> str:
raise ValueError("helpers.get_user method called before user variable was set. Run helpers.set_user first.")
return user
#Get the user owner of the given file path.
def get_user_owner(file_path) -> str:
return pwd.getpwuid(os.stat(file_path).st_uid)[0]
#Get the user group of the given file path.
def get_user_group(file_path) -> str:
return grp.getgrgid(os.stat(file_path).st_gid)[0]
# Set the global user group. get_user must be called first
def set_user_group() -> str:
global group
+1 -1
View File
@@ -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
+3 -3
View File
@@ -118,7 +118,7 @@ class Loader:
def handle_frontend_bundle(self, request):
plugin = self.plugins[request.match_info["plugin_name"]]
with open(path.join(self.plugin_path, plugin.plugin_directory, "dist/index.js"), 'r') as bundle:
with open(path.join(self.plugin_path, plugin.plugin_directory, "dist/index.js"), "r", encoding="utf-8") as bundle:
return web.Response(text=bundle.read(), content_type="application/javascript")
def import_plugin(self, file, plugin_directory, refresh=False, batch=False):
@@ -186,7 +186,7 @@ class Loader:
"""
async def load_plugin_main_view(self, request):
plugin = self.plugins[request.match_info["name"]]
with open(path.join(self.plugin_path, plugin.plugin_directory, plugin.main_view_html), 'r') as template:
with open(path.join(self.plugin_path, plugin.plugin_directory, plugin.main_view_html), "r", encoding="utf-8") as template:
template_data = template.read()
ret = f"""
<script src="/legacy/library.js"></script>
@@ -202,7 +202,7 @@ class Loader:
self.logger.info(path)
ret = ""
file_path = path.join(self.plugin_path, plugin.plugin_directory, route_path)
with open(file_path, 'r') as resource_data:
with open(file_path, "r", encoding="utf-8") as resource_data:
ret = resource_data.read()
return web.Response(text=ret)
+2 -2
View File
@@ -124,7 +124,7 @@ class PluginManager:
while not tab:
try:
tab = await get_gamepadui_tab()
except client_exceptions.ClientConnectorError or client_exceptions.ServerDisconnectedError:
except (client_exceptions.ClientConnectorError, client_exceptions.ServerDisconnectedError):
if not dc:
logger.debug("Couldn't connect to debugger, waiting...")
dc = True
@@ -176,7 +176,7 @@ class PluginManager:
# 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.StartRestart(), 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)
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)
except:
logger.info("Failed to inject JavaScript into tab\n" + format_exc())
pass
+5 -5
View File
@@ -27,9 +27,9 @@ class PluginWrapper:
self.version = None
json = load(open(path.join(plugin_path, plugin_directory, "plugin.json"), "r"))
json = load(open(path.join(plugin_path, plugin_directory, "plugin.json"), "r", encoding="utf-8"))
if path.isfile(path.join(plugin_path, plugin_directory, "package.json")):
package_json = load(open(path.join(plugin_path, plugin_directory, "package.json"), "r"))
package_json = load(open(path.join(plugin_path, plugin_directory, "package.json"), "r", encoding="utf-8"))
self.version = package_json["version"]
@@ -112,7 +112,7 @@ class PluginWrapper:
d["res"] = str(e)
d["success"] = False
finally:
writer.write((dumps(d)+"\n").encode("utf-8"))
writer.write((dumps(d, ensure_ascii=False)+"\n").encode("utf-8"))
await writer.drain()
async def _open_socket_if_not_exists(self):
@@ -140,7 +140,7 @@ class PluginWrapper:
return
async def _(self):
if await self._open_socket_if_not_exists():
self.writer.write((dumps({"stop": True})+"\n").encode("utf-8"))
self.writer.write((dumps({ "stop": True }, ensure_ascii=False)+"\n").encode("utf-8"))
await self.writer.drain()
self.writer.close()
get_event_loop().create_task(_(self))
@@ -151,7 +151,7 @@ class PluginWrapper:
async with self.method_call_lock:
if await self._open_socket_if_not_exists():
self.writer.write(
(dumps({"method": method_name, "args": kwargs})+"\n").encode("utf-8"))
(dumps({ "method": method_name, "args": kwargs }, ensure_ascii=False) + "\n").encode("utf-8"))
await self.writer.drain()
line = bytearray()
while True:
+24 -8
View File
@@ -1,40 +1,56 @@
import imp
from json import dump, load
from os import mkdir, path
from os import mkdir, path, listdir, rename
from shutil import chown
from helpers import get_home_path, get_homebrew_path, get_user, set_user
from helpers import get_home_path, get_homebrew_path, get_user, set_user, get_user_owner
class SettingsManager:
def __init__(self, name, settings_directory = None) -> None:
set_user()
USER = get_user()
wrong_dir = get_homebrew_path(get_home_path(USER))
if settings_directory == None:
settings_directory = get_homebrew_path(get_home_path(USER))
settings_directory = path.join(wrong_dir, "settings")
self.path = path.join(settings_directory, name + ".json")
#Create the folder with the correct permission
if not path.exists(settings_directory):
mkdir(settings_directory)
chown(settings_directory, USER, USER)
#Copy all old settings file in the root directory to the correct folder
for file in listdir(wrong_dir):
if file.endswith(".json"):
rename(path.join(wrong_dir,file),
path.join(settings_directory, file))
self.path = path.join(settings_directory, name + ".json")
#If the owner of the settings directory is not the user, then set it as the user:
if get_user_owner(settings_directory) != USER:
chown(settings_directory, USER, USER)
self.settings = {}
try:
open(self.path, "x")
open(self.path, "x", encoding="utf-8")
except FileExistsError as e:
self.read()
pass
def read(self):
try:
with open(self.path, "r") as file:
with open(self.path, "r", encoding="utf-8") as file:
self.settings = load(file)
except Exception as e:
print(e)
pass
def commit(self):
with open(self.path, "w+") as file:
dump(self.settings, file, indent=4)
with open(self.path, "w+", encoding="utf-8") as file:
dump(self.settings, file, indent=4, ensure_ascii=False)
def getSetting(self, key, default):
return self.settings.get(key, default)
+4 -4
View File
@@ -32,7 +32,7 @@ class Updater:
self.allRemoteVers = None
try:
logger.info(getcwd())
with open(path.join(getcwd(), ".loader.version"), 'r') as version_file:
with open(path.join(getcwd(), ".loader.version"), "r", encoding="utf-8") as version_file:
self.localVer = version_file.readline().replace("\n", "")
except:
self.localVer = False
@@ -159,10 +159,10 @@ class Updater:
out.write(data)
except Exception as e:
logger.error(f"Error at %s", exc_info=e)
with open(path.join(getcwd(), "plugin_loader.service"), 'r') as service_file:
with open(path.join(getcwd(), "plugin_loader.service"), "r", encoding="utf-8") as service_file:
service_data = service_file.read()
service_data = service_data.replace("${HOMEBREW_FOLDER}", "/home/"+helpers.get_user()+"/homebrew")
with open(path.join(getcwd(), "plugin_loader.service"), 'w') as service_file:
with open(path.join(getcwd(), "plugin_loader.service"), "w", encoding="utf-8") as service_file:
service_file.write(service_data)
logger.debug("Saved service file")
@@ -191,7 +191,7 @@ class Updater:
self.context.loop.create_task(tab.evaluate_js(f"window.DeckyUpdater.updateProgress({new_progress})", False, False, False))
progress = new_progress
with open(path.join(getcwd(), ".loader.version"), "w") as out:
with open(path.join(getcwd(), ".loader.version"), "w", encoding="utf-8") as out:
out.write(version)
call(['chmod', '+x', path.join(getcwd(), "PluginLoader")])
+2 -1
View File
@@ -81,10 +81,11 @@ class Utilities:
async def http_request(self, method="", url="", **kwargs):
async with ClientSession() as web:
res = await web.request(method, url, ssl=helpers.get_ssl_context(), **kwargs)
text = await res.text()
return {
"status": res.status,
"headers": dict(res.headers),
"body": await res.text()
"body": text
}
async def ping(self, **kwargs):
+8
View File
@@ -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
+2
View File
@@ -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"))")
@@ -40,6 +41,7 @@ User=root
Restart=always
ExecStart=${HOMEBREW_FOLDER}/services/PluginLoader
WorkingDirectory=${HOMEBREW_FOLDER}/services
KillSignal=SIGKILL
Environment=PLUGIN_PATH=${HOMEBREW_FOLDER}/plugins
Environment=LOG_LEVEL=DEBUG
[Install]
+2
View File
@@ -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"))")
@@ -40,6 +41,7 @@ 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]
+2 -1
View File
@@ -8,7 +8,8 @@ User=root
Restart=always
ExecStart=${HOMEBREW_FOLDER}/services/PluginLoader
WorkingDirectory=${HOMEBREW_FOLDER}/services
KillSignal=SIGKILL
Environment=PLUGIN_PATH=${HOMEBREW_FOLDER}/plugins
Environment=LOG_LEVEL=DEBUG
[Install]
WantedBy=multi-user.target
WantedBy=multi-user.target
+2 -1
View File
@@ -8,7 +8,8 @@ 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
WantedBy=multi-user.target
+164
View File
@@ -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
+1 -1
View File
@@ -41,7 +41,7 @@
}
},
"dependencies": {
"decky-frontend-lib": "^3.7.14",
"decky-frontend-lib": "^3.18.4",
"react-file-icon": "^1.2.0",
"react-icons": "^4.4.0",
"react-markdown": "^8.0.3",
+4 -4
View File
@@ -10,7 +10,7 @@ specifiers:
'@types/react-file-icon': ^1.0.1
'@types/react-router': 5.1.18
'@types/webpack': ^5.28.0
decky-frontend-lib: ^3.7.14
decky-frontend-lib: ^3.18.4
husky: ^8.0.1
import-sort-style-module: ^6.0.0
inquirer: ^8.2.4
@@ -30,7 +30,7 @@ specifiers:
typescript: ^4.7.4
dependencies:
decky-frontend-lib: 3.7.14
decky-frontend-lib: 3.18.4
react-file-icon: 1.2.0_wcqkhtmu7mswc6yz4uyexck3ty
react-icons: 4.4.0_react@16.14.0
react-markdown: 8.0.3_vshvapmxg47tngu7tvrsqpq55u
@@ -944,8 +944,8 @@ packages:
dependencies:
ms: 2.1.2
/decky-frontend-lib/3.7.14:
resolution: {integrity: sha512-ShAoP3VqiwkJYukDBHsOF9fk7wYw0VaKpHw6j9WdzLxwZwBcg0J7QBNIFYP3nfA0UgEwSJVEg/22kONiplipmA==}
/decky-frontend-lib/3.18.4:
resolution: {integrity: sha512-i3TAe3RJtT1TK0rJgW9Ek5jxMWZRCYLDvqHDylGVieUvuyI7c8X+cogz30pP4cqeGOaA1d/MxBEbhlpD3JhVvg==}
dev: false
/decode-named-character-reference/1.0.2:
+2 -2
View File
@@ -5,7 +5,7 @@ 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, handleWarning } from 'rollup';
import { defineConfig } from 'rollup';
const hiddenWarnings = [
"THIS_IS_UNDEFINED",
@@ -41,7 +41,7 @@ export default defineConfig({
return 'chunk-[hash].js'
}
},
onwarn: function ( message ) {
onwarn: function ( message, handleWarning ) {
if (hiddenWarnings.some(warning => message.code === warning)) return;
handleWarning(message);
}
@@ -1,4 +1,4 @@
import { ConfirmModal, QuickAccessTab, Router, Spinner, staticClasses } from 'decky-frontend-lib';
import { ConfirmModal, Navigation, QuickAccessTab, Spinner, staticClasses } from 'decky-frontend-lib';
import { FC, useState } from 'react';
interface PluginInstallModalProps {
@@ -20,7 +20,7 @@ const PluginInstallModal: FC<PluginInstallModalProps> = ({ artifact, version, ha
onOK={async () => {
setLoading(true);
await onOK();
setTimeout(() => Router.OpenQuickAccessMenu(QuickAccessTab.Decky), 250);
setTimeout(() => Navigation.OpenQuickAccessMenu(QuickAccessTab.Decky), 250);
setTimeout(() => window.DeckyPluginLoader.checkPluginUpdates(), 1000);
}}
onCancel={async () => {
+3 -3
View File
@@ -2,8 +2,8 @@ import {
DialogButton,
Dropdown,
Focusable,
Navigation,
QuickAccessTab,
Router,
SingleDropdownOption,
SuspensefulImage,
joinClassNames,
@@ -38,8 +38,8 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
}}
onCancel={(_: CustomEvent) => {
if (containerRef.current!.querySelectorAll('* :focus').length === 0) {
Router.NavigateBackOrOpenMenu();
setTimeout(() => Router.OpenQuickAccessMenu(QuickAccessTab.Decky), 1000);
Navigation.NavigateBack();
setTimeout(() => Navigation.OpenQuickAccessMenu(QuickAccessTab.Decky), 1000);
} else {
containerRef.current!.focus();
}
+17 -8
View File
@@ -1,4 +1,5 @@
import {
Navigation,
ReactRouter,
Router,
fakeRenderComponent,
@@ -26,13 +27,20 @@ const logger = new Logger('DeveloperMode');
let removeSettingsObserver: () => void = () => {};
export function setShowValveInternal(show: boolean) {
const settingsMod = findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (typeof m[prop]?.settings?.bIsValveEmail !== 'undefined') return m[prop];
export async function setShowValveInternal(show: boolean) {
let settingsMod: any;
while (!settingsMod) {
settingsMod = findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (typeof m[prop]?.settings?.bIsValveEmail !== 'undefined') return m[prop];
}
});
if (!settingsMod) {
logger.debug('[ValveInternal] waiting for settingsMod');
await sleep(1000);
}
});
}
if (show) {
removeSettingsObserver = settingsMod[
@@ -74,13 +82,14 @@ export async function startup() {
window.DFL = {
findModuleChild,
findModule,
Navigation,
Router,
ReactRouter,
ReactUtils: {
fakeRenderComponent,
findInReactTree,
findInTree,
},
Router,
ReactRouter,
classes: {
scrollClasses,
staticClasses,
+4 -1
View File
@@ -10,6 +10,7 @@ export enum Store {
export interface StorePluginVersion {
name: string;
hash: string;
artifact: string | undefined | null;
}
export interface StorePlugin {
@@ -73,9 +74,11 @@ export async function installFromURL(url: string) {
}
export async function requestPluginInstall(plugin: string, selectedVer: StorePluginVersion) {
const artifactUrl =
selectedVer.artifact ?? `https://cdn.tzatzikiweeb.moe/file/steam-deck-homebrew/versions/${selectedVer.hash}.zip`;
await window.DeckyPluginLoader.callServerMethod('install_plugin', {
name: plugin,
artifact: `https://cdn.tzatzikiweeb.moe/file/steam-deck-homebrew/versions/${selectedVer.hash}.zip`,
artifact: artifactUrl,
version: selectedVer.name,
hash: selectedVer.hash,
});
-2
View File
@@ -23,7 +23,6 @@ class TabsHook extends Logger {
tabs: Tab[] = [];
private qAMRoot?: any;
private qamPatch?: Patch;
private unsubscribeSecurity?: () => void;
constructor() {
super('TabsHook');
@@ -114,7 +113,6 @@ class TabsHook extends Logger {
deinit() {
this.qamPatch?.unpatch();
this.qAMRoot.return.alternate.type = this.qAMRoot.return.type;
this.unsubscribeSecurity?.();
}
add(tab: Tab) {
+27 -8
View File
@@ -1,4 +1,4 @@
import { Patch, ToastData, afterPatch, findInReactTree, sleep } from 'decky-frontend-lib';
import { Module, Patch, ToastData, afterPatch, findInReactTree, findModuleChild, sleep } from 'decky-frontend-lib';
import { ReactNode } from 'react';
import Toast from './components/Toast';
@@ -7,6 +7,7 @@ import Logger from './logger';
declare global {
interface Window {
__TOASTER_INSTANCE: any;
settingsStore: any;
NotificationStore: any;
}
}
@@ -16,7 +17,7 @@ class Toaster extends Logger {
// private toasterState: DeckyToasterState = new DeckyToasterState();
private node: any;
private rNode: any;
private settingsModule: any;
private audioModule: any;
private finishStartup?: () => void;
private ready: Promise<void> = new Promise((res) => (this.finishStartup = res));
private toasterPatch?: Patch;
@@ -127,6 +128,17 @@ class Toaster extends Logger {
this.rNode.stateNode.forceUpdate();
delete this.rNode.stateNode.shouldComponentUpdate;
this.audioModule = findModuleChild((m: Module) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
try {
if (m[prop].PlayNavSound && m[prop].RegisterCallbackOnPlaySound) return m[prop];
} catch {
return undefined;
}
}
});
this.log('Initialized');
this.finishStartup?.();
}
@@ -135,24 +147,31 @@ class Toaster extends Logger {
// toast.duration = toast.duration || 5e3;
// this.toasterState.addToast(toast);
await this.ready;
const settings = this.settingsModule?.settings;
let toastData = {
nNotificationID: window.NotificationStore.m_nNextTestNotificationID++,
rtCreated: Date.now(),
eType: 15,
eType: toast.eType || 11,
nToastDurationMS: toast.duration || (toast.duration = 5e3),
data: toast,
decky: true,
};
// @ts-ignore
toastData.data.appid = () => 0;
if (toast.sound === undefined) toast.sound = 6;
if (toast.playSound === undefined) toast.playSound = true;
if (toast.showToast === undefined) toast.showToast = true;
if (
(settings?.bDisableAllToasts && !toast.critical) ||
(settings?.bDisableToastsInGame && !toast.critical && window.NotificationStore.BIsUserInGame())
(window.settingsStore.settings.bDisableAllToasts && !toast.critical) ||
(window.settingsStore.settings.bDisableToastsInGame &&
!toast.critical &&
window.NotificationStore.BIsUserInGame())
)
return;
window.NotificationStore.m_rgNotificationToasts.push(toastData);
window.NotificationStore.DispatchNextToast();
if (toast.playSound) this.audioModule?.PlayNavSound(toast.sound);
if (toast.showToast) {
window.NotificationStore.m_rgNotificationToasts.push(toastData);
window.NotificationStore.DispatchNextToast();
}
}
deinit() {