mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-07-11 19:21:59 +00:00
attempt to add plugin events to the plugin frontend api.
unable to test right now though
This commit is contained in:
committed by
PartyWumpus
parent
de9d2144a6
commit
f9ff518e6d
@@ -8,7 +8,6 @@ from typing import Any, Tuple, Dict
|
|||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from os.path import exists
|
from os.path import exists
|
||||||
from attr import dataclass
|
|
||||||
from watchdog.events import RegexMatchingEventHandler, DirCreatedEvent, DirModifiedEvent, FileCreatedEvent, FileModifiedEvent # type: ignore
|
from watchdog.events import RegexMatchingEventHandler, DirCreatedEvent, DirModifiedEvent, FileCreatedEvent, FileModifiedEvent # type: ignore
|
||||||
from watchdog.observers import Observer # 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.logger.debug(f"file modified: {src_path}")
|
||||||
self.maybe_reload(src_path)
|
self.maybe_reload(src_path)
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class PluginEvent:
|
|
||||||
plugin_name: str
|
|
||||||
event: str
|
|
||||||
data: str
|
|
||||||
|
|
||||||
class Loader:
|
class Loader:
|
||||||
def __init__(self, server_instance: PluginManager, ws: WSRouter, plugin_path: str, loop: AbstractEventLoop, live_reload: bool = False) -> None:
|
def __init__(self, server_instance: PluginManager, ws: WSRouter, plugin_path: str, loop: AbstractEventLoop, live_reload: bool = False) -> None:
|
||||||
self.loop = loop
|
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):
|
def import_plugin(self, file: str, plugin_directory: str, refresh: bool | None = False, batch: bool | None = False):
|
||||||
try:
|
try:
|
||||||
async def plugin_emitted_event(event: str, data: Any):
|
async def plugin_emitted_event(event: str, args: Any):
|
||||||
self.logger.debug(f"PLUGIN EMITTED EVENT: {str(event)} {data}")
|
self.logger.debug(f"PLUGIN EMITTED EVENT: {event} with args {args}")
|
||||||
event_data = PluginEvent(plugin_name=plugin.name, event=event, data=data)
|
await self.ws.emit(f"loader/plugin_event", {plugin:plugin.name, event:event, args:args})
|
||||||
await self.ws.emit("loader/plugin_event", event_data)
|
|
||||||
|
|
||||||
plugin = PluginWrapper(file, plugin_directory, self.plugin_path, plugin_emitted_event)
|
plugin = PluginWrapper(file, plugin_directory, self.plugin_path, plugin_emitted_event)
|
||||||
if plugin.name in self.plugins:
|
if plugin.name in self.plugins:
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import subprocess
|
|||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from typing import TypeVar, Type
|
from typing import Any
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Constants
|
Constants
|
||||||
@@ -213,9 +213,8 @@ logger.setLevel(logging.INFO)
|
|||||||
"""
|
"""
|
||||||
Event handling
|
Event handling
|
||||||
"""
|
"""
|
||||||
DataType = TypeVar("DataType")
|
|
||||||
# TODO better docstring im lazy
|
# 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.
|
Send an event to the frontend.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ __version__ = '0.1.0'
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from typing import TypeVar, Type
|
from typing import Any
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Constants
|
Constants
|
||||||
@@ -177,9 +177,8 @@ logger: logging.Logger
|
|||||||
"""
|
"""
|
||||||
Event handling
|
Event handling
|
||||||
"""
|
"""
|
||||||
DataType = TypeVar("DataType")
|
|
||||||
# TODO better docstring im lazy
|
# 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.
|
Send an event to the frontend.
|
||||||
"""
|
"""
|
||||||
@@ -59,7 +59,7 @@ class PluginWrapper:
|
|||||||
if line != None:
|
if line != None:
|
||||||
res = loads(line)
|
res = loads(line)
|
||||||
if res["type"] == SocketMessageType.EVENT.value:
|
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:
|
elif res["type"] == SocketMessageType.RESPONSE.value:
|
||||||
self._method_call_requests.pop(res["id"]).set_result(res)
|
self._method_call_requests.pop(res["id"]).set_result(res)
|
||||||
except:
|
except:
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from ..localplatform.localplatform import setgid, setuid, get_username, get_home
|
|||||||
from ..enums import UserType
|
from ..enums import UserType
|
||||||
from .. import helpers
|
from .. import helpers
|
||||||
|
|
||||||
from typing import List, TypeVar, Type
|
from typing import List, TypeVar, Any
|
||||||
|
|
||||||
DataType = TypeVar("DataType")
|
DataType = TypeVar("DataType")
|
||||||
|
|
||||||
@@ -83,11 +83,11 @@ class SandboxedPlugin:
|
|||||||
sysmodules[key.replace("decky_loader.", "")] = sysmodules[key]
|
sysmodules[key.replace("decky_loader.", "")] = sysmodules[key]
|
||||||
|
|
||||||
from .imports import decky
|
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({
|
await self._socket.write_single_line_server(dumps({
|
||||||
"type": SocketMessageType.EVENT,
|
"type": SocketMessageType.EVENT,
|
||||||
"event": event,
|
"event": event,
|
||||||
"data": data
|
"args": args
|
||||||
}))
|
}))
|
||||||
# copy the docstring over so we don't have to duplicate it
|
# copy the docstring over so we don't have to duplicate it
|
||||||
emit.__doc__ = decky.emit.__doc__
|
emit.__doc__ = decky.emit.__doc__
|
||||||
|
|||||||
@@ -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>(
|
const callPluginMethod = DeckyBackend.callable<[pluginName: string, method: string, ...args: any], any>(
|
||||||
'loader/call_plugin_method',
|
'loader/call_plugin_method',
|
||||||
);
|
);
|
||||||
@@ -58,6 +61,8 @@ class PluginLoader extends Logger {
|
|||||||
private routerHook: RouterHook = new RouterHook();
|
private routerHook: RouterHook = new RouterHook();
|
||||||
public toaster: Toaster = new Toaster();
|
public toaster: Toaster = new Toaster();
|
||||||
private deckyState: DeckyState = new DeckyState();
|
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 frozenPluginsService = new FrozenPluginService(this.deckyState);
|
||||||
public hiddenPluginsService = new HiddenPluginsService(this.deckyState);
|
public hiddenPluginsService = new HiddenPluginsService(this.deckyState);
|
||||||
@@ -81,6 +86,7 @@ class PluginLoader extends Logger {
|
|||||||
DeckyBackend.addEventListener('updater/update_download_percentage', () => {
|
DeckyBackend.addEventListener('updater/update_download_percentage', () => {
|
||||||
this.deckyState.setIsLoaderUpdating(true);
|
this.deckyState.setIsLoaderUpdating(true);
|
||||||
});
|
});
|
||||||
|
DeckyBackend.addEventListener(`loader/plugin_event`, this.pluginEventListener);
|
||||||
|
|
||||||
this.tabsHook.init();
|
this.tabsHook.init();
|
||||||
|
|
||||||
@@ -513,6 +519,9 @@ class PluginLoader extends Logger {
|
|||||||
throw new Error(`Plugin ${pluginName} requested invalid backend api version ${version}.`);
|
throw new Error(`Plugin ${pluginName} requested invalid backend api version ${version}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const eventListeners: listenerMap = new Map();
|
||||||
|
this.pluginEventListeners.set(pluginName, eventListeners);
|
||||||
|
|
||||||
const backendAPI = {
|
const backendAPI = {
|
||||||
call: (methodName: string, ...args: any) => {
|
call: (methodName: string, ...args: any) => {
|
||||||
return callPluginMethod(pluginName, methodName, ...args);
|
return callPluginMethod(pluginName, methodName, ...args);
|
||||||
@@ -520,6 +529,20 @@ class PluginLoader extends Logger {
|
|||||||
callable: (methodName: string) => {
|
callable: (methodName: string) => {
|
||||||
return (...args: any) => callPluginMethod(pluginName, methodName, ...args);
|
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.`);
|
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) {
|
createLegacyPluginAPI(pluginName: string) {
|
||||||
const pluginAPI = {
|
const pluginAPI = {
|
||||||
routerHook: this.routerHook,
|
routerHook: this.routerHook,
|
||||||
|
|||||||
Reference in New Issue
Block a user