diff options
| author | Party Wumpus <48649272+PartyWumpus@users.noreply.github.com> | 2024-04-09 15:44:38 +0100 |
|---|---|---|
| committer | PartyWumpus <48649272+PartyWumpus@users.noreply.github.com> | 2024-04-09 15:54:48 +0100 |
| commit | f9ff518e6d6368f65ead7634f459498b183081f2 (patch) | |
| tree | 4af2f6e8d3b8da6b01e4565518f416ab9af15d03 | |
| parent | de9d2144a604506bf9499078147b7c0c899ccb65 (diff) | |
| download | decky-loader-f9ff518e6d6368f65ead7634f459498b183081f2.tar.gz decky-loader-f9ff518e6d6368f65ead7634f459498b183081f2.zip | |
attempt to add plugin events to the plugin frontend api.
unable to test right now though
| -rw-r--r-- | backend/decky_loader/loader.py | 14 | ||||
| -rw-r--r-- | backend/decky_loader/plugin/imports/decky.py | 5 | ||||
| -rw-r--r-- | backend/decky_loader/plugin/imports/decky.pyi | 5 | ||||
| -rw-r--r-- | backend/decky_loader/plugin/plugin.py | 2 | ||||
| -rw-r--r-- | backend/decky_loader/plugin/sandboxed_plugin.py | 6 | ||||
| -rw-r--r-- | frontend/src/plugin-loader.tsx | 46 |
6 files changed, 57 insertions, 21 deletions
diff --git a/backend/decky_loader/loader.py b/backend/decky_loader/loader.py index aad595e7..9eecb575 100644 --- a/backend/decky_loader/loader.py +++ b/backend/decky_loader/loader.py @@ -8,7 +8,6 @@ from typing import Any, Tuple, Dict from aiohttp import web from os.path import exists -from attr import dataclass from watchdog.events import RegexMatchingEventHandler, DirCreatedEvent, DirModifiedEvent, FileCreatedEvent, FileModifiedEvent # type: ignore from watchdog.observers import Observer # type: ignore @@ -66,12 +65,6 @@ class FileChangeHandler(RegexMatchingEventHandler): self.logger.debug(f"file modified: {src_path}") self.maybe_reload(src_path) -@dataclass -class PluginEvent: - plugin_name: str - event: str - data: str - class Loader: def __init__(self, server_instance: PluginManager, ws: WSRouter, plugin_path: str, loop: AbstractEventLoop, live_reload: bool = False) -> None: self.loop = loop @@ -149,10 +142,9 @@ class Loader: def import_plugin(self, file: str, plugin_directory: str, refresh: bool | None = False, batch: bool | None = False): try: - async def plugin_emitted_event(event: str, data: Any): - self.logger.debug(f"PLUGIN EMITTED EVENT: {str(event)} {data}") - event_data = PluginEvent(plugin_name=plugin.name, event=event, data=data) - await self.ws.emit("loader/plugin_event", event_data) + async def plugin_emitted_event(event: str, args: Any): + self.logger.debug(f"PLUGIN EMITTED EVENT: {event} with args {args}") + await self.ws.emit(f"loader/plugin_event", {plugin:plugin.name, event:event, args:args}) plugin = PluginWrapper(file, plugin_directory, self.plugin_path, plugin_emitted_event) if plugin.name in self.plugins: diff --git a/backend/decky_loader/plugin/imports/decky.py b/backend/decky_loader/plugin/imports/decky.py index 9a784ec4..6c5173de 100644 --- a/backend/decky_loader/plugin/imports/decky.py +++ b/backend/decky_loader/plugin/imports/decky.py @@ -19,7 +19,7 @@ import subprocess import logging import time -from typing import TypeVar, Type +from typing import Any """ Constants @@ -213,9 +213,8 @@ logger.setLevel(logging.INFO) """ Event handling """ -DataType = TypeVar("DataType") # TODO better docstring im lazy -async def emit(event: str, data: DataType | None = None, data_type: Type[DataType] | None = None) -> None: +async def emit(event: str, *args: Any) -> None: """ Send an event to the frontend. """ diff --git a/backend/decky_loader/plugin/imports/decky.pyi b/backend/decky_loader/plugin/imports/decky.pyi index 7a0dfa6a..50a0f66c 100644 --- a/backend/decky_loader/plugin/imports/decky.pyi +++ b/backend/decky_loader/plugin/imports/decky.pyi @@ -16,7 +16,7 @@ __version__ = '0.1.0' import logging -from typing import TypeVar, Type +from typing import Any """ Constants @@ -177,9 +177,8 @@ logger: logging.Logger """ Event handling """ -DataType = TypeVar("DataType") # TODO better docstring im lazy -async def emit(event: str, data: DataType | None = None, data_type: Type[DataType] | None = None) -> None: +async def emit(event: str, *args: Any) -> None: """ Send an event to the frontend. """
\ No newline at end of file diff --git a/backend/decky_loader/plugin/plugin.py b/backend/decky_loader/plugin/plugin.py index cad323f4..02d80470 100644 --- a/backend/decky_loader/plugin/plugin.py +++ b/backend/decky_loader/plugin/plugin.py @@ -59,7 +59,7 @@ class PluginWrapper: if line != None: res = loads(line) if res["type"] == SocketMessageType.EVENT.value: - create_task(self.emitted_event_callback(res["event"], res["data"])) + 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: diff --git a/backend/decky_loader/plugin/sandboxed_plugin.py b/backend/decky_loader/plugin/sandboxed_plugin.py index b49dcf41..98706a33 100644 --- a/backend/decky_loader/plugin/sandboxed_plugin.py +++ b/backend/decky_loader/plugin/sandboxed_plugin.py @@ -14,7 +14,7 @@ from ..localplatform.localplatform import setgid, setuid, get_username, get_home from ..enums import UserType from .. import helpers -from typing import List, TypeVar, Type +from typing import List, TypeVar, Any DataType = TypeVar("DataType") @@ -83,11 +83,11 @@ class SandboxedPlugin: sysmodules[key.replace("decky_loader.", "")] = sysmodules[key] from .imports import decky - async def emit(event: str, data: DataType | None = None, data_type: Type[DataType] | None = None) -> None: + async def emit(event: str, *args: Any) -> None: await self._socket.write_single_line_server(dumps({ "type": SocketMessageType.EVENT, "event": event, - "data": data + "args": args })) # copy the docstring over so we don't have to duplicate it emit.__doc__ = decky.emit.__doc__ diff --git a/frontend/src/plugin-loader.tsx b/frontend/src/plugin-loader.tsx index 7a87253f..75b09091 100644 --- a/frontend/src/plugin-loader.tsx +++ b/frontend/src/plugin-loader.tsx @@ -48,6 +48,9 @@ declare global { } } +/** Map of event names to event listeners */ +type listenerMap = Map<string, Set<(...args: any) => any>>; + const callPluginMethod = DeckyBackend.callable<[pluginName: string, method: string, ...args: any], any>( 'loader/call_plugin_method', ); @@ -58,6 +61,8 @@ class PluginLoader extends Logger { private routerHook: RouterHook = new RouterHook(); public toaster: Toaster = new Toaster(); private deckyState: DeckyState = new DeckyState(); + // stores a map of plugin names to all their event listeners + private pluginEventListeners: Map<string, listenerMap> = new Map(); public frozenPluginsService = new FrozenPluginService(this.deckyState); public hiddenPluginsService = new HiddenPluginsService(this.deckyState); @@ -81,6 +86,7 @@ class PluginLoader extends Logger { DeckyBackend.addEventListener('updater/update_download_percentage', () => { this.deckyState.setIsLoaderUpdating(true); }); + DeckyBackend.addEventListener(`loader/plugin_event`, this.pluginEventListener); this.tabsHook.init(); @@ -513,6 +519,9 @@ class PluginLoader extends Logger { throw new Error(`Plugin ${pluginName} requested invalid backend api version ${version}.`); } + const eventListeners: listenerMap = new Map(); + this.pluginEventListeners.set(pluginName, eventListeners); + const backendAPI = { call: (methodName: string, ...args: any) => { return callPluginMethod(pluginName, methodName, ...args); @@ -520,6 +529,20 @@ class PluginLoader extends Logger { callable: (methodName: string) => { return (...args: any) => callPluginMethod(pluginName, methodName, ...args); }, + addEventListener: (event: string, listener: (...args: any) => any) => { + if (!eventListeners.has(event)) { + eventListeners.set(event, new Set([listener])); + } else { + eventListeners.get(event)?.add(listener); + } + return listener; + }, + removeEventListener: (event: string, listener: (...args: any) => any) => { + if (eventListeners.has(event)) { + const set = eventListeners.get(event); + set?.delete(listener); + } + }, }; this.debug(`${pluginName} connected to backend API.`); @@ -528,6 +551,29 @@ class PluginLoader extends Logger { }; } + pluginEventListener = (data: { plugin: string; event: string; args: any }) => { + const { plugin, event, args } = data; + this.debug(`Recieved plugin event ${event} for ${plugin} with args`, args); + if (!this.pluginEventListeners.has(plugin)) { + this.warn(`plugin ${plugin} does not have event listeners`); + return; + } + const eventListeners = this.pluginEventListeners.get(plugin)!; + if (eventListeners.has(event)) { + for (const listener of eventListeners.get(event)!) { + (async () => { + try { + await listener(...args); + } catch (e) { + this.error(`error in event ${event}`, e, listener); + } + })(); + } + } else { + this.warn(`event ${event} has no listeners`); + } + }; + createLegacyPluginAPI(pluginName: string) { const pluginAPI = { routerHook: this.routerHook, |
