### Makefile for Backend Compilation Source: https://context7.com/steamdeckhomebrew/decky-plugin-template/llms.txt A basic Makefile example for compiling a C/C++ backend binary. This is a starting point for integrating custom native code. ```makefile # backend/Makefile .PHONY: all all: mybackend ``` -------------------------------- ### Install Dependencies and Build Frontend Source: https://github.com/steamdeckhomebrew/decky-plugin-template/blob/main/README.md Installs project dependencies using pnpm and builds the frontend code for testing. These commands should be run in your local plugin repository. ```bash pnpm i ``` ```bash pnpm run build ``` -------------------------------- ### Makefile Snippet for Backend Compilation Source: https://github.com/steamdeckhomebrew/decky-plugin-template/blob/main/README.md Example snippet from a makefile demonstrating how to create the output directory and compile a C backend binary for a Decky plugin. Ensure backend binaries are placed in 'backend/out'. ```makefile hello: mkdir -p ./out gcc -o ./out/hello ./src/main.c ``` -------------------------------- ### Install pnpm v9 on Linux Source: https://github.com/steamdeckhomebrew/decky-plugin-template/blob/main/README.md Installs the required pnpm version globally on Linux systems using npm. ```bash sudo npm i -g pnpm@9 ``` -------------------------------- ### Access Decky Module Constants in Python Source: https://context7.com/steamdeckhomebrew/decky-plugin-template/llms.txt Use these constants to access user information, Decky installation details, plugin metadata, and recommended storage paths. Ensure necessary imports are included. ```python import decky import os import json class Plugin: async def _main(self): # User and home directory info decky.logger.info(f"User: {decky.USER}") # e.g., "deck" or "root" decky.logger.info(f"Home: {decky.HOME}") # e.g., "/home/deck" # Decky installation info decky.logger.info(f"Decky version: {decky.DECKY_VERSION}") # e.g., "v2.5.0" decky.logger.info(f"Decky home: {decky.DECKY_HOME}") # e.g., "/home/deck/homebrew" # Plugin metadata decky.logger.info(f"Plugin name: {decky.DECKY_PLUGIN_NAME}") decky.logger.info(f"Plugin version: {decky.DECKY_PLUGIN_VERSION}") decky.logger.info(f"Plugin author: {decky.DECKY_PLUGIN_AUTHOR}") decky.logger.info(f"Plugin dir: {decky.DECKY_PLUGIN_DIR}") # Recommended storage paths (auto-created) settings_file = os.path.join(decky.DECKY_PLUGIN_SETTINGS_DIR, "config.json") runtime_file = os.path.join(decky.DECKY_PLUGIN_RUNTIME_DIR, "cache.dat") # Save settings example settings = {"theme": "dark", "notifications": True} with open(settings_file, "w") as f: json.dump(settings, f) # Log file path decky.logger.info(f"Log file: {decky.DECKY_PLUGIN_LOG}") ``` -------------------------------- ### Build Native Binary with Makefile Source: https://context7.com/steamdeckhomebrew/decky-plugin-template/llms.txt This Makefile defines a target to build a C binary and place it in the ./out directory. It also includes a clean target to remove the build output. ```makefile mybackend: mkdir -p ./out gcc -o ./out/mybackend ./src/main.c .PHONY: clean clean: rm -rf ./out ``` -------------------------------- ### C Backend Entry Point Source: https://context7.com/steamdeckhomebrew/decky-plugin-template/llms.txt The main function for the C backend binary. It expects a command-line argument and performs a 'process' operation if provided. Ensure the 'strcmp' function is available or include necessary headers. ```c // backend/src/main.c #include #include int main(int argc, char *argv[]) { if (argc < 2) { printf("Usage: mybackend \n"); return 1; } if (strcmp(argv[1], "process") == 0) { // Perform CPU-intensive operation printf("Processing complete\n"); return 0; } return 1; } ``` -------------------------------- ### Call Native Binary from Python Backend Source: https://context7.com/steamdeckhomebrew/decky-plugin-template/llms.txt This Python code demonstrates how to execute a native binary from the backend. It constructs the binary path using decky.DECKY_PLUGIN_DIR and captures the output. ```python # main.py - calling the binary from Python import decky import subprocess import os class Plugin: async def run_native_task(self, command: str) -> str: binary_path = os.path.join(decky.DECKY_PLUGIN_DIR, "bin", "mybackend") result = subprocess.run( [binary_path, command], capture_output=True, text=True ) return result.stdout ``` -------------------------------- ### Registering a Decky Plugin with definePlugin Source: https://context7.com/steamdeckhomebrew/decky-plugin-template/llms.txt Use `definePlugin` to initialize your plugin, set up event listeners, and configure its name, icon, content, and cleanup function. It's essential for any Decky plugin's entry point. ```tsx import { ButtonItem, PanelSection, PanelSectionRow, staticClasses } from "@decky/ui"; import { addEventListener, removeEventListener, definePlugin, toaster } from "@decky/api"; import { FaShip } from "react-icons/fa"; function Content() { return ( console.log("clicked")}> Click Me ); } export default definePlugin(() => { console.log("Plugin initializing"); // Subscribe to backend events const listener = addEventListener<[message: string, success: boolean]>( "my_event", (message, success) => { toaster.toast({ title: "Event Received", body: `${message} - ${success}` }); } ); return { name: "My Plugin", titleView:
My Plugin Title
, content: , icon: , onDismount() { console.log("Plugin unloading"); removeEventListener("my_event", listener); } }; }); ``` -------------------------------- ### Migrate Plugin Data with Decky Helpers in Python Source: https://context7.com/steamdeckhomebrew/decky-plugin-template/llms.txt Utilize these utility functions to migrate plugin data from legacy locations to the recommended Decky directory structure. Ensure the `decky` module is imported. ```python import decky import os class Plugin: async def _migration(self): decky.logger.info("Running migrations") # Migrate log files to DECKY_PLUGIN_LOG_DIR # Old: ~/.config/my-plugin/plugin.log # New: ~/homebrew/logs/my-plugin/plugin.log old_log = os.path.join(decky.DECKY_USER_HOME, ".config", "my-plugin", "plugin.log") migrations = decky.migrate_logs(old_log) for old_path, new_path in migrations.items(): decky.logger.info(f"Migrated log: {old_path} -> {new_path}") # Migrate settings to DECKY_PLUGIN_SETTINGS_DIR # Handles both files and directories old_settings_file = os.path.join(decky.DECKY_HOME, "settings", "my-plugin.json") old_settings_dir = os.path.join(decky.DECKY_USER_HOME, ".config", "my-plugin") migrations = decky.migrate_settings(old_settings_file, old_settings_dir) # Migrate runtime data to DECKY_PLUGIN_RUNTIME_DIR old_data_dir = os.path.join(decky.DECKY_HOME, "my-plugin") old_local_dir = os.path.join(decky.DECKY_USER_HOME, ".local", "share", "my-plugin") migrations = decky.migrate_runtime(old_data_dir, old_local_dir) # Generic migration to any target directory custom_target = "/custom/path" migrations = decky.migrate_any(custom_target, "/old/path/file.txt", "/old/path/dir") ``` -------------------------------- ### Build Plugin Interfaces with Decky UI Components in TSX Source: https://context7.com/steamdeckhomebrew/decky-plugin-template/llms.txt Use these React components from `@decky/ui` to create native-looking plugin interfaces. Import necessary components and manage state with `useState`. ```tsx import { ButtonItem, PanelSection, PanelSectionRow, Navigation, staticClasses, ToggleField, SliderField, TextField } from "@decky/ui"; import { useState } from "react"; function SettingsPanel() { const [enabled, setEnabled] = useState(true); const [volume, setVolume] = useState(50); const [username, setUsername] = useState(""); return ( <> setUsername(e.target.value)} /> { Navigation.Navigate("/decky-plugin-test"); Navigation.CloseSideMenus(); }} > Open Full Page ); } ``` -------------------------------- ### Update Decky UI Package Source: https://github.com/steamdeckhomebrew/decky-plugin-template/blob/main/README.md Updates the @decky/ui package to the latest version. Run this command if you encounter build errors due to outdated libraries. ```bash pnpm update @decky/ui --latest ``` -------------------------------- ### Python Plugin Class Definition Source: https://context7.com/steamdeckhomebrew/decky-plugin-template/llms.txt Define backend logic, including callable methods, lifecycle hooks, and event emission. Use `decky.logger` for logging and `asyncio` for asynchronous operations. ```python import decky import asyncio import os class Plugin: # Method callable from TypeScript via callable<[number, number], number>("add") async def add(self, left: int, right: int) -> int: decky.logger.info(f"Adding {left} + {right}") return left + right # Method callable from TypeScript via callable<[string], dict>("get_user_data") async def get_user_data(self, user_id: str) -> dict: return {"name": f"User_{user_id}", "score": 100} # Long-running async task that emits events to frontend async def long_running_task(self): for i in range(5): await asyncio.sleep(1) # Emit progress event to frontend await decky.emit("progress_event", f"Step {i+1}/5", False, i+1) await decky.emit("progress_event", "Complete!", True, 5) # Start background task callable from frontend async def start_background_task(self): self.loop.create_task(self.long_running_task()) # Called once when plugin loads - initialize resources async def _main(self): self.loop = asyncio.get_event_loop() decky.logger.info(f"Plugin loaded: {decky.DECKY_PLUGIN_NAME}") decky.logger.info(f"Settings dir: {decky.DECKY_PLUGIN_SETTINGS_DIR}") # Called when plugin is disabled/stopped async def _unload(self): decky.logger.info("Plugin unloading") # Called when plugin is uninstalled - cleanup persistent data async def _uninstall(self): decky.logger.info("Plugin uninstalled") ``` -------------------------------- ### Frontend Event Listeners Source: https://context7.com/steamdeckhomebrew/decky-plugin-template/llms.txt This section covers how to subscribe to and unsubscribe from events emitted by the Python backend using `addEventListener` and `removeEventListener`. ```APIDOC ## addEventListener / removeEventListener Subscribe to and unsubscribe from events emitted by the Python backend. Events enable real-time communication from backend to frontend with typed payload parameters. ### Description Allows frontend components to listen for specific events broadcast from the backend and execute callback functions when those events occur. It also provides a mechanism to clean up these listeners when they are no longer needed. ### Method `addEventListener(eventName: string, callback: (...args: PayloadType) => void): ListenerId` `removeEventListener(eventName: string, listenerId: ListenerId): void` ### Parameters #### addEventListener - **eventName** (string) - Required - The name of the event to listen for. - **callback** (function) - Required - The function to execute when the event is received. The arguments of this function should match the `PayloadType`. #### removeEventListener - **eventName** (string) - Required - The name of the event to stop listening to. - **listenerId** (ListenerId) - Required - The unique identifier returned by `addEventListener` for the listener to remove. ### Request Example ```typescript import { addEventListener, removeEventListener, toaster, definePlugin } from "@decky/api"; export default definePlugin(() => { const timerListener = addEventListener<[ message: string, isComplete: boolean, elapsedSeconds: number ]>("timer_event", (message, isComplete, elapsedSeconds) => { console.log(`Timer: ${message}, Complete: ${isComplete}, Elapsed: ${elapsedSeconds}s`); toaster.toast({ title: isComplete ? "Timer Complete!" : "Timer Update", body: `${message} (${elapsedSeconds}s)` }); }); const syncListener = addEventListener<[status: string, count: number]>( "sync_complete", (status, count) => { console.log(`Sync ${status}: ${count} items`); } ); return { name: "Event Demo", content:
Event Demo Plugin
, icon: 📡, onDismount() { removeEventListener("timer_event", timerListener); removeEventListener("sync_complete", syncListener); } }; }); ``` ### Response #### Success Response `addEventListener` returns a `ListenerId` which is a unique identifier for the registered listener. `removeEventListener` does not return a value. ``` -------------------------------- ### Backend Event Emission (decky.emit) Source: https://context7.com/steamdeckhomebrew/decky-plugin-template/llms.txt Details on how to send typed events from the Python backend to the TypeScript frontend using `decky.emit`. ```APIDOC ## decky.emit Sends typed events from the Python backend to the TypeScript frontend. Multiple arguments of any JSON-serializable type can be passed. ### Description This function allows the Python backend to broadcast real-time information or notifications to the frontend. Events are named, and can carry multiple arguments of various data types, which are then received by corresponding listeners set up in the frontend. ### Method `await decky.emit(eventName: string, ...args: any[])` ### Parameters - **eventName** (string) - Required - The name of the event to emit. - **args** (any[]) - Optional - A variable number of arguments of any JSON-serializable type to send with the event. These arguments will be passed to the frontend event listener's callback function in the order they are provided. ### Request Example (Python) ```python import decky import asyncio class Plugin: async def notify_frontend(self): # Emit event with multiple typed arguments await decky.emit( "timer_event", # Event name "Hello from backend!", # string argument True, # boolean argument 42 # number argument ) async def send_complex_data(self): # Emit event with complex data await decky.emit( "data_update", {"users": ["alice", "bob"], "count": 2}, [1, 2, 3, 4, 5] ) async def run_periodic_updates(self): count = 0 while True: count += 1 await decky.emit("heartbeat", f"Tick #{count}", count) await asyncio.sleep(5) ``` ### Response `decky.emit` does not return a value to the backend. Its purpose is to send data to the frontend. The success of the emission is typically confirmed by the frontend listener receiving the event. ``` -------------------------------- ### Decky Plugin Zip Distribution Structure Source: https://github.com/steamdeckhomebrew/decky-plugin-template/blob/main/README.md This is the standard directory and file layout for a plugin zip archive intended for distribution. Ensure all required files are present in their respective locations. ```text pluginname-v1.0.0.zip (version number is optional but recommended for users sake) | pluginname/ | | | | | bin/ (optional) | | | | | binary (optional) | | | dist/ [required] | | | index.js [required] | package.json [required] plugin.json [required] main.py {required if you are using the python backend of decky-loader: serverAPI} README.md (optional but recommended) LICENSE(.md) [required, filename should be roughly similar, suffix not needed] ``` -------------------------------- ### Emit Events from Python Backend Source: https://context7.com/steamdeckhomebrew/decky-plugin-template/llms.txt Send typed events to the TypeScript frontend using `decky.emit`. Supports multiple arguments of any JSON-serializable type. ```python import decky import asyncio class Plugin: async def notify_frontend(self): # Emit event with multiple typed arguments await decky.emit( "timer_event", # Event name "Hello from backend!", # string argument True, # boolean argument 42 # number argument ) async def send_complex_data(self): # Emit event with complex data await decky.emit( "data_update", {"users": ["alice", "bob"], "count": 2}, [1, 2, 3, 4, 5] ) async def run_periodic_updates(self): count = 0 while True: count += 1 await decky.emit("heartbeat", f"Tick #{count}", count) await asyncio.sleep(5) ``` -------------------------------- ### Listen to Backend Events in TypeScript Source: https://context7.com/steamdeckhomebrew/decky-plugin-template/llms.txt Subscribe to typed events from the Python backend using `addEventListener`. Ensure `removeEventListener` is called in `onDismount` to prevent memory leaks. ```typescript import { addEventListener, removeEventListener, toaster, definePlugin } from "@decky/api"; export default definePlugin(() => { // Listen for timer completion events with typed parameters const timerListener = addEventListener<[ message: string, isComplete: boolean, elapsedSeconds: number ]>("timer_event", (message, isComplete, elapsedSeconds) => { console.log(`Timer: ${message}, Complete: ${isComplete}, Elapsed: ${elapsedSeconds}s`); toaster.toast({ title: isComplete ? "Timer Complete!" : "Timer Update", body: `${message} (${elapsedSeconds}s)` }); }); // Listen for data sync events const syncListener = addEventListener<[status: string, count: number]> ("sync_complete", (status, count) => { console.log(`Sync ${status}: ${count} items`); }); return { name: "Event Demo", content:
Event Demo Plugin
, icon: 📡, onDismount() { // Clean up all event listeners on plugin unload removeEventListener("timer_event", timerListener); removeEventListener("sync_complete", syncListener); } }; }); ``` -------------------------------- ### Python Plugin Class Source: https://context7.com/steamdeckhomebrew/decky-plugin-template/llms.txt Defines the structure for a backend Python plugin, including callable methods, lifecycle hooks, and event emission. ```APIDOC ## Python Plugin Class The backend Plugin class defines async methods callable from TypeScript, lifecycle hooks for initialization and cleanup, and event emission to the frontend. ### Description This class serves as the core of a Decky plugin's backend logic. It allows developers to define functions that can be invoked from the frontend, implement setup and teardown routines, and send real-time updates to the frontend via events. ### Methods - **`add(left: int, right: int) -> int`**: An example asynchronous method that takes two integers and returns their sum. Callable from TypeScript using `callable<[number, number], number>("add")`. - **`get_user_data(user_id: str) -> dict`**: An example asynchronous method that takes a user ID string and returns user data as a dictionary. Callable from TypeScript using `callable<[string], dict>("get_user_data")`. - **`long_running_task()`**: An example asynchronous method simulating a long-running process that emits progress events. - **`start_background_task()`**: Initiates the `long_running_task` as a background process. - **`_main()`**: Lifecycle hook called once when the plugin loads. Used for initialization. - **`_unload()`**: Lifecycle hook called when the plugin is disabled or stopped. Used for cleanup. - **`_uninstall()`**: Lifecycle hook called when the plugin is uninstalled. Used for removing persistent data. ### Parameters #### `add` method parameters: - **left** (int) - Required - The first integer operand. - **right** (int) - Required - The second integer operand. #### `get_user_data` method parameters: - **user_id** (string) - Required - The ID of the user whose data is to be retrieved. ### Request Example (TypeScript Call) ```typescript import { callable } from "@decky/api"; const add = callable<[number, number], number>("add"); const getUserData = callable<[string], { name: string, score: number } >("get_user_data"); const startTask = callable<[], void>("start_background_task"); async function exampleUsage() { const sum = await add(5, 3); console.log("Sum:", sum); // Output: Sum: 8 const userData = await getUserData("123"); console.log("User Data:", userData); // Output: User Data: { name: 'User_123', score: 100 } await startTask(); } ``` ### Response #### Success Response - **`add`**: Returns an integer representing the sum. - **`get_user_data`**: Returns a dictionary containing user information. - **`_main`, `_unload`, `_uninstall`**: These are lifecycle hooks and typically do not return values intended for the frontend. #### Response Example (`add`) ```json { "result": 8 } ``` #### Response Example (`get_user_data`) ```json { "result": { "name": "User_123", "score": 100 } } ``` ``` -------------------------------- ### Invoking Python Backend Methods with callable Source: https://context7.com/steamdeckhomebrew/decky-plugin-template/llms.txt The `callable` utility creates type-safe functions for invoking backend Python methods from TypeScript. Define the argument and return types to ensure type safety during calls. This is useful for triggering server-side logic or fetching data. ```tsx import { callable } from "@decky/api"; import { useState } from "react"; import { ButtonItem, PanelSection, PanelSectionRow } from "@decky/ui"; // Define callable with typed arguments and return value const add = callable<[first: number, second: number], number>("add"); const getUserData = callable<[userId: string], { name: string; score: number }>("get_user_data"); const startBackgroundTask = callable<[], void>("start_background_task"); function CalculatorPanel() { const [result, setResult] = useState(); const handleAdd = async () => { try { const sum = await add(Math.random() * 100, Math.random() * 100); setResult(sum); } catch (error) { console.error("Failed to call backend:", error); } }; return ( {result !== undefined ? `Result: ${result.toFixed(2)}` : "Add Random Numbers"} startBackgroundTask()}> Start Background Task ); } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.