mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-06-13 12:15:09 +03:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 88e1e9b869 | |||
| fc0089f7a5 | |||
| d335562328 | |||
| f9624a0859 | |||
| 97bb3fa4c8 | |||
| 611245aec9 | |||
| e1807e8c75 | |||
| b94cfe32d9 | |||
| f1e679c3fb | |||
| e1b138bcbd | |||
| c6be8f6c14 | |||
| ac086cf59e | |||
| 3e120ea312 |
@@ -12,6 +12,7 @@ body:
|
||||
- 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)
|
||||
- label: I have attached logs to this bug report (failure to include logs will mean your issue will not be responded too).
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
@@ -70,4 +71,4 @@ body:
|
||||
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
|
||||
required: true
|
||||
|
||||
@@ -69,7 +69,7 @@ jobs:
|
||||
run: pnpm run build
|
||||
|
||||
- name: Build Python Backend 🛠️
|
||||
run: pyinstaller --noconfirm --onefile --name "PluginLoader" --add-data ./backend/static:/static --add-data ./backend/legacy:/legacy ./backend/*.py
|
||||
run: pyinstaller --noconfirm --onefile --name "PluginLoader" --add-data ./backend/static:/static --add-data ./backend/legacy:/legacy --add-data ./plugin:/plugin ./backend/*.py
|
||||
|
||||
- name: Upload package artifact ⬆️
|
||||
if: ${{ !env.ACT }}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<a href="https://github.com/SteamDeckHomebrew/decky-loader/stargazers"><img src="https://img.shields.io/github/stars/SteamDeckHomebrew/decky-loader" /></a>
|
||||
<a href="https://github.com/SteamDeckHomebrew/decky-loader/commits/main"><img src="https://img.shields.io/github/last-commit/SteamDeckHomebrew/decky-loader.svg" /></a>
|
||||
<a href="https://github.com/SteamDeckHomebrew/decky-loader/blob/main/LICENSE"><img src="https://img.shields.io/github/license/SteamDeckHomebrew/decky-loader" /></a>
|
||||
<a href="https://discord.gg/ZU74G2NJzk"><img src="https://img.shields.io/discord/960281551428522045?color=%235865F2&label=discord" /></a>
|
||||
<a href="https://deckbrew.xyz/discord"><img src="https://img.shields.io/discord/960281551428522045?color=%235865F2&label=discord" /></a>
|
||||
<br>
|
||||
<br>
|
||||
<img src="https://media.discordapp.net/attachments/966017112244125756/1012466063893610506/main.jpg" alt="Decky screenshot" width="80%">
|
||||
@@ -62,7 +62,7 @@ For more information about Decky Loader as well as documentation and development
|
||||
|
||||
### 👋 Uninstallation
|
||||
|
||||
We are sorry to see you go! If you are considering uninstalling because you are having issues, please consider [opening an issue](https://github.com/SteamDeckHomebrew/decky-loader/issues) or [joining our Discord](https://discord.gg/ZU74G2NJzk) so we can help you and other users.
|
||||
We are sorry to see you go! If you are considering uninstalling because you are having issues, please consider [opening an issue](https://github.com/SteamDeckHomebrew/decky-loader/issues) or [joining our Discord](https://deckbrew.xyz/discord) so we can help you and other users.
|
||||
|
||||
1. Press the <img src="./docs/images/light/steam.svg#gh-dark-mode-only" height=16><img src="./docs/images/dark/steam.svg#gh-light-mode-only" height=16> button and open the Power menu.
|
||||
1. Select "Switch to Desktop".
|
||||
@@ -84,15 +84,16 @@ Now that you have Decky Loader installed, you can start using plugins. Each plug
|
||||
|
||||
### 🛠️ Plugin Development
|
||||
|
||||
There is no complete plugin development documentation yet. However a good starting point is the [plugin template repository](https://github.com/SteamDeckHomebrew/decky-plugin-template). Consider [joining our Discord](https://discord.gg/ZU74G2NJzk) if you have any questions.
|
||||
There is no complete plugin development documentation yet. However a good starting point is the [plugin template repository](https://github.com/SteamDeckHomebrew/decky-plugin-template). Consider [joining our Discord](https://deckbrew.xyz/discord) if you have any questions.
|
||||
|
||||
### 🤝 Contributing
|
||||
|
||||
Please consult [the wiki page regarding development](https://deckbrew.xyz/en/loader-dev/development) for more information on installing development versions of Decky Loader. You can also install the Steam Deck UI on a Windows or Linux computer for testing by following [this YouTube guide](https://youtu.be/1IAbZte8e7E?t=112).
|
||||
Please consult [the wiki page regarding development](https://wiki.deckbrew.xyz/en/loader-dev/development) for more information on installing development versions of Decky Loader. You can also install the Steam Deck UI on a Windows or Linux computer for testing by following [this YouTube guide](https://youtu.be/1IAbZte8e7E?t=112).
|
||||
|
||||
1. Clone the repository using the latest commit to main before starting your PR.
|
||||
1. In your clone of the repository, run these commands.
|
||||
```bash
|
||||
cd frontend
|
||||
pnpm i
|
||||
pnpm run build
|
||||
```
|
||||
|
||||
+12
-2
@@ -115,8 +115,18 @@ def mkdir_as_user(path):
|
||||
|
||||
# Fetches the version of loader
|
||||
def get_loader_version() -> str:
|
||||
with open(os.path.join(os.path.dirname(sys.argv[0]), ".loader.version"), "r", encoding="utf-8") as version_file:
|
||||
return version_file.readline().replace("\n", "")
|
||||
try:
|
||||
with open(os.path.join(os.getcwd(), ".loader.version"), "r", encoding="utf-8") as version_file:
|
||||
return version_file.readline().strip()
|
||||
except:
|
||||
return "unknown"
|
||||
|
||||
# returns the appropriate system python paths
|
||||
def get_system_pythonpaths() -> list[str]:
|
||||
# run as normal normal user to also include user python paths
|
||||
proc = subprocess.run(["python3", "-c", "import sys; print(':'.join(x for x in sys.path if x))"],
|
||||
user=get_user_id(), env={}, capture_output=True)
|
||||
return proc.stdout.decode().strip().split(":")
|
||||
|
||||
# Download Remote Binaries to local Plugin
|
||||
async def download_remote_binary_to_path(url, binHash, path) -> bool:
|
||||
|
||||
+2
-7
@@ -6,14 +6,9 @@ from pathlib import Path
|
||||
from traceback import print_exc
|
||||
|
||||
from aiohttp import web
|
||||
from genericpath import exists
|
||||
from os.path import exists
|
||||
from watchdog.events import RegexMatchingEventHandler
|
||||
from watchdog.utils import UnsupportedLibc
|
||||
|
||||
try:
|
||||
from watchdog.observers.inotify import InotifyObserver as Observer
|
||||
except UnsupportedLibc:
|
||||
from watchdog.observers.fsevents import FSEventsObserver as Observer
|
||||
from watchdog.observers import Observer
|
||||
|
||||
from injector import get_tab, get_gamepadui_tab
|
||||
from plugin import PluginWrapper
|
||||
|
||||
+10
-1
@@ -9,7 +9,7 @@ from logging import getLogger
|
||||
from traceback import format_exc
|
||||
from os import path, setgid, setuid, environ
|
||||
from signal import SIGINT, signal
|
||||
from sys import exit
|
||||
from sys import exit, path as syspath
|
||||
from time import time
|
||||
import helpers
|
||||
from updater import Updater
|
||||
@@ -66,6 +66,7 @@ class PluginWrapper:
|
||||
environ["USER"] = "root" if "root" in self.flags else helpers.get_user()
|
||||
environ["DECKY_VERSION"] = helpers.get_loader_version()
|
||||
environ["DECKY_USER"] = helpers.get_user()
|
||||
environ["DECKY_USER_HOME"] = helpers.get_home_path()
|
||||
environ["DECKY_HOME"] = helpers.get_homebrew_path()
|
||||
environ["DECKY_PLUGIN_SETTINGS_DIR"] = path.join(environ["DECKY_HOME"], "settings", self.plugin_directory)
|
||||
helpers.mkdir_as_user(environ["DECKY_PLUGIN_SETTINGS_DIR"])
|
||||
@@ -77,11 +78,19 @@ class PluginWrapper:
|
||||
environ["DECKY_PLUGIN_NAME"] = self.name
|
||||
environ["DECKY_PLUGIN_VERSION"] = self.version
|
||||
environ["DECKY_PLUGIN_AUTHOR"] = self.author
|
||||
# append the loader's plugin path to the recognized python paths
|
||||
syspath.append(path.realpath(path.join(path.dirname(__file__), "plugin")))
|
||||
# append the plugin's `py_modules` to the recognized python paths
|
||||
syspath.append(path.join(environ["DECKY_PLUGIN_DIR"], "py_modules"))
|
||||
# append the system and user python paths
|
||||
syspath.extend(helpers.get_system_pythonpaths())
|
||||
spec = spec_from_file_location("_", self.file)
|
||||
module = module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
self.Plugin = module.Plugin
|
||||
|
||||
if hasattr(self.Plugin, "_migration"):
|
||||
get_event_loop().run_until_complete(self.Plugin._migration(self.Plugin))
|
||||
if hasattr(self.Plugin, "_main"):
|
||||
get_event_loop().create_task(self.Plugin._main(self.Plugin))
|
||||
get_event_loop().create_task(self._setup_socket())
|
||||
|
||||
+8
-14
@@ -30,10 +30,7 @@ class Updater:
|
||||
}
|
||||
self.remoteVer = None
|
||||
self.allRemoteVers = None
|
||||
try:
|
||||
self.localVer = helpers.get_loader_version()
|
||||
except:
|
||||
self.localVer = False
|
||||
self.localVer = helpers.get_loader_version()
|
||||
|
||||
try:
|
||||
self.currentBranch = self.get_branch(self.context.settings)
|
||||
@@ -68,7 +65,7 @@ class Updater:
|
||||
logger.debug("current branch: %i" % ver)
|
||||
if ver == -1:
|
||||
logger.info("Current branch is not set, determining branch from version...")
|
||||
if self.localVer.startswith("v") and self.localVer.find("-pre"):
|
||||
if self.localVer.startswith("v") and "-pre" in self.localVer:
|
||||
logger.info("Current version determined to be pre-release")
|
||||
return 1
|
||||
else:
|
||||
@@ -94,15 +91,12 @@ class Updater:
|
||||
return str(url)
|
||||
|
||||
async def get_version(self):
|
||||
if self.localVer:
|
||||
return {
|
||||
"current": self.localVer,
|
||||
"remote": self.remoteVer,
|
||||
"all": self.allRemoteVers,
|
||||
"updatable": self.localVer != None
|
||||
}
|
||||
else:
|
||||
return {"current": "unknown", "remote": self.remoteVer, "all": self.allRemoteVers, "updatable": False}
|
||||
return {
|
||||
"current": self.localVer,
|
||||
"remote": self.remoteVer,
|
||||
"all": self.allRemoteVers,
|
||||
"updatable": self.localVer != "unknown"
|
||||
}
|
||||
|
||||
async def check_for_updates(self):
|
||||
logger.debug("checking for updates")
|
||||
|
||||
@@ -251,7 +251,7 @@ class Utilities:
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: function() {
|
||||
return FocusNavController?.m_ActiveContext?.ActiveWindow || window;
|
||||
return (GamepadNavTree?.m_context?.m_controller || FocusNavController)?.m_ActiveContext?.ActiveWindow || window;
|
||||
}
|
||||
});
|
||||
""" + await res.text() + "\n}"
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"decky-frontend-lib": "^3.18.10",
|
||||
"decky-frontend-lib": "^3.19.1",
|
||||
"react-file-icon": "^1.2.0",
|
||||
"react-icons": "^4.4.0",
|
||||
"react-markdown": "^8.0.3",
|
||||
|
||||
Generated
+4
-4
@@ -11,7 +11,7 @@ specifiers:
|
||||
'@types/react-file-icon': ^1.0.1
|
||||
'@types/react-router': 5.1.18
|
||||
'@types/webpack': ^5.28.0
|
||||
decky-frontend-lib: ^3.18.10
|
||||
decky-frontend-lib: ^3.19.1
|
||||
husky: ^8.0.1
|
||||
import-sort-style-module: ^6.0.0
|
||||
inquirer: ^8.2.4
|
||||
@@ -31,7 +31,7 @@ specifiers:
|
||||
typescript: ^4.7.4
|
||||
|
||||
dependencies:
|
||||
decky-frontend-lib: 3.18.10
|
||||
decky-frontend-lib: 3.19.1
|
||||
react-file-icon: 1.2.0_wcqkhtmu7mswc6yz4uyexck3ty
|
||||
react-icons: 4.4.0_react@16.14.0
|
||||
react-markdown: 8.0.3_vshvapmxg47tngu7tvrsqpq55u
|
||||
@@ -975,8 +975,8 @@ packages:
|
||||
dependencies:
|
||||
ms: 2.1.2
|
||||
|
||||
/decky-frontend-lib/3.18.10:
|
||||
resolution: {integrity: sha512-2mgbA3sSkuwQR/FnmhXVrcW6LyTS95IuL6muJAmQCruhBvXapDtjk1TcgxqMZxFZwGD1IPnemPYxHZll6IgnZw==}
|
||||
/decky-frontend-lib/3.19.1:
|
||||
resolution: {integrity: sha512-hU4+EFs74MGzUCv8l1AO2+EBj9RRbnpU19Crm4u+3lbLu6d63U2GsUeQ9ssmNRcOMY1OuVZkRoZBE58soOBJ3A==}
|
||||
dev: false
|
||||
|
||||
/decode-named-character-reference/1.0.2:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Focusable, Router } from 'decky-frontend-lib';
|
||||
import { Focusable, Navigation } from 'decky-frontend-lib';
|
||||
import { FunctionComponent, useRef } from 'react';
|
||||
import ReactMarkdown, { Options as ReactMarkdownOptions } from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
@@ -22,7 +22,7 @@ const Markdown: FunctionComponent<MarkdownProps> = (props) => {
|
||||
onActivate={() => {}}
|
||||
onOKButton={() => {
|
||||
props.onDismiss?.();
|
||||
Router.NavigateToExternalWeb(aRef.current!.href);
|
||||
Navigation.NavigateToExternalWeb(aRef.current!.href);
|
||||
}}
|
||||
style={{ display: 'inline' }}
|
||||
>
|
||||
|
||||
@@ -4,15 +4,11 @@ const QuickAccessVisibleState = createContext<boolean>(true);
|
||||
|
||||
export const useQuickAccessVisible = () => useContext(QuickAccessVisibleState);
|
||||
|
||||
export const QuickAccessVisibleStateProvider: FC<{ initial: boolean; setter: ((val: boolean) => {}[]) | never[] }> = ({
|
||||
children,
|
||||
initial,
|
||||
setter,
|
||||
}) => {
|
||||
export const QuickAccessVisibleStateProvider: FC<{ initial: boolean; tab: any }> = ({ children, initial, tab }) => {
|
||||
const [visible, setVisible] = useState<boolean>(initial);
|
||||
const [prev, setPrev] = useState<boolean>(initial);
|
||||
// hack to use an array as a "pointer" to pass the setter up the tree
|
||||
setter[0] = setVisible;
|
||||
// HACK but i can't think of a better way to do this
|
||||
tab.qAMVisibilitySetter = setVisible;
|
||||
if (initial != prev) {
|
||||
setPrev(initial);
|
||||
setVisible(initial);
|
||||
|
||||
@@ -4,13 +4,6 @@ import Logger from '../../../../logger';
|
||||
|
||||
const logger = new Logger('LibraryPatch');
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
SteamClient: any;
|
||||
appDetailsStore: any;
|
||||
}
|
||||
}
|
||||
|
||||
let patch: Patch;
|
||||
|
||||
function rePatch() {
|
||||
@@ -20,7 +13,9 @@ function rePatch() {
|
||||
const details = window.appDetailsStore.GetAppDetails(appid);
|
||||
logger.debug('game details', details);
|
||||
// strShortcutStartDir
|
||||
const file = await window.DeckyPluginLoader.openFilePicker(details.strShortcutStartDir.replaceAll('"', ''));
|
||||
const file = await window.DeckyPluginLoader.openFilePicker(
|
||||
details?.strShortcutStartDir.replaceAll('"', '') || '/',
|
||||
);
|
||||
logger.debug('user selected', file);
|
||||
window.SteamClient.Apps.SetShortcutExe(appid, JSON.stringify(file.path));
|
||||
const pathArr = file.path.split('/');
|
||||
|
||||
@@ -52,7 +52,7 @@ export default function DeveloperSettings() {
|
||||
>
|
||||
<Toggle
|
||||
value={reactDevtoolsEnabled}
|
||||
disabled={reactDevtoolsIP == ''}
|
||||
// disabled={reactDevtoolsIP == ''}
|
||||
onChange={(toggleValue) => {
|
||||
setReactDevtoolsEnabled(toggleValue);
|
||||
setShouldConnectToReactDevTools(toggleValue);
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
Focusable,
|
||||
ProgressBarWithInfo,
|
||||
Spinner,
|
||||
findSP,
|
||||
showModal,
|
||||
} from 'decky-frontend-lib';
|
||||
import { useCallback } from 'react';
|
||||
@@ -14,7 +15,6 @@ import { useEffect, useState } from 'react';
|
||||
import { FaExclamation } from 'react-icons/fa';
|
||||
|
||||
import { VerInfo, callUpdaterMethod, finishUpdate } from '../../../../updater';
|
||||
import { findSP } from '../../../../utils/windows';
|
||||
import { useDeckyState } from '../../../DeckyState';
|
||||
import InlinePatchNotes from '../../../patchnotes/InlinePatchNotes';
|
||||
import WithSuspense from '../../../WithSuspense';
|
||||
|
||||
@@ -15,7 +15,7 @@ import Logger from '../../logger';
|
||||
import { StorePlugin, getPluginList } from '../../store';
|
||||
import PluginCard from './PluginCard';
|
||||
|
||||
const logger = new Logger('FilePicker');
|
||||
const logger = new Logger('Store');
|
||||
|
||||
const StorePage: FC<{}> = () => {
|
||||
const [currentTabRoute, setCurrentTabRoute] = useState<string>('browse');
|
||||
|
||||
@@ -123,11 +123,9 @@ class RouterHook extends Logger {
|
||||
this.wrapperPatch = afterPatch(this.gamepadWrapper, 'render', (_: any, ret: any) => {
|
||||
if (ret?.props?.children?.props?.children?.length == 5 || ret?.props?.children?.props?.children?.length == 4) {
|
||||
const idx = ret?.props?.children?.props?.children?.length == 4 ? 1 : 2;
|
||||
if (
|
||||
ret.props.children.props.children[idx]?.props?.children?.[0]?.type?.type
|
||||
?.toString()
|
||||
?.includes('GamepadUI.Settings.Root()')
|
||||
) {
|
||||
const potentialSettingsRootString =
|
||||
ret.props.children.props.children[idx]?.props?.children?.[0]?.type?.type?.toString() || '';
|
||||
if (potentialSettingsRootString?.includes('Settings.Root()')) {
|
||||
if (!this.router) {
|
||||
this.router = ret.props.children.props.children[idx]?.props?.children?.[0]?.type;
|
||||
this.routerPatch = afterPatch(this.router, 'type', (_: any, ret: any) => {
|
||||
|
||||
@@ -7,6 +7,6 @@ export function deinitSteamFixes() {
|
||||
}
|
||||
|
||||
export async function initSteamFixes() {
|
||||
fixes.push(reloadFix());
|
||||
fixes.push(await reloadFix());
|
||||
fixes.push(await restartFix());
|
||||
}
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import { getFocusNavController, sleep } from 'decky-frontend-lib';
|
||||
|
||||
import Logger from '../logger';
|
||||
|
||||
const logger = new Logger('ReloadSteamFix');
|
||||
|
||||
export default function reloadFix() {
|
||||
declare global {
|
||||
var GamepadNavTree: any;
|
||||
}
|
||||
|
||||
export default async function reloadFix() {
|
||||
// Hack to unbreak the ui when reloading it
|
||||
if (window.FocusNavController?.m_rgAllContexts?.length == 0) {
|
||||
await sleep(4000);
|
||||
if (getFocusNavController()?.m_rgAllContexts?.length == 0) {
|
||||
SteamClient.URL.ExecuteSteamURL('steam://open/settings');
|
||||
logger.log('Applied UI reload fix.');
|
||||
}
|
||||
|
||||
@@ -4,13 +4,6 @@ import Logger from '../logger';
|
||||
|
||||
const logger = new Logger('RestartSteamFix');
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
SteamClient: any;
|
||||
appDetailsStore: any;
|
||||
}
|
||||
}
|
||||
|
||||
let patch: Patch;
|
||||
|
||||
function rePatch() {
|
||||
|
||||
@@ -128,22 +128,23 @@ class TabsHook extends Logger {
|
||||
let deckyTabAmount = existingTabs.reduce((prev: any, cur: any) => (cur.decky ? prev + 1 : prev), 0);
|
||||
if (deckyTabAmount == this.tabs.length) {
|
||||
for (let tab of existingTabs) {
|
||||
if (tab?.decky) tab.panel.props.setter[0](visible);
|
||||
if (tab?.decky && tab?.qAMVisibilitySetter) tab?.qAMVisibilitySetter(visible);
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (const { title, icon, content, id } of this.tabs) {
|
||||
existingTabs.push({
|
||||
const tab: any = {
|
||||
key: id,
|
||||
title,
|
||||
tab: icon,
|
||||
decky: true,
|
||||
panel: (
|
||||
<QuickAccessVisibleStateProvider initial={visible} setter={[]}>
|
||||
{content}
|
||||
</QuickAccessVisibleStateProvider>
|
||||
),
|
||||
});
|
||||
};
|
||||
tab.panel = (
|
||||
<QuickAccessVisibleStateProvider initial={visible} tab={tab}>
|
||||
{content}
|
||||
</QuickAccessVisibleStateProvider>
|
||||
);
|
||||
existingTabs.push(tab);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,11 +40,14 @@ class Toaster extends Logger {
|
||||
let instance: any;
|
||||
const tree = (document.getElementById('root') as any)._reactRootContainer._internalRoot.current;
|
||||
const findToasterRoot = (currentNode: any, iters: number): any => {
|
||||
if (iters >= 50) {
|
||||
// currently 40
|
||||
if (iters >= 65) {
|
||||
// currently 65
|
||||
return null;
|
||||
}
|
||||
if (currentNode?.memoizedProps?.className?.startsWith?.('toastmanager_ToastPlaceholder')) {
|
||||
if (
|
||||
currentNode?.memoizedProps?.className?.startsWith?.('toastmanager_ToastPlaceholder') ||
|
||||
currentNode?.memoizedProps?.className?.startsWith?.('toastmanager_ToastPopup')
|
||||
) {
|
||||
this.log(`Toaster root was found in ${iters} recursion cycles`);
|
||||
return currentNode;
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
export function findSP(): Window {
|
||||
// old (SP as host)
|
||||
if (document.title == 'SP') return window;
|
||||
// new (SP as popup)
|
||||
return FocusNavController.m_ActiveContext.m_rgGamepadNavigationTrees.find((x: any) => x.m_ID == 'root_1_').Root
|
||||
.Element.ownerDocument.defaultView;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "ESNext",
|
||||
"target": "ES2020",
|
||||
"target": "ES2021",
|
||||
"jsx": "react",
|
||||
"jsxFactory": "window.SP_REACT.createElement",
|
||||
"jsxFragmentFactory": "window.SP_REACT.Fragment",
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
"""
|
||||
This module exposes various constants and helpers useful for decky plugins.
|
||||
|
||||
* Plugin's settings and configurations should be stored under `DECKY_PLUGIN_SETTINGS_DIR`.
|
||||
* Plugin's runtime data should be stored under `DECKY_PLUGIN_RUNTIME_DIR`.
|
||||
* Plugin's persistent log files should be stored under `DECKY_PLUGIN_LOG_DIR`.
|
||||
|
||||
Avoid writing outside of `DECKY_HOME`, storing under the suggested paths is strongly recommended.
|
||||
|
||||
Some basic migration helpers are available: `migrate_any`, `migrate_settings`, `migrate_runtime`, `migrate_logs`.
|
||||
|
||||
A logging facility `logger` is available which writes to the recommended location.
|
||||
"""
|
||||
|
||||
__version__ = '0.1.0'
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import logging
|
||||
|
||||
"""
|
||||
Constants
|
||||
"""
|
||||
|
||||
HOME: str = os.getenv("HOME", default="")
|
||||
"""
|
||||
The home directory of the effective user running the process.
|
||||
Environment variable: `HOME`.
|
||||
If `root` was specified in the plugin's flags it will be `/root` otherwise the user whose home decky resides in.
|
||||
e.g.: `/home/deck`
|
||||
"""
|
||||
|
||||
USER: str = os.getenv("USER", default="")
|
||||
"""
|
||||
The effective username running the process.
|
||||
Environment variable: `USER`.
|
||||
It would be `root` if `root` was specified in the plugin's flags otherwise the user whose home decky resides in.
|
||||
e.g.: `deck`
|
||||
"""
|
||||
|
||||
DECKY_VERSION: str = os.getenv("DECKY_VERSION", default="")
|
||||
"""
|
||||
The version of the decky loader.
|
||||
Environment variable: `DECKY_VERSION`.
|
||||
e.g.: `v2.5.0-pre1`
|
||||
"""
|
||||
|
||||
DECKY_USER: str = os.getenv("DECKY_USER", default="")
|
||||
"""
|
||||
The user whose home decky resides in.
|
||||
Environment variable: `DECKY_USER`.
|
||||
e.g.: `deck`
|
||||
"""
|
||||
|
||||
DECKY_USER_HOME: str = os.getenv("DECKY_USER_HOME", default="")
|
||||
"""
|
||||
The home of the user where decky resides in.
|
||||
Environment variable: `DECKY_USER_HOME`.
|
||||
e.g.: `/home/deck`
|
||||
"""
|
||||
|
||||
DECKY_HOME: str = os.getenv("DECKY_HOME", default="")
|
||||
"""
|
||||
The root of the decky folder.
|
||||
Environment variable: `DECKY_HOME`.
|
||||
e.g.: `/home/deck/homebrew`
|
||||
"""
|
||||
|
||||
DECKY_PLUGIN_SETTINGS_DIR: str = os.getenv(
|
||||
"DECKY_PLUGIN_SETTINGS_DIR", default="")
|
||||
"""
|
||||
The recommended path in which to store configuration files (created automatically).
|
||||
Environment variable: `DECKY_PLUGIN_SETTINGS_DIR`.
|
||||
e.g.: `/home/deck/homebrew/settings/decky-plugin-template`
|
||||
"""
|
||||
|
||||
DECKY_PLUGIN_RUNTIME_DIR: str = os.getenv(
|
||||
"DECKY_PLUGIN_RUNTIME_DIR", default="")
|
||||
"""
|
||||
The recommended path in which to store runtime data (created automatically).
|
||||
Environment variable: `DECKY_PLUGIN_RUNTIME_DIR`.
|
||||
e.g.: `/home/deck/homebrew/data/decky-plugin-template`
|
||||
"""
|
||||
|
||||
DECKY_PLUGIN_LOG_DIR: str = os.getenv("DECKY_PLUGIN_LOG_DIR", default="")
|
||||
"""
|
||||
The recommended path in which to store persistent logs (created automatically).
|
||||
Environment variable: `DECKY_PLUGIN_LOG_DIR`.
|
||||
e.g.: `/home/deck/homebrew/logs/decky-plugin-template`
|
||||
"""
|
||||
|
||||
DECKY_PLUGIN_DIR: str = os.getenv("DECKY_PLUGIN_DIR", default="")
|
||||
"""
|
||||
The root of the plugin's directory.
|
||||
Environment variable: `DECKY_PLUGIN_DIR`.
|
||||
e.g.: `/home/deck/homebrew/plugins/decky-plugin-template`
|
||||
"""
|
||||
|
||||
DECKY_PLUGIN_NAME: str = os.getenv("DECKY_PLUGIN_NAME", default="")
|
||||
"""
|
||||
The name of the plugin as specified in the 'plugin.json'.
|
||||
Environment variable: `DECKY_PLUGIN_NAME`.
|
||||
e.g.: `Example Plugin`
|
||||
"""
|
||||
|
||||
DECKY_PLUGIN_VERSION: str = os.getenv("DECKY_PLUGIN_VERSION", default="")
|
||||
"""
|
||||
The version of the plugin as specified in the 'package.json'.
|
||||
Environment variable: `DECKY_PLUGIN_VERSION`.
|
||||
e.g.: `0.0.1`
|
||||
"""
|
||||
|
||||
DECKY_PLUGIN_AUTHOR: str = os.getenv("DECKY_PLUGIN_AUTHOR", default="")
|
||||
"""
|
||||
The author of the plugin as specified in the 'plugin.json'.
|
||||
Environment variable: `DECKY_PLUGIN_AUTHOR`.
|
||||
e.g.: `John Doe`
|
||||
"""
|
||||
|
||||
DECKY_PLUGIN_LOG: str = os.path.join(DECKY_PLUGIN_LOG_DIR, "plugin.log")
|
||||
"""
|
||||
The path to the plugin's main logfile.
|
||||
Environment variable: `DECKY_PLUGIN_LOG`.
|
||||
e.g.: `/home/deck/homebrew/logs/decky-plugin-template/plugin.log`
|
||||
"""
|
||||
|
||||
"""
|
||||
Migration helpers
|
||||
"""
|
||||
|
||||
|
||||
def migrate_any(target_dir: str, *files_or_directories: str) -> dict[str, str]:
|
||||
"""
|
||||
Migrate files and directories to a new location and remove old locations.
|
||||
Specified files will be migrated to `target_dir`.
|
||||
Specified directories will have their contents recursively migrated to `target_dir`.
|
||||
|
||||
Returns the mapping of old -> new location.
|
||||
"""
|
||||
file_map: dict[str, str] = {}
|
||||
for f in files_or_directories:
|
||||
if not os.path.exists(f):
|
||||
file_map[f] = ""
|
||||
continue
|
||||
if os.path.isdir(f):
|
||||
src_dir = f
|
||||
src_file = "."
|
||||
file_map[f] = target_dir
|
||||
else:
|
||||
src_dir = os.path.dirname(f)
|
||||
src_file = os.path.basename(f)
|
||||
file_map[f] = os.path.join(target_dir, src_file)
|
||||
subprocess.run(["sh", "-c", "mkdir -p \"$3\"; tar -cf - -C \"$1\" \"$2\" | tar -xf - -C \"$3\" && rm -rf \"$4\"",
|
||||
"_", src_dir, src_file, target_dir, f])
|
||||
return file_map
|
||||
|
||||
|
||||
def migrate_settings(*files_or_directories: str) -> dict[str, str]:
|
||||
"""
|
||||
Migrate files and directories relating to plugin settings to the recommended location and remove old locations.
|
||||
Specified files will be migrated to `DECKY_PLUGIN_SETTINGS_DIR`.
|
||||
Specified directories will have their contents recursively migrated to `DECKY_PLUGIN_SETTINGS_DIR`.
|
||||
|
||||
Returns the mapping of old -> new location.
|
||||
"""
|
||||
return migrate_any(DECKY_PLUGIN_SETTINGS_DIR, *files_or_directories)
|
||||
|
||||
|
||||
def migrate_runtime(*files_or_directories: str) -> dict[str, str]:
|
||||
"""
|
||||
Migrate files and directories relating to plugin runtime data to the recommended location and remove old locations
|
||||
Specified files will be migrated to `DECKY_PLUGIN_RUNTIME_DIR`.
|
||||
Specified directories will have their contents recursively migrated to `DECKY_PLUGIN_RUNTIME_DIR`.
|
||||
|
||||
Returns the mapping of old -> new location.
|
||||
"""
|
||||
return migrate_any(DECKY_PLUGIN_RUNTIME_DIR, *files_or_directories)
|
||||
|
||||
|
||||
def migrate_logs(*files_or_directories: str) -> dict[str, str]:
|
||||
"""
|
||||
Migrate files and directories relating to plugin logs to the recommended location and remove old locations.
|
||||
Specified files will be migrated to `DECKY_PLUGIN_LOG_DIR`.
|
||||
Specified directories will have their contents recursively migrated to `DECKY_PLUGIN_LOG_DIR`.
|
||||
|
||||
Returns the mapping of old -> new location.
|
||||
"""
|
||||
return migrate_any(DECKY_PLUGIN_LOG_DIR, *files_or_directories)
|
||||
|
||||
|
||||
"""
|
||||
Logging
|
||||
"""
|
||||
|
||||
logging.basicConfig(filename=DECKY_PLUGIN_LOG,
|
||||
format='[%(asctime)s][%(levelname)s]: %(message)s',
|
||||
force=True)
|
||||
logger: logging.Logger = logging.getLogger()
|
||||
"""The main plugin logger writing to `DECKY_PLUGIN_LOG`."""
|
||||
|
||||
logger.setLevel(logging.INFO)
|
||||
@@ -0,0 +1,173 @@
|
||||
"""
|
||||
This module exposes various constants and helpers useful for decky plugins.
|
||||
|
||||
* Plugin's settings and configurations should be stored under `DECKY_PLUGIN_SETTINGS_DIR`.
|
||||
* Plugin's runtime data should be stored under `DECKY_PLUGIN_RUNTIME_DIR`.
|
||||
* Plugin's persistent log files should be stored under `DECKY_PLUGIN_LOG_DIR`.
|
||||
|
||||
Avoid writing outside of `DECKY_HOME`, storing under the suggested paths is strongly recommended.
|
||||
|
||||
Some basic migration helpers are available: `migrate_any`, `migrate_settings`, `migrate_runtime`, `migrate_logs`.
|
||||
|
||||
A logging facility `logger` is available which writes to the recommended location.
|
||||
"""
|
||||
|
||||
__version__ = '0.1.0'
|
||||
|
||||
import logging
|
||||
|
||||
"""
|
||||
Constants
|
||||
"""
|
||||
|
||||
HOME: str
|
||||
"""
|
||||
The home directory of the effective user running the process.
|
||||
Environment variable: `HOME`.
|
||||
If `root` was specified in the plugin's flags it will be `/root` otherwise the user whose home decky resides in.
|
||||
e.g.: `/home/deck`
|
||||
"""
|
||||
|
||||
USER: str
|
||||
"""
|
||||
The effective username running the process.
|
||||
Environment variable: `USER`.
|
||||
It would be `root` if `root` was specified in the plugin's flags otherwise the user whose home decky resides in.
|
||||
e.g.: `deck`
|
||||
"""
|
||||
|
||||
DECKY_VERSION: str
|
||||
"""
|
||||
The version of the decky loader.
|
||||
Environment variable: `DECKY_VERSION`.
|
||||
e.g.: `v2.5.0-pre1`
|
||||
"""
|
||||
|
||||
DECKY_USER: str
|
||||
"""
|
||||
The user whose home decky resides in.
|
||||
Environment variable: `DECKY_USER`.
|
||||
e.g.: `deck`
|
||||
"""
|
||||
|
||||
DECKY_USER_HOME: str
|
||||
"""
|
||||
The home of the user where decky resides in.
|
||||
Environment variable: `DECKY_USER_HOME`.
|
||||
e.g.: `/home/deck`
|
||||
"""
|
||||
|
||||
DECKY_HOME: str
|
||||
"""
|
||||
The root of the decky folder.
|
||||
Environment variable: `DECKY_HOME`.
|
||||
e.g.: `/home/deck/homebrew`
|
||||
"""
|
||||
|
||||
DECKY_PLUGIN_SETTINGS_DIR: str
|
||||
"""
|
||||
The recommended path in which to store configuration files (created automatically).
|
||||
Environment variable: `DECKY_PLUGIN_SETTINGS_DIR`.
|
||||
e.g.: `/home/deck/homebrew/settings/decky-plugin-template`
|
||||
"""
|
||||
|
||||
DECKY_PLUGIN_RUNTIME_DIR: str
|
||||
"""
|
||||
The recommended path in which to store runtime data (created automatically).
|
||||
Environment variable: `DECKY_PLUGIN_RUNTIME_DIR`.
|
||||
e.g.: `/home/deck/homebrew/data/decky-plugin-template`
|
||||
"""
|
||||
|
||||
DECKY_PLUGIN_LOG_DIR: str
|
||||
"""
|
||||
The recommended path in which to store persistent logs (created automatically).
|
||||
Environment variable: `DECKY_PLUGIN_LOG_DIR`.
|
||||
e.g.: `/home/deck/homebrew/logs/decky-plugin-template`
|
||||
"""
|
||||
|
||||
DECKY_PLUGIN_DIR: str
|
||||
"""
|
||||
The root of the plugin's directory.
|
||||
Environment variable: `DECKY_PLUGIN_DIR`.
|
||||
e.g.: `/home/deck/homebrew/plugins/decky-plugin-template`
|
||||
"""
|
||||
|
||||
DECKY_PLUGIN_NAME: str
|
||||
"""
|
||||
The name of the plugin as specified in the 'plugin.json'.
|
||||
Environment variable: `DECKY_PLUGIN_NAME`.
|
||||
e.g.: `Example Plugin`
|
||||
"""
|
||||
|
||||
DECKY_PLUGIN_VERSION: str
|
||||
"""
|
||||
The version of the plugin as specified in the 'package.json'.
|
||||
Environment variable: `DECKY_PLUGIN_VERSION`.
|
||||
e.g.: `0.0.1`
|
||||
"""
|
||||
|
||||
DECKY_PLUGIN_AUTHOR: str
|
||||
"""
|
||||
The author of the plugin as specified in the 'plugin.json'.
|
||||
Environment variable: `DECKY_PLUGIN_AUTHOR`.
|
||||
e.g.: `John Doe`
|
||||
"""
|
||||
|
||||
DECKY_PLUGIN_LOG: str
|
||||
"""
|
||||
The path to the plugin's main logfile.
|
||||
Environment variable: `DECKY_PLUGIN_LOG`.
|
||||
e.g.: `/home/deck/homebrew/logs/decky-plugin-template/plugin.log`
|
||||
"""
|
||||
|
||||
"""
|
||||
Migration helpers
|
||||
"""
|
||||
|
||||
|
||||
def migrate_any(target_dir: str, *files_or_directories: str) -> dict[str, str]:
|
||||
"""
|
||||
Migrate files and directories to a new location and remove old locations.
|
||||
Specified files will be migrated to `target_dir`.
|
||||
Specified directories will have their contents recursively migrated to `target_dir`.
|
||||
|
||||
Returns the mapping of old -> new location.
|
||||
"""
|
||||
|
||||
|
||||
def migrate_settings(*files_or_directories: str) -> dict[str, str]:
|
||||
"""
|
||||
Migrate files and directories relating to plugin settings to the recommended location and remove old locations.
|
||||
Specified files will be migrated to `DECKY_PLUGIN_SETTINGS_DIR`.
|
||||
Specified directories will have their contents recursively migrated to `DECKY_PLUGIN_SETTINGS_DIR`.
|
||||
|
||||
Returns the mapping of old -> new location.
|
||||
"""
|
||||
|
||||
|
||||
def migrate_runtime(*files_or_directories: str) -> dict[str, str]:
|
||||
"""
|
||||
Migrate files and directories relating to plugin runtime data to the recommended location and remove old locations
|
||||
Specified files will be migrated to `DECKY_PLUGIN_RUNTIME_DIR`.
|
||||
Specified directories will have their contents recursively migrated to `DECKY_PLUGIN_RUNTIME_DIR`.
|
||||
|
||||
Returns the mapping of old -> new location.
|
||||
"""
|
||||
|
||||
|
||||
def migrate_logs(*files_or_directories: str) -> dict[str, str]:
|
||||
"""
|
||||
Migrate files and directories relating to plugin logs to the recommended location and remove old locations.
|
||||
Specified files will be migrated to `DECKY_PLUGIN_LOG_DIR`.
|
||||
Specified directories will have their contents recursively migrated to `DECKY_PLUGIN_LOG_DIR`.
|
||||
|
||||
Returns the mapping of old -> new location.
|
||||
"""
|
||||
|
||||
|
||||
"""
|
||||
Logging
|
||||
"""
|
||||
|
||||
logger: logging.Logger
|
||||
"""The main plugin logger writing to `DECKY_PLUGIN_LOG`."""
|
||||
Reference in New Issue
Block a user