mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-07-01 23:39:55 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7043256232 | |||
| c1f01dcb21 | |||
| fb3de87921 | |||
| 846995e0d3 | |||
| f73aced452 | |||
| a45df8566e | |||
| 2b2a1d9e39 | |||
| 86df2e0fd9 | |||
| f5eba51c52 | |||
| 83972972f3 | |||
| 2c4edeb810 | |||
| b32fa7948f | |||
| 9a098ec942 |
@@ -15,14 +15,14 @@
|
||||
<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%">
|
||||
<!-- <img src="https://media.discordapp.net/attachments/966017112244125756/1012466063893610506/main.jpg" alt="Decky screenshot" width="80%">-->
|
||||
</p>
|
||||
|
||||
## 📖 About
|
||||
|
||||
Decky Loader is a homebrew plugin launcher for the Steam Deck. It can be used to [stylize your menus](https://github.com/suchmememanyskill/SDH-CssLoader), [change system sounds](https://github.com/EMERALD0874/SDH-AudioLoader), [adjust your screen saturation](https://github.com/libvibrant/vibrantDeck), [change additional system settings](https://github.com/NGnius/PowerTools), and [more](https://plugins.deckbrew.xyz/).
|
||||
|
||||
For more information about Decky Loader as well as documentation and development tools, please visit [our wiki](https://deckbrew.xyz).
|
||||
For more information about Decky Loader as well as documentation and development tools, please visit [our wiki](https://wiki.deckbrew.xyz).
|
||||
|
||||
### 🎨 Features
|
||||
|
||||
|
||||
@@ -147,8 +147,9 @@ class PluginBrowser:
|
||||
except Exception as e:
|
||||
logger.error(f"Plugin {name} in {plugin_dir} was not uninstalled")
|
||||
logger.error(f"Error at {str(e)}", exc_info=e)
|
||||
if self.loader.watcher:
|
||||
self.loader.watcher.disabled = False
|
||||
finally:
|
||||
if self.loader.watcher:
|
||||
self.loader.watcher.disabled = False
|
||||
|
||||
async def _install(self, artifact: str, name: str, version: str, hash: str):
|
||||
await self.loader.ws.emit("loader/plugin_download_start", name)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from __future__ import annotations
|
||||
from asyncio import AbstractEventLoop, Queue, sleep
|
||||
from asyncio import AbstractEventLoop, Queue, gather, sleep
|
||||
from logging import getLogger
|
||||
from os import listdir, path
|
||||
from pathlib import Path
|
||||
@@ -98,6 +98,9 @@ class Loader:
|
||||
server_instance.ws.add_route("loader/call_plugin_method", self.handle_plugin_method_call)
|
||||
server_instance.ws.add_route("loader/call_legacy_plugin_method", self.handle_plugin_method_call_legacy)
|
||||
|
||||
async def shutdown_plugins(self):
|
||||
await gather(*[self.plugins[plugin_name].stop() for plugin_name in self.plugins])
|
||||
|
||||
async def enable_reload_wait(self):
|
||||
if self.live_reload:
|
||||
await sleep(10)
|
||||
|
||||
@@ -19,10 +19,15 @@ class UnixSocket:
|
||||
self.reader = None
|
||||
self.writer = None
|
||||
self.server_writer = None
|
||||
self.open_lock = asyncio.Lock()
|
||||
|
||||
async def setup_server(self):
|
||||
self.socket = await asyncio.start_unix_server(self._listen_for_method_call, path=self.socket_addr, limit=BUFFER_LIMIT)
|
||||
|
||||
try:
|
||||
self.socket = await asyncio.start_unix_server(self._listen_for_method_call, path=self.socket_addr, limit=BUFFER_LIMIT)
|
||||
except asyncio.CancelledError:
|
||||
await self.close_socket_connection()
|
||||
raise
|
||||
|
||||
async def _open_socket_if_not_exists(self):
|
||||
if not self.reader:
|
||||
retries = 0
|
||||
@@ -38,10 +43,11 @@ class UnixSocket:
|
||||
return True
|
||||
|
||||
async def get_socket_connection(self):
|
||||
if not await self._open_socket_if_not_exists():
|
||||
return None, None
|
||||
|
||||
return self.reader, self.writer
|
||||
async with self.open_lock:
|
||||
if not await self._open_socket_if_not_exists():
|
||||
return None, None
|
||||
|
||||
return self.reader, self.writer
|
||||
|
||||
async def close_socket_connection(self):
|
||||
if self.writer != None:
|
||||
@@ -49,6 +55,10 @@ class UnixSocket:
|
||||
|
||||
self.reader = None
|
||||
|
||||
if self.socket:
|
||||
self.socket.close()
|
||||
await self.socket.wait_closed()
|
||||
|
||||
async def read_single_line(self) -> str|None:
|
||||
reader, _ = await self.get_socket_connection()
|
||||
|
||||
@@ -80,6 +90,8 @@ class UnixSocket:
|
||||
except asyncio.IncompleteReadError as err:
|
||||
line.extend(err.partial)
|
||||
break
|
||||
except asyncio.CancelledError:
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
@@ -121,8 +133,12 @@ class PortSocket (UnixSocket):
|
||||
self.port = random.sample(range(40000, 60000), 1)[0]
|
||||
|
||||
async def setup_server(self):
|
||||
self.socket = await asyncio.start_server(self._listen_for_method_call, host=self.host, port=self.port, limit=BUFFER_LIMIT)
|
||||
|
||||
try:
|
||||
self.socket = await asyncio.start_server(self._listen_for_method_call, host=self.host, port=self.port, limit=BUFFER_LIMIT)
|
||||
except asyncio.CancelledError:
|
||||
await self.close_socket_connection()
|
||||
raise
|
||||
|
||||
async def _open_socket_if_not_exists(self):
|
||||
if not self.reader:
|
||||
retries = 0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Change PyInstaller files permissions
|
||||
import sys
|
||||
from typing import Dict
|
||||
from typing import Any, Dict
|
||||
from .localplatform.localplatform import (chmod, chown, service_stop, service_start,
|
||||
ON_WINDOWS, ON_LINUX, get_log_level, get_live_reload,
|
||||
get_server_port, get_server_host, get_chown_plugin_path,
|
||||
@@ -8,7 +8,7 @@ from .localplatform.localplatform import (chmod, chown, service_stop, service_st
|
||||
if hasattr(sys, '_MEIPASS'):
|
||||
chmod(sys._MEIPASS, 755) # type: ignore
|
||||
# Full imports
|
||||
from asyncio import AbstractEventLoop, new_event_loop, set_event_loop, sleep
|
||||
from asyncio import AbstractEventLoop, CancelledError, Task, all_tasks, current_task, gather, new_event_loop, set_event_loop, sleep
|
||||
from logging import basicConfig, getLogger
|
||||
from os import path
|
||||
from traceback import format_exc
|
||||
@@ -19,6 +19,7 @@ import aiohttp_cors # pyright: ignore [reportMissingTypeStubs]
|
||||
from aiohttp import client_exceptions
|
||||
from aiohttp.web import Application, Response, Request, get, run_app, static # pyright: ignore [reportUnknownVariableType]
|
||||
from aiohttp_jinja2 import setup as jinja_setup
|
||||
from setproctitle import getproctitle, setproctitle, setthreadtitle
|
||||
|
||||
# local modules
|
||||
from .browser import PluginBrowser
|
||||
@@ -55,6 +56,8 @@ if get_chown_plugin_path() == True:
|
||||
class PluginManager:
|
||||
def __init__(self, loop: AbstractEventLoop) -> None:
|
||||
self.loop = loop
|
||||
self.reinject: bool = True
|
||||
self.js_ctx_tab: Tab | None = None
|
||||
self.web_app = Application()
|
||||
self.web_app.middlewares.append(csrf_middleware)
|
||||
self.cors = aiohttp_cors.setup(self.web_app, defaults={
|
||||
@@ -82,6 +85,7 @@ class PluginManager:
|
||||
self.loop.create_task(self.load_plugins())
|
||||
|
||||
self.web_app.on_startup.append(startup)
|
||||
self.web_app.on_shutdown.append(self.shutdown)
|
||||
|
||||
self.loop.set_exception_handler(self.exception_handler)
|
||||
self.web_app.add_routes([get("/auth/token", self.get_auth_token)])
|
||||
@@ -90,6 +94,40 @@ class PluginManager:
|
||||
self.cors.add(route) # pyright: ignore [reportUnknownMemberType]
|
||||
self.web_app.add_routes([static("/static", path.join(path.dirname(__file__), 'static'))])
|
||||
|
||||
async def shutdown(self, _: Application):
|
||||
try:
|
||||
logger.info(f"Shutting down...")
|
||||
await self.plugin_loader.shutdown_plugins()
|
||||
await self.ws.disconnect()
|
||||
self.reinject = False
|
||||
if self.js_ctx_tab:
|
||||
await self.js_ctx_tab.close_websocket()
|
||||
self.js_ctx_tab = None
|
||||
except:
|
||||
logger.info("Error during shutdown:\n" + format_exc())
|
||||
pass
|
||||
finally:
|
||||
logger.info("Cancelling tasks...")
|
||||
tasks = all_tasks()
|
||||
current = current_task()
|
||||
async def cancel_task(task: Task[Any]):
|
||||
logger.debug(f"Cancelling task {task}")
|
||||
try:
|
||||
task.cancel()
|
||||
try:
|
||||
await task
|
||||
except CancelledError:
|
||||
pass
|
||||
logger.debug(f"Task {task} finished")
|
||||
except:
|
||||
logger.warn(f"Failed to cancel task {task}:\n" + format_exc())
|
||||
pass
|
||||
if current:
|
||||
tasks.remove(current)
|
||||
await gather(*[cancel_task(task) for task in tasks])
|
||||
|
||||
logger.info("Shutdown finished.")
|
||||
|
||||
def exception_handler(self, loop: AbstractEventLoop, context: Dict[str, str]):
|
||||
if context["message"] == "Unclosed connection":
|
||||
return
|
||||
@@ -107,11 +145,13 @@ class PluginManager:
|
||||
logger.debug("Did not find pluginOrder setting, set it to default")
|
||||
|
||||
async def loader_reinjector(self):
|
||||
while True:
|
||||
while self.reinject:
|
||||
tab = None
|
||||
nf = False
|
||||
dc = False
|
||||
while not tab:
|
||||
if not self.reinject:
|
||||
return
|
||||
try:
|
||||
tab = await get_gamepadui_tab()
|
||||
except (client_exceptions.ClientConnectorError, client_exceptions.ServerDisconnectedError):
|
||||
@@ -127,6 +167,7 @@ class PluginManager:
|
||||
if not tab:
|
||||
await sleep(5)
|
||||
await tab.open_websocket()
|
||||
self.js_ctx_tab = tab
|
||||
await tab.enable()
|
||||
await self.inject_javascript(tab, True)
|
||||
try:
|
||||
@@ -135,16 +176,22 @@ class PluginManager:
|
||||
if not await tab.has_global_var("deckyHasLoaded", False):
|
||||
await self.inject_javascript(tab)
|
||||
elif msg.get("method", None) == "Inspector.detached":
|
||||
if not self.reinject:
|
||||
return
|
||||
logger.info("CEF has requested that we detach.")
|
||||
await tab.close_websocket()
|
||||
self.js_ctx_tab = None
|
||||
break
|
||||
# If this is a forceful disconnect the loop will just stop without any failure message. In this case, injector.py will handle this for us so we don't need to close the socket.
|
||||
# This is because of https://github.com/aio-libs/aiohttp/blob/3ee7091b40a1bc58a8d7846e7878a77640e96996/aiohttp/client_ws.py#L321
|
||||
logger.info("CEF has disconnected...")
|
||||
# At this point the loop starts again and we connect to the freshly started Steam client once it is ready.
|
||||
except Exception:
|
||||
if not self.reinject:
|
||||
return
|
||||
logger.error("Exception while reading page events " + format_exc())
|
||||
await tab.close_websocket()
|
||||
self.js_ctx_tab = None
|
||||
pass
|
||||
# while True:
|
||||
# await sleep(5)
|
||||
@@ -157,6 +204,8 @@ class PluginManager:
|
||||
try:
|
||||
# if first:
|
||||
if ON_LINUX and await tab.has_global_var("deckyHasLoaded", False):
|
||||
await tab.close_websocket()
|
||||
self.js_ctx_tab = None
|
||||
await restart_webhelper()
|
||||
return # We'll catch the next tab in the main loop
|
||||
await tab.evaluate_js("try{if (window.deckyHasLoaded){setTimeout(() => SteamClient.Browser.RestartJSContext(), 100)}else{window.deckyHasLoaded = true;(async()=>{try{await import('http://localhost:1337/frontend/index.js?v=%s')}catch(e){console.error(e)};})();}}catch(e){console.error(e)}" % (get_loader_version(), ), False, False, False)
|
||||
@@ -165,9 +214,11 @@ class PluginManager:
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
return run_app(self.web_app, host=get_server_host(), port=get_server_port(), loop=self.loop, access_log=None)
|
||||
run_app(self.web_app, host=get_server_host(), port=get_server_port(), loop=self.loop, access_log=None, handle_signals=True, shutdown_timeout=40)
|
||||
|
||||
def main():
|
||||
setproctitle(f"Decky Loader {get_loader_version()} ({getproctitle()})")
|
||||
setthreadtitle("Decky Loader")
|
||||
if ON_WINDOWS:
|
||||
# Fix windows/flask not recognising that .js means 'application/javascript'
|
||||
import mimetypes
|
||||
@@ -176,8 +227,8 @@ def main():
|
||||
# Required for multiprocessing support in frozen files
|
||||
multiprocessing.freeze_support()
|
||||
else:
|
||||
if get_effective_user_id() != 0:
|
||||
logger.warning(f"decky is running as an unprivileged user, this is not officially supported and may cause issues")
|
||||
if get_effective_user_id() != 0:
|
||||
logger.warning(f"decky is running as an unprivileged user, this is not officially supported and may cause issues")
|
||||
|
||||
# Append the system and user python paths
|
||||
sys.path.extend(get_system_pythonpaths())
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from asyncio import Task, create_task
|
||||
from asyncio import CancelledError, Task, create_task, sleep
|
||||
from json import dumps, load, loads
|
||||
from logging import getLogger
|
||||
from os import path
|
||||
@@ -41,6 +41,7 @@ class PluginWrapper:
|
||||
self.log = getLogger("plugin")
|
||||
|
||||
self.sandboxed_plugin = SandboxedPlugin(self.name, self.passive, self.flags, self.file, self.plugin_directory, self.plugin_path, self.version, self.author, self.api_version)
|
||||
self.proc: Process | None = None
|
||||
# TODO: Maybe make LocalSocket not require on_new_message to make this cleaner
|
||||
self._socket = LocalSocket(self.sandboxed_plugin.on_new_message)
|
||||
self._listener_task: Task[Any]
|
||||
@@ -73,6 +74,10 @@ class PluginWrapper:
|
||||
create_task(self.emitted_event_callback(res["event"], res["args"]))
|
||||
elif res["type"] == SocketMessageType.RESPONSE.value:
|
||||
self._method_call_requests.pop(res["id"]).set_result(res)
|
||||
except CancelledError:
|
||||
self.log.info(f"Stopping response listener for {self.name}")
|
||||
await self._socket.close_socket_connection()
|
||||
raise
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -104,13 +109,39 @@ class PluginWrapper:
|
||||
def start(self):
|
||||
if self.passive:
|
||||
return self
|
||||
Process(target=self.sandboxed_plugin.initialize, args=[self._socket]).start()
|
||||
self.proc = Process(target=self.sandboxed_plugin.initialize, args=[self._socket])
|
||||
self.proc.start()
|
||||
self._listener_task = create_task(self._response_listener())
|
||||
return self
|
||||
|
||||
async def stop(self, uninstall: bool = False):
|
||||
self.log.info(f"Stopping plugin {self.name}")
|
||||
if self.passive:
|
||||
return
|
||||
if hasattr(self, "_socket"):
|
||||
await self._socket.write_single_line(dumps({ "stop": True, "uninstall": uninstall }, ensure_ascii=False))
|
||||
await self._socket.close_socket_connection()
|
||||
if self.proc:
|
||||
self.proc.join()
|
||||
await self.kill_if_still_running()
|
||||
if hasattr(self, "_listener_task"):
|
||||
self._listener_task.cancel()
|
||||
self._listener_task.cancel()
|
||||
|
||||
async def kill_if_still_running(self):
|
||||
time = 0
|
||||
while self.proc and self.proc.is_alive():
|
||||
await sleep(0.1)
|
||||
time += 1
|
||||
if time == 100:
|
||||
self.log.warn(f"Plugin {self.name} still alive 10 seconds after stop request! Sending SIGTERM!")
|
||||
self.terminate()
|
||||
elif time == 200:
|
||||
self.log.warn(f"Plugin {self.name} still alive 20 seconds after stop request! Sending SIGKILL!")
|
||||
self.terminate(True)
|
||||
|
||||
def terminate(self, kill: bool = False):
|
||||
if self.proc and self.proc.is_alive():
|
||||
if kill:
|
||||
self.proc.kill()
|
||||
else:
|
||||
self.proc.terminate()
|
||||
@@ -1,12 +1,13 @@
|
||||
from os import path, environ
|
||||
from signal import SIGINT, signal
|
||||
from signal import SIG_IGN, SIGINT, SIGTERM, getsignal, signal
|
||||
from importlib.util import module_from_spec, spec_from_file_location
|
||||
from json import dumps, loads
|
||||
from logging import getLogger
|
||||
from sys import exit, path as syspath, modules as sysmodules
|
||||
from traceback import format_exc
|
||||
from asyncio import (get_event_loop, new_event_loop,
|
||||
set_event_loop, sleep)
|
||||
set_event_loop)
|
||||
from setproctitle import setproctitle, setthreadtitle
|
||||
|
||||
from .messages import SocketResponseDict, SocketMessageType
|
||||
from ..localplatform.localsocket import LocalSocket
|
||||
@@ -18,6 +19,8 @@ from typing import List, TypeVar, Any
|
||||
|
||||
DataType = TypeVar("DataType")
|
||||
|
||||
original_term_handler = getsignal(SIGTERM)
|
||||
|
||||
class SandboxedPlugin:
|
||||
def __init__(self,
|
||||
name: str,
|
||||
@@ -39,13 +42,19 @@ class SandboxedPlugin:
|
||||
self.author = author
|
||||
self.api_version = api_version
|
||||
|
||||
self.log = getLogger("plugin")
|
||||
self.log = getLogger("sandboxed_plugin")
|
||||
|
||||
def initialize(self, socket: LocalSocket):
|
||||
self._socket = socket
|
||||
|
||||
try:
|
||||
signal(SIGINT, lambda s, f: exit(0))
|
||||
# Ignore signals meant for parent Process
|
||||
# TODO SURELY there's a better way to do this.
|
||||
signal(SIGINT, SIG_IGN)
|
||||
signal(SIGTERM, SIG_IGN)
|
||||
|
||||
setproctitle(f"{self.name} ({self.file})")
|
||||
setthreadtitle(self.name)
|
||||
|
||||
set_event_loop(new_event_loop())
|
||||
if self.passive:
|
||||
@@ -112,10 +121,17 @@ class SandboxedPlugin:
|
||||
else:
|
||||
get_event_loop().create_task(self.Plugin._main(self.Plugin))
|
||||
get_event_loop().create_task(socket.setup_server())
|
||||
get_event_loop().run_forever()
|
||||
except:
|
||||
self.log.error("Failed to start " + self.name + "!\n" + format_exc())
|
||||
exit(0)
|
||||
try:
|
||||
get_event_loop().run_forever()
|
||||
except SystemExit:
|
||||
pass
|
||||
except:
|
||||
self.log.error("Loop exited for " + self.name + "!\n" + format_exc())
|
||||
finally:
|
||||
get_event_loop().close()
|
||||
|
||||
async def _unload(self):
|
||||
try:
|
||||
@@ -130,7 +146,7 @@ class SandboxedPlugin:
|
||||
self.log.info("Could not find \"_unload\" in " + self.name + "'s main.py" + "\n")
|
||||
except:
|
||||
self.log.error("Failed to unload " + self.name + "!\n" + format_exc())
|
||||
exit(0)
|
||||
pass
|
||||
|
||||
async def _uninstall(self):
|
||||
try:
|
||||
@@ -145,24 +161,26 @@ class SandboxedPlugin:
|
||||
self.log.info("Could not find \"_uninstall\" in " + self.name + "'s main.py" + "\n")
|
||||
except:
|
||||
self.log.error("Failed to uninstall " + self.name + "!\n" + format_exc())
|
||||
exit(0)
|
||||
pass
|
||||
|
||||
async def on_new_message(self, message : str) -> str|None:
|
||||
data = loads(message)
|
||||
|
||||
if "stop" in data:
|
||||
self.log.info("Calling Loader unload function.")
|
||||
# Incase the loader needs to terminate our process soon
|
||||
signal(SIGTERM, original_term_handler)
|
||||
self.log.info(f"Calling Loader unload function for {self.name}.")
|
||||
await self._unload()
|
||||
|
||||
if data.get('uninstall'):
|
||||
self.log.info("Calling Loader uninstall function.")
|
||||
await self._uninstall()
|
||||
|
||||
get_event_loop().stop()
|
||||
while get_event_loop().is_running():
|
||||
await sleep(0)
|
||||
get_event_loop().close()
|
||||
raise Exception("Closing message listener")
|
||||
self.log.debug("Stopping event loop")
|
||||
|
||||
loop = get_event_loop()
|
||||
loop.call_soon_threadsafe(loop.stop)
|
||||
exit(0)
|
||||
|
||||
d: SocketResponseDict = {"type": SocketMessageType.RESPONSE, "res": None, "success": True, "id": data["id"]}
|
||||
try:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from logging import getLogger
|
||||
|
||||
from asyncio import AbstractEventLoop, create_task
|
||||
from asyncio import AbstractEventLoop
|
||||
|
||||
from aiohttp import WSMsgType, WSMessage
|
||||
from aiohttp import WSCloseCode, WSMsgType, WSMessage
|
||||
from aiohttp.web import Application, WebSocketResponse, Request, Response, get
|
||||
|
||||
from enum import IntEnum
|
||||
@@ -114,10 +114,10 @@ class WSRouter:
|
||||
# do stuff with the message
|
||||
if data["route"] in self.routes:
|
||||
self.logger.debug(f'Started PY call {data["route"]} ID {data["id"]}')
|
||||
create_task(self._call_route(data["route"], data["args"], data["id"]))
|
||||
self.loop.create_task(self._call_route(data["route"], data["args"], data["id"]))
|
||||
else:
|
||||
error = {"error":f'Route {data["route"]} does not exist.', "name": "RouteNotFoundError", "traceback": None}
|
||||
create_task(self.write({"type": MessageType.ERROR.value, "id": data["id"], "error": error}))
|
||||
self.loop.create_task(self.write({"type": MessageType.ERROR.value, "id": data["id"], "error": error}))
|
||||
case _:
|
||||
self.logger.error("Unknown message type", data)
|
||||
finally:
|
||||
@@ -133,3 +133,7 @@ class WSRouter:
|
||||
async def emit(self, event: str, *args: Any):
|
||||
self.logger.debug(f'Firing frontend event {event} with args {args}')
|
||||
await self.write({ "type": MessageType.EVENT.value, "event": event, "args": args })
|
||||
|
||||
async def disconnect(self):
|
||||
if self.ws:
|
||||
await self.ws.close(code=WSCloseCode.GOING_AWAY, message=b"Loader is shutting down")
|
||||
|
||||
Generated
+101
-1
@@ -598,6 +598,106 @@ files = [
|
||||
{file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "setproctitle"
|
||||
version = "1.3.3"
|
||||
description = "A Python module to customize the process title"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "setproctitle-1.3.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:897a73208da48db41e687225f355ce993167079eda1260ba5e13c4e53be7f754"},
|
||||
{file = "setproctitle-1.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c331e91a14ba4076f88c29c777ad6b58639530ed5b24b5564b5ed2fd7a95452"},
|
||||
{file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbbd6c7de0771c84b4aa30e70b409565eb1fc13627a723ca6be774ed6b9d9fa3"},
|
||||
{file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c05ac48ef16ee013b8a326c63e4610e2430dbec037ec5c5b58fcced550382b74"},
|
||||
{file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1342f4fdb37f89d3e3c1c0a59d6ddbedbde838fff5c51178a7982993d238fe4f"},
|
||||
{file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc74e84fdfa96821580fb5e9c0b0777c1c4779434ce16d3d62a9c4d8c710df39"},
|
||||
{file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9617b676b95adb412bb69645d5b077d664b6882bb0d37bfdafbbb1b999568d85"},
|
||||
{file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6a249415f5bb88b5e9e8c4db47f609e0bf0e20a75e8d744ea787f3092ba1f2d0"},
|
||||
{file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:38da436a0aaace9add67b999eb6abe4b84397edf4a78ec28f264e5b4c9d53cd5"},
|
||||
{file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:da0d57edd4c95bf221b2ebbaa061e65b1788f1544977288bdf95831b6e44e44d"},
|
||||
{file = "setproctitle-1.3.3-cp310-cp310-win32.whl", hash = "sha256:a1fcac43918b836ace25f69b1dca8c9395253ad8152b625064415b1d2f9be4fb"},
|
||||
{file = "setproctitle-1.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:200620c3b15388d7f3f97e0ae26599c0c378fdf07ae9ac5a13616e933cbd2086"},
|
||||
{file = "setproctitle-1.3.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:334f7ed39895d692f753a443102dd5fed180c571eb6a48b2a5b7f5b3564908c8"},
|
||||
{file = "setproctitle-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:950f6476d56ff7817a8fed4ab207727fc5260af83481b2a4b125f32844df513a"},
|
||||
{file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:195c961f54a09eb2acabbfc90c413955cf16c6e2f8caa2adbf2237d1019c7dd8"},
|
||||
{file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f05e66746bf9fe6a3397ec246fe481096664a9c97eb3fea6004735a4daf867fd"},
|
||||
{file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b5901a31012a40ec913265b64e48c2a4059278d9f4e6be628441482dd13fb8b5"},
|
||||
{file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64286f8a995f2cd934082b398fc63fca7d5ffe31f0e27e75b3ca6b4efda4e353"},
|
||||
{file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:184239903bbc6b813b1a8fc86394dc6ca7d20e2ebe6f69f716bec301e4b0199d"},
|
||||
{file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:664698ae0013f986118064b6676d7dcd28fefd0d7d5a5ae9497cbc10cba48fa5"},
|
||||
{file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e5119a211c2e98ff18b9908ba62a3bd0e3fabb02a29277a7232a6fb4b2560aa0"},
|
||||
{file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:417de6b2e214e837827067048f61841f5d7fc27926f2e43954567094051aff18"},
|
||||
{file = "setproctitle-1.3.3-cp311-cp311-win32.whl", hash = "sha256:6a143b31d758296dc2f440175f6c8e0b5301ced3b0f477b84ca43cdcf7f2f476"},
|
||||
{file = "setproctitle-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:a680d62c399fa4b44899094027ec9a1bdaf6f31c650e44183b50d4c4d0ccc085"},
|
||||
{file = "setproctitle-1.3.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d4460795a8a7a391e3567b902ec5bdf6c60a47d791c3b1d27080fc203d11c9dc"},
|
||||
{file = "setproctitle-1.3.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bdfd7254745bb737ca1384dee57e6523651892f0ea2a7344490e9caefcc35e64"},
|
||||
{file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:477d3da48e216d7fc04bddab67b0dcde633e19f484a146fd2a34bb0e9dbb4a1e"},
|
||||
{file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ab2900d111e93aff5df9fddc64cf51ca4ef2c9f98702ce26524f1acc5a786ae7"},
|
||||
{file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:088b9efc62d5aa5d6edf6cba1cf0c81f4488b5ce1c0342a8b67ae39d64001120"},
|
||||
{file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6d50252377db62d6a0bb82cc898089916457f2db2041e1d03ce7fadd4a07381"},
|
||||
{file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:87e668f9561fd3a457ba189edfc9e37709261287b52293c115ae3487a24b92f6"},
|
||||
{file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:287490eb90e7a0ddd22e74c89a92cc922389daa95babc833c08cf80c84c4df0a"},
|
||||
{file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:4fe1c49486109f72d502f8be569972e27f385fe632bd8895f4730df3c87d5ac8"},
|
||||
{file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4a6ba2494a6449b1f477bd3e67935c2b7b0274f2f6dcd0f7c6aceae10c6c6ba3"},
|
||||
{file = "setproctitle-1.3.3-cp312-cp312-win32.whl", hash = "sha256:2df2b67e4b1d7498632e18c56722851ba4db5d6a0c91aaf0fd395111e51cdcf4"},
|
||||
{file = "setproctitle-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:f38d48abc121263f3b62943f84cbaede05749047e428409c2c199664feb6abc7"},
|
||||
{file = "setproctitle-1.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:816330675e3504ae4d9a2185c46b573105d2310c20b19ea2b4596a9460a4f674"},
|
||||
{file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68f960bc22d8d8e4ac886d1e2e21ccbd283adcf3c43136161c1ba0fa509088e0"},
|
||||
{file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00e6e7adff74796ef12753ff399491b8827f84f6c77659d71bd0b35870a17d8f"},
|
||||
{file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53bc0d2358507596c22b02db079618451f3bd720755d88e3cccd840bafb4c41c"},
|
||||
{file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad6d20f9541f5f6ac63df553b6d7a04f313947f550eab6a61aa758b45f0d5657"},
|
||||
{file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c1c84beab776b0becaa368254801e57692ed749d935469ac10e2b9b825dbdd8e"},
|
||||
{file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:507e8dc2891021350eaea40a44ddd887c9f006e6b599af8d64a505c0f718f170"},
|
||||
{file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b1067647ac7aba0b44b591936118a22847bda3c507b0a42d74272256a7a798e9"},
|
||||
{file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2e71f6365744bf53714e8bd2522b3c9c1d83f52ffa6324bd7cbb4da707312cd8"},
|
||||
{file = "setproctitle-1.3.3-cp37-cp37m-win32.whl", hash = "sha256:7f1d36a1e15a46e8ede4e953abb104fdbc0845a266ec0e99cc0492a4364f8c44"},
|
||||
{file = "setproctitle-1.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9a402881ec269d0cc9c354b149fc29f9ec1a1939a777f1c858cdb09c7a261df"},
|
||||
{file = "setproctitle-1.3.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ff814dea1e5c492a4980e3e7d094286077054e7ea116cbeda138819db194b2cd"},
|
||||
{file = "setproctitle-1.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:accb66d7b3ccb00d5cd11d8c6e07055a4568a24c95cf86109894dcc0c134cc89"},
|
||||
{file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:554eae5a5b28f02705b83a230e9d163d645c9a08914c0ad921df363a07cf39b1"},
|
||||
{file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a911b26264dbe9e8066c7531c0591cfab27b464459c74385b276fe487ca91c12"},
|
||||
{file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2982efe7640c4835f7355fdb4da313ad37fb3b40f5c69069912f8048f77b28c8"},
|
||||
{file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df3f4274b80709d8bcab2f9a862973d453b308b97a0b423a501bcd93582852e3"},
|
||||
{file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:af2c67ae4c795d1674a8d3ac1988676fa306bcfa1e23fddb5e0bd5f5635309ca"},
|
||||
{file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:af4061f67fd7ec01624c5e3c21f6b7af2ef0e6bab7fbb43f209e6506c9ce0092"},
|
||||
{file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:37a62cbe16d4c6294e84670b59cf7adcc73faafe6af07f8cb9adaf1f0e775b19"},
|
||||
{file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a83ca086fbb017f0d87f240a8f9bbcf0809f3b754ee01cec928fff926542c450"},
|
||||
{file = "setproctitle-1.3.3-cp38-cp38-win32.whl", hash = "sha256:059f4ce86f8cc92e5860abfc43a1dceb21137b26a02373618d88f6b4b86ba9b2"},
|
||||
{file = "setproctitle-1.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:ab92e51cd4a218208efee4c6d37db7368fdf182f6e7ff148fb295ecddf264287"},
|
||||
{file = "setproctitle-1.3.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c7951820b77abe03d88b114b998867c0f99da03859e5ab2623d94690848d3e45"},
|
||||
{file = "setproctitle-1.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5bc94cf128676e8fac6503b37763adb378e2b6be1249d207630f83fc325d9b11"},
|
||||
{file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f5d9027eeda64d353cf21a3ceb74bb1760bd534526c9214e19f052424b37e42"},
|
||||
{file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e4a8104db15d3462e29d9946f26bed817a5b1d7a47eabca2d9dc2b995991503"},
|
||||
{file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c32c41ace41f344d317399efff4cffb133e709cec2ef09c99e7a13e9f3b9483c"},
|
||||
{file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbf16381c7bf7f963b58fb4daaa65684e10966ee14d26f5cc90f07049bfd8c1e"},
|
||||
{file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e18b7bd0898398cc97ce2dfc83bb192a13a087ef6b2d5a8a36460311cb09e775"},
|
||||
{file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:69d565d20efe527bd8a9b92e7f299ae5e73b6c0470f3719bd66f3cd821e0d5bd"},
|
||||
{file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ddedd300cd690a3b06e7eac90ed4452348b1348635777ce23d460d913b5b63c3"},
|
||||
{file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:415bfcfd01d1fbf5cbd75004599ef167a533395955305f42220a585f64036081"},
|
||||
{file = "setproctitle-1.3.3-cp39-cp39-win32.whl", hash = "sha256:21112fcd2195d48f25760f0eafa7a76510871bbb3b750219310cf88b04456ae3"},
|
||||
{file = "setproctitle-1.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:5a740f05d0968a5a17da3d676ce6afefebeeeb5ce137510901bf6306ba8ee002"},
|
||||
{file = "setproctitle-1.3.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6b9e62ddb3db4b5205c0321dd69a406d8af9ee1693529d144e86bd43bcb4b6c0"},
|
||||
{file = "setproctitle-1.3.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e3b99b338598de0bd6b2643bf8c343cf5ff70db3627af3ca427a5e1a1a90dd9"},
|
||||
{file = "setproctitle-1.3.3-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ae9a02766dad331deb06855fb7a6ca15daea333b3967e214de12cfae8f0ef5"},
|
||||
{file = "setproctitle-1.3.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:200ede6fd11233085ba9b764eb055a2a191fb4ffb950c68675ac53c874c22e20"},
|
||||
{file = "setproctitle-1.3.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0d3a953c50776751e80fe755a380a64cb14d61e8762bd43041ab3f8cc436092f"},
|
||||
{file = "setproctitle-1.3.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5e08e232b78ba3ac6bc0d23ce9e2bee8fad2be391b7e2da834fc9a45129eb87"},
|
||||
{file = "setproctitle-1.3.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1da82c3e11284da4fcbf54957dafbf0655d2389cd3d54e4eaba636faf6d117a"},
|
||||
{file = "setproctitle-1.3.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:aeaa71fb9568ebe9b911ddb490c644fbd2006e8c940f21cb9a1e9425bd709574"},
|
||||
{file = "setproctitle-1.3.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:59335d000c6250c35989394661eb6287187854e94ac79ea22315469ee4f4c244"},
|
||||
{file = "setproctitle-1.3.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3ba57029c9c50ecaf0c92bb127224cc2ea9fda057b5d99d3f348c9ec2855ad3"},
|
||||
{file = "setproctitle-1.3.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d876d355c53d975c2ef9c4f2487c8f83dad6aeaaee1b6571453cb0ee992f55f6"},
|
||||
{file = "setproctitle-1.3.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:224602f0939e6fb9d5dd881be1229d485f3257b540f8a900d4271a2c2aa4e5f4"},
|
||||
{file = "setproctitle-1.3.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d7f27e0268af2d7503386e0e6be87fb9b6657afd96f5726b733837121146750d"},
|
||||
{file = "setproctitle-1.3.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5e7266498cd31a4572378c61920af9f6b4676a73c299fce8ba93afd694f8ae7"},
|
||||
{file = "setproctitle-1.3.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33c5609ad51cd99d388e55651b19148ea99727516132fb44680e1f28dd0d1de9"},
|
||||
{file = "setproctitle-1.3.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:eae8988e78192fd1a3245a6f4f382390b61bce6cfcc93f3809726e4c885fa68d"},
|
||||
{file = "setproctitle-1.3.3.tar.gz", hash = "sha256:c913e151e7ea01567837ff037a23ca8740192880198b7fbb90b16d181607caae"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
test = ["pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "70.1.1"
|
||||
@@ -763,4 +863,4 @@ multidict = ">=4.0"
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.10,<3.13"
|
||||
content-hash = "1d379cbadab535087a57eaa9ffc3d0af01b5274dac8df54080a39949c11f2003"
|
||||
content-hash = "81a35e932aea2fecab78f82f12cc8ab435cfcda5e1fe20acb42cb95542b4608f"
|
||||
|
||||
@@ -23,6 +23,7 @@ watchdog = "^4"
|
||||
certifi = "*"
|
||||
packaging = "^24"
|
||||
multidict = "^6.0.5"
|
||||
setproctitle = "^1.3.3"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pyinstaller = "^6.8.0"
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ Description=SteamDeck Plugin Loader
|
||||
Type=simple
|
||||
User=root
|
||||
Restart=always
|
||||
TimeoutStopSec=45
|
||||
ExecStart=${HOMEBREW_FOLDER}/services/PluginLoader
|
||||
WorkingDirectory=${HOMEBREW_FOLDER}/services
|
||||
Environment=UNPRIVILEGED_PATH=${HOMEBREW_FOLDER}
|
||||
|
||||
Vendored
+1
@@ -4,6 +4,7 @@ Description=SteamDeck Plugin Loader
|
||||
Type=simple
|
||||
User=root
|
||||
Restart=always
|
||||
TimeoutStopSec=45
|
||||
ExecStart=${HOMEBREW_FOLDER}/services/PluginLoader
|
||||
WorkingDirectory=${HOMEBREW_FOLDER}/services
|
||||
Environment=UNPRIVILEGED_PATH=${HOMEBREW_FOLDER}
|
||||
|
||||
Generated
+9
-9
@@ -59,11 +59,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1719254875,
|
||||
"narHash": "sha256-ECni+IkwXjusHsm9Sexdtq8weAq/yUyt1TWIemXt3Ko=",
|
||||
"lastModified": 1719848872,
|
||||
"narHash": "sha256-H3+EC5cYuq+gQW8y0lSrrDZfH71LB4DAf+TDFyvwCNA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2893f56de08021cffd9b6b6dfc70fd9ccd51eb60",
|
||||
"rev": "00d80d13810dbfea8ab4ed1009b09100cca86ba8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -84,11 +84,11 @@
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1719395064,
|
||||
"narHash": "sha256-SsutCU+IytywS9HHGKtVZaGINcm6lpHXcyJzy7Rv0Co=",
|
||||
"lastModified": 1719850884,
|
||||
"narHash": "sha256-UU/lVTHFx0GpEkihoLJrMuM9DcuhZmNe3db45vshSyI=",
|
||||
"owner": "nix-community",
|
||||
"repo": "poetry2nix",
|
||||
"rev": "0e52508053e3dcb568bf432a144bff367978d199",
|
||||
"rev": "42262f382c68afab1113ebd1911d0c93822d756e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -156,11 +156,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1718522839,
|
||||
"narHash": "sha256-ULzoKzEaBOiLRtjeY3YoGFJMwWSKRYOic6VNw2UyTls=",
|
||||
"lastModified": 1719749022,
|
||||
"narHash": "sha256-ddPKHcqaKCIFSFc/cvxS14goUhCOAwsM1PbMr0ZtHMg=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "68eb1dc333ce82d0ab0c0357363ea17c31ea1f81",
|
||||
"rev": "8df5ff62195d4e67e2264df0b7f5e8c9995fd0bd",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -26,13 +26,12 @@
|
||||
});
|
||||
}).env.overrideAttrs (oldAttrs: {
|
||||
shellHook = ''
|
||||
set -o noclobber
|
||||
PYTHONPATH=`which python`
|
||||
FILE=.vscode/settings.json
|
||||
if [ -f "$FILE" ]; then
|
||||
echo "$FILE already exists, not writing interpreter path to it."
|
||||
jq --arg pythonpath "$PYTHONPATH" '.["python.defaultInterpreterPath"] = $pythonpath' $FILE > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"
|
||||
else
|
||||
echo "{\"python.defaultInterpreterPath\": \"''${PYTHONPATH}\"}" > "$FILE"
|
||||
echo "{\"python.defaultInterpreterPath\": \"$PYTHONPATH\"}" > "$FILE"
|
||||
fi
|
||||
'';
|
||||
UV_USE_IO_URING = 0; # work around node#48444
|
||||
@@ -40,6 +39,7 @@
|
||||
nodejs_22
|
||||
nodePackages.pnpm
|
||||
poetry
|
||||
jq
|
||||
# fixes local pyright not being able to see the pythonpath properly.
|
||||
(pkgs.writeShellScriptBin "pyright" ''
|
||||
${pkgs.pyright}/bin/pyright --pythonpath `which python3` "$@" '')
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@decky/ui": "^4.2.1",
|
||||
"@decky/ui": "^4.2.2",
|
||||
"filesize": "^10.1.2",
|
||||
"i18next": "^23.11.5",
|
||||
"i18next-http-backend": "^2.5.2",
|
||||
|
||||
Generated
+2676
-2164
File diff suppressed because it is too large
Load Diff
@@ -26,7 +26,7 @@ const WithSuspense: FunctionComponent<WithSuspenseProps> = (props) => {
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<SteamSpinner />
|
||||
<SteamSpinner background="transparent" />
|
||||
</Focusable>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -58,13 +58,23 @@ const PluginInstallModal: FC<PluginInstallModalProps> = ({
|
||||
await onCancel();
|
||||
}}
|
||||
strTitle={
|
||||
<div>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', width: '100%' }}>
|
||||
<TranslationHelper
|
||||
transClass={TranslationClass.PLUGIN_INSTALL_MODAL}
|
||||
transText="title"
|
||||
i18nArgs={{ artifact: artifact }}
|
||||
installType={installType}
|
||||
/>
|
||||
{loading && (
|
||||
<div style={{ marginLeft: 'auto' }}>
|
||||
<ProgressBarWithInfo
|
||||
layout="inline"
|
||||
bottomSeparator="none"
|
||||
nProgress={percentage}
|
||||
sOperationText={downloadInfo}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
strOKButtonText={
|
||||
@@ -98,14 +108,6 @@ const PluginInstallModal: FC<PluginInstallModalProps> = ({
|
||||
installType={installType}
|
||||
/>
|
||||
</div>
|
||||
{loading && (
|
||||
<ProgressBarWithInfo
|
||||
layout="inline"
|
||||
bottomSeparator="none"
|
||||
nProgress={percentage}
|
||||
sOperationText={downloadInfo}
|
||||
/>
|
||||
)}
|
||||
{hash == 'False' && <span style={{ color: 'red' }}>{t('PluginInstallModal.no_hash')}</span>}
|
||||
</ConfirmModal>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ConfirmModal } from '@decky/ui';
|
||||
import { FC } from 'react';
|
||||
import { ConfirmModal, Spinner } from '@decky/ui';
|
||||
import { FC, useState } from 'react';
|
||||
|
||||
import { uninstallPlugin } from '../../plugin';
|
||||
|
||||
@@ -12,10 +12,12 @@ interface PluginUninstallModalProps {
|
||||
}
|
||||
|
||||
const PluginUninstallModal: FC<PluginUninstallModalProps> = ({ name, title, buttonText, description, closeModal }) => {
|
||||
const [uninstalling, setUninstalling] = useState<boolean>(false);
|
||||
return (
|
||||
<ConfirmModal
|
||||
closeModal={closeModal}
|
||||
onOK={async () => {
|
||||
setUninstalling(true);
|
||||
await uninstallPlugin(name);
|
||||
// uninstalling a plugin resets the hidden setting for it server-side
|
||||
// we invalidate here so if you re-install it, you won't have an out-of-date hidden filter
|
||||
@@ -23,7 +25,14 @@ const PluginUninstallModal: FC<PluginUninstallModalProps> = ({ name, title, butt
|
||||
await DeckyPluginLoader.hiddenPluginsService.invalidate();
|
||||
closeModal?.();
|
||||
}}
|
||||
strTitle={title}
|
||||
bOKDisabled={uninstalling}
|
||||
bCancelDisabled={uninstalling}
|
||||
strTitle={
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', width: '100%' }}>
|
||||
{title}
|
||||
{uninstalling && <Spinner width="24px" height="24px" style={{ marginLeft: 'auto' }} />}
|
||||
</div>
|
||||
}
|
||||
strOKButtonText={buttonText}
|
||||
>
|
||||
{description}
|
||||
|
||||
@@ -58,7 +58,7 @@ export default function TestingVersionList() {
|
||||
if (loading) {
|
||||
return (
|
||||
<>
|
||||
<SteamSpinner>{t('Testing.loading')}</SteamSpinner>
|
||||
<SteamSpinner background="transparent">{t('Testing.loading')}</SteamSpinner>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ const BrowseTab: FC<{ setPluginCount: Dispatch<SetStateAction<number | null>> }>
|
||||
<div>
|
||||
{!pluginList ? (
|
||||
<div style={{ height: '100%' }}>
|
||||
<SteamSpinner />
|
||||
<SteamSpinner background="transparent" />
|
||||
</div>
|
||||
) : (
|
||||
pluginList
|
||||
|
||||
@@ -115,6 +115,28 @@ class PluginLoader extends Logger {
|
||||
<DeckyStateContextProvider deckyState={this.deckyState}>
|
||||
<FaPlug />
|
||||
<TabBadge />
|
||||
<style>
|
||||
{`
|
||||
/* fixes random overscrolling in QAM */
|
||||
.${quickAccessMenuClasses?.TabContentColumn} {
|
||||
flex-grow: 1 !important;
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
justify-content: center !important;
|
||||
}
|
||||
.${quickAccessMenuClasses?.Tab} {
|
||||
flex-grow: 1 !important;
|
||||
height: unset !important;
|
||||
--decky-qam-tab-max-height: 64px; /* make things a little easier for themers */
|
||||
max-height: var(--decky-qam-tab-max-height) !important;
|
||||
}
|
||||
/* they broke the footer a while ago and forgot to update the styles LOL */
|
||||
.${quickAccessMenuClasses?.Tabs}.${quickAccessMenuClasses.TabsWithFooter} {
|
||||
margin-bottom: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</DeckyStateContextProvider>
|
||||
),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user