Compare commits

..

13 Commits

Author SHA1 Message Date
AAGaming 2461f52ca7 change console.log to styled log 2022-10-01 21:24:10 -04:00
AAGaming 3c00eb8cf4 fix friends menu focusing itself 2022-10-01 21:22:30 -04:00
TrainDoctor 21e1d8504a Update README.md 2022-10-01 16:17:40 -07:00
Party Wumpus ba93c4add2 Change 'PluginLoader' to 'Decky Loader' in readme (#190)
* (readme) Make store link go to beta.deckbrew + Change wording for clarification

* further change

* change line 26 to say plugin menu

* Change all 'PluginLoader' to 'Decky Loader' in readme

Seems like a reasonable change as Decky was rebranded a while ago
2022-10-01 14:41:09 -07:00
TrainDoctor 61fea41c8a Update package.json 2022-09-30 21:40:51 -07:00
TrainDoctor e40d3e4db5 Removal of legacy link 2022-09-30 21:31:19 -07:00
Lukas Senionis bbad6bf2be fix(pluginview): align icon and plugin name (#185) 2022-09-26 21:51:39 -07:00
Party Wumpus 4e04455163 (readme) Make store link go to beta.deckbrew + Change wording (#184)
* (readme) Make store link go to beta.deckbrew + Change wording for clarification

* further change

* change line 26 to say plugin menu
2022-09-25 10:04:50 -07:00
Dan Burkhardt 314292b042 updated installation steps to reflect confirmation and system reboot step (#180)
Co-authored-by: TrainDoctor <traindoctor@protonmail.com>
2022-09-21 15:05:18 -07:00
Aamir Tahir a264f36966 Add note about opening terminal to README (#176)
Quick note about how to open the terminal using the pre-installed Konsole app for users that are either new to linux or Steam Deck.
2022-09-21 14:56:45 -07:00
AAGaming 60c8c5db42 check for plugin updates after installing a plugin 2022-09-19 21:13:06 -04:00
Trent Callan 852c52c59a Grab Plugin path from find_plugin_folder plugin name is not always folder anme (#178) 2022-09-18 22:13:45 -07:00
Trent Callan 3136ad72ed Download Remote Binaries during Store Install (#177)
* Download Remote Binaries during store install.
Fix Manual Install Modal Type

* Use Unix Line endings. (Thanks WSL)

* Fix Merge Commit with main branch.
2022-09-18 17:20:19 -07:00
8 changed files with 130 additions and 24 deletions
+12 -11
View File
@@ -1,28 +1,29 @@
# Plugin Loader [![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/ZU74G2NJzk)
# Decky Loader [![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/ZU74G2NJzk)
![Decky](https://media.discordapp.net/attachments/966017112244125756/1012466063893610506/main.jpg)
Keep an eye on the [Wiki](https://deckbrew.xyz) for more information about Plugin Loader, documentation + tools for plugin development and more.
Keep an eye on the [Wiki](https://deckbrew.xyz) for more information about Decky Loader, documentation + tools for plugin development and more.
## Installation
1. Go into the Steam Deck Settings
2. Under System -> System Settings toggle `Enable Developer Mode`
3. Scroll the sidebar all the way down and click on `Developer`
4. Under Miscellaneous, enable `CEF Remote Debugging`
5. Click on the `STEAM` button and select `Power` -> `Switch to Desktop`
6. Make sure you have a password set with the "passwd" command in terminal to install it ([YouTube Guide](https://www.youtube.com/watch?v=1vOMYGj22rQ)).
7. Open a terminal and paste the following command into it:
5. Confirm dialog and wait for system reboot
6. Click on the `STEAM` button and select `Power` -> `Switch to Desktop`
7. Make sure you have a password set with the "passwd" command in terminal to install it ([YouTube Guide](https://www.youtube.com/watch?v=1vOMYGj22rQ)).
8. Open a terminal ("Konsole" is the pre-installed terminal application) and paste the following command into it:
- For the latest release:
- `curl -L https://github.com/SteamDeckHomebrew/decky-loader/raw/main/dist/install_prerelease.sh | sh`
- For the latest pre-release:
- `curl -L https://github.com/SteamDeckHomebrew/decky-loader/raw/main/dist/install_prerelease.sh | sh`
- For testers/plugin developers:
- `curl -L https://github.com/SteamDeckHomebrew/decky-loader/raw/main/dist/install_prerelease.sh | sh`
- [Wiki Link](https://deckbrew.xyz/en/loader-dev/development)
- For the legacy version (unsupported):
- `curl -L https://github.com/SteamDeckHomebrew/decky-loader/raw/legacy/dist/install_release.sh | sh`
7. Done! Reboot back into Gaming mode and enjoy your plugins!
9. Done! Reboot back into Gaming mode and enjoy your plugins!
### Install/Uninstall Plugins
- Using the shopping bag button in the top right corner, you can go to the offical ["Plugin Store"](https://plugins.deckbrew.xyz/)
- Using the shopping bag button in the top right corner of the plugin menu, you can go to the offical Plugin Store ([Web Preview](https://beta.deckbrew.xyz/)).
- Simply copy the plugin's folder into `~/homebrew/plugins`
- Use the settings menu to uninstall plugins, this will not remove any files made in different directories by plugins.
@@ -41,8 +42,8 @@ Keep an eye on the [Wiki](https://deckbrew.xyz) for more information about Plugi
- There is no complete plugin development documentation yet. However a good starting point is the [Plugin Template](https://github.com/SteamDeckHomebrew/decky-plugin-template) repository.
## [Contribution](https://deckbrew.xyz/en/loader-dev/development)
- Please consult the [Wiki](https://deckbrew.xyz/en/loader-dev/development) for installing development versions of PluginLoader.
- This is also useful for Plugin Developers looking to target new but unreleased versions of PluginLoader.
- Please consult the [Wiki](https://deckbrew.xyz/en/loader-dev/development) for installing development versions of Decky Loader.
- This is also useful for Plugin Developers looking to target new but unreleased versions of Decky Loader.
- [Here's how to get the Steam Deck UI on your enviroment of choice.](https://youtu.be/1IAbZte8e7E?t=112)
- (The video shows Windows usage but unless you're using Arch WSL/cygwin this script is unsupported on Windows.)
+55 -9
View File
@@ -8,14 +8,14 @@ from concurrent.futures import ProcessPoolExecutor
from hashlib import sha256
from io import BytesIO
from logging import getLogger
from os import path, rename, listdir
from os import R_OK, W_OK, path, rename, listdir, access, mkdir
from shutil import rmtree
from subprocess import call
from time import time
from zipfile import ZipFile
# Local modules
from helpers import get_ssl_context, get_user, get_user_group
from helpers import get_ssl_context, get_user, get_user_group, download_remote_binary_to_path
from injector import get_tab, inject_to_tab
logger = getLogger("Browser")
@@ -47,6 +47,48 @@ class PluginBrowser:
logger.error(f"chown/chmod exited with a non-zero exit code (chown: {code_chown}, chmod: {code_chmod})")
return False
return True
async def _download_remote_binaries_for_plugin_with_name(self, pluginBasePath):
rv = False
try:
packageJsonPath = path.join(pluginBasePath, 'package.json')
pluginBinPath = path.join(pluginBasePath, 'bin')
if access(packageJsonPath, R_OK):
with open(packageJsonPath, 'r') as f:
packageJson = json.load(f)
if len(packageJson["remote_binary"]) > 0:
# create bin directory if needed.
rc=call(["chmod", "-R", "777", pluginBasePath])
if access(pluginBasePath, W_OK):
if not path.exists(pluginBinPath):
mkdir(pluginBinPath)
if not access(pluginBinPath, W_OK):
rc=call(["chmod", "-R", "777", pluginBinPath])
rv = True
for remoteBinary in packageJson["remote_binary"]:
# Required Fields. If any Remote Binary is missing these fail the install.
binName = remoteBinary["name"]
binURL = remoteBinary["url"]
binHash = remoteBinary["sha256hash"]
if not await download_remote_binary_to_path(binURL, binHash, path.join(pluginBinPath, binName)):
rv = False
raise Exception(f"Error Downloading Remote Binary {binName}@{binURL} with hash {binHash} to {path.join(pluginBinPath, binName)}")
code_chown = call(["chown", "-R", get_user()+":"+get_user_group(), self.plugin_path])
rc=call(["chmod", "-R", "555", pluginBasePath])
else:
rv = True
logger.debug(f"No Remote Binaries to Download")
except Exception as e:
rv = False
logger.debug(str(e))
return rv
def find_plugin_folder(self, name):
for folder in listdir(self.plugin_path):
@@ -100,14 +142,18 @@ class PluginBrowser:
logger.debug("Unzipping...")
ret = self._unzip_to_plugin_dir(res_zip, name, hash)
if ret:
logger.info(f"Installed {name} (Version: {version})")
plugin_dir = self.find_plugin_folder(name)
if name in self.loader.plugins:
self.loader.plugins[name].stop()
self.loader.plugins.pop(name, None)
await sleep(1)
self.loader.import_plugin(path.join(plugin_dir, "main.py"), plugin_dir)
# await inject_to_tab("SP", "window.syncDeckyPlugins()")
ret = await self._download_remote_binaries_for_plugin_with_name(plugin_dir)
if ret:
logger.info(f"Installed {name} (Version: {version})")
if name in self.loader.plugins:
self.loader.plugins[name].stop()
self.loader.plugins.pop(name, None)
await sleep(1)
self.loader.import_plugin(path.join(plugin_dir, "main.py"), plugin_dir)
# await inject_to_tab("SP", "window.syncDeckyPlugins()")
else:
logger.fatal(f"Failed Downloading Remote Binaries")
else:
self.log.fatal(f"SHA-256 Mismatch!!!! {name} (Version: {version})")
if self.loader.watcher:
+28
View File
@@ -2,11 +2,15 @@ import re
import ssl
import subprocess
import uuid
import os
from subprocess import check_output
from time import sleep
from hashlib import sha256
from io import BytesIO
import certifi
from aiohttp.web import Response, middleware
from aiohttp import ClientSession
REMOTE_DEBUGGER_UNIT = "steam-web-debug-portforward.service"
@@ -83,6 +87,30 @@ def get_homebrew_path(home_path = None) -> str:
return str(home_path+"/homebrew")
# return str(home_path+"/homebrew")
# Download Remote Binaries to local Plugin
async def download_remote_binary_to_path(url, binHash, path) -> bool:
rv = False
try:
if os.access(os.path.dirname(path), os.W_OK):
async with ClientSession() as client:
res = await client.get(url, ssl=get_ssl_context())
if res.status == 200:
data = BytesIO(await res.read())
remoteHash = sha256(data.getbuffer()).hexdigest()
if binHash == remoteHash:
data.seek(0)
with open(path, 'wb') as f:
f.write(data.getbuffer())
rv = True
else:
raise Exception(f"Fatal Error: Hash Mismatch for remote binary {path}@{url}")
else:
rv = False
except:
rv = False
return rv
async def is_systemd_unit_active(unit_name: str) -> bool:
res = subprocess.run(["systemctl", "is-active", unit_name], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
return res.returncode == 0
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "decky_frontend",
"version": "0.0.1",
"version": "2.1.1",
"private": true,
"license": "GPLV2",
"scripts": {
+2 -2
View File
@@ -32,8 +32,8 @@ const PluginView: VFC = () => {
.map(({ name, icon }) => (
<PanelSectionRow key={name}>
<ButtonItem layout="below" onClick={() => setActivePlugin(name)}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div>{icon}</div>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
{icon}
<div>{name}</div>
<NotificationBadge show={updates?.has(name)} style={{ top: '-5px', right: '-5px' }} />
</div>
@@ -21,6 +21,7 @@ const PluginInstallModal: FC<PluginInstallModalProps> = ({ artifact, version, ha
setLoading(true);
await onOK();
setTimeout(() => Router.OpenQuickAccessMenu(QuickAccessTab.Decky), 250);
setTimeout(() => window.DeckyPluginLoader.checkPluginUpdates(), 1000);
}}
onCancel={async () => {
await onCancel();
+1
View File
@@ -101,6 +101,7 @@ class TabsHook extends Logger {
});
this.cNode = scrollRoot;
this.cNode.stateNode.forceUpdate();
this.log('Finished initial injection');
})();
}
+30 -1
View File
@@ -1,4 +1,14 @@
import { Patch, ToastData, afterPatch, findInReactTree, findModuleChild, sleep } from 'decky-frontend-lib';
import {
Patch,
ToastData,
afterPatch,
callOriginal,
findInReactTree,
findModuleChild,
replacePatch,
sleep,
staticClasses,
} from 'decky-frontend-lib';
import { ReactNode } from 'react';
import Toast from './components/Toast';
@@ -27,6 +37,24 @@ class Toaster extends Logger {
async init() {
let instance: any;
const self = this;
const focusManager = findModuleChild((m) => {
if (typeof m !== 'object') return false;
for (let prop in m) {
if (m[prop]?.prototype?.TakeFocus) return m[prop];
}
return false;
});
const focusWorkaroundPatch = replacePatch(focusManager.prototype, 'BFocusWithin', function () {
// @ts-ignore
if (this.m_node?.m_element && this.m_node?.m_element.classList.contains(staticClasses.TabGroupPanel)) {
self.debug('Intercepted friends re-focus');
return true;
}
return callOriginal;
});
while (true) {
instance = findInReactTree(
(document.getElementById('root') as any)._reactRootContainer._internalRoot.current,
@@ -71,6 +99,7 @@ class Toaster extends Logger {
if (typeof m[prop]?.settings && m[prop]?.communityPreferences) return m[prop];
}
});
focusWorkaroundPatch.unpatch();
this.log('Initialized');
this.ready = true;
}