summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md110
-rw-r--r--assets/image.pngbin0 -> 1232759 bytes
-rw-r--r--assets/lsfg-vk.pngbin0 -> 1779289 bytes
-rw-r--r--justfile11
-rw-r--r--main.py180
-rw-r--r--package.json9
-rw-r--r--plugin.json10
-rw-r--r--py_modules/.keep0
-rwxr-xr-xsrc/index.tsx256
9 files changed, 396 insertions, 180 deletions
diff --git a/README.md b/README.md
index de27fae..720ed73 100644
--- a/README.md
+++ b/README.md
@@ -1,101 +1,27 @@
-# Decky Plugin Template [![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://deckbrew.xyz/discord)
+# Lossless Scaling for Steam Deck
+A Decky plugin that streamlines the installation of **lsfg-vk** ([Lossless Scaling Frame Generation Vulkan layer](https://github.com/PancakeTAS/lsfg-vk)) on Steam Deck, allowing you to use the Lossless Scaling app on Linux.
-Reference example for using [decky-frontend-lib](https://github.com/SteamDeckHomebrew/decky-frontend-lib) (@decky/ui) in a [decky-loader](https://github.com/SteamDeckHomebrew/decky-loader) plugin.
+## What is this?
-### **Please also refer to the [wiki](https://wiki.deckbrew.xyz/en/user-guide/home#plugin-development) for important information on plugin development and submissions/updates. currently documentation is split between this README and the wiki which is something we are hoping to rectify in the future.**
+This plugin automates the installation of lsfg-vk, a compatibility layer that allows the Windows-only [Lossless Scaling](https://store.steampowered.com/app/993090/Lossless_Scaling/) app to work on Linux systems like Steam Deck. Lossless Scaling provides frame generation and upscaling features for games.
-## Developers
+![Plugin Screenshot](assets/image.png)
-### Dependencies
+## How to Use
-This template relies on the user having Node.js v16.14+ and `pnpm` (v9) installed on their system.
-Please make sure to install pnpm v9 to prevent issues with CI during plugin submission.
-`pnpm` can be downloaded from `npm` itself which is recommended.
+1. **Install the plugin** through the Decky Plugin Store (TBD)
+2. **Purchase and install** [Lossless Scaling](https://store.steampowered.com/app/993090/Lossless_Scaling/) from Steam
+3. **Open the plugin** from the Decky menu
+4. **Click "Install lsfg-vk"** to automatically set up the compatibility layer
+5. **Apply launch commands** to the game you want to use frame generation with. The plugin provides a short guide for these commands in its interface.
-#### Linux
+## What it does
-```bash
-sudo npm i -g pnpm@9
-```
+The plugin:
+- Extracts the lsfg-vk library to `~/.local/lib/`
+- Installs the Vulkan layer configuration to `~/.local/share/vulkan/implicit_layer.d/`
+- Provides easy uninstallation by removing these files when no longer needed
-If you would like to build plugins that have their own custom backends, Docker is required as it is used by the Decky CLI tool.
+## Credits
-### Making your own plugin
-
-1. You can fork this repo or utilize the "Use this template" button on Github.
-2. In your local fork/own plugin-repository run these commands:
- 1. ``pnpm i``
- 2. ``pnpm run build``
- - These setup pnpm and build the frontend code for testing.
-3. Consult the [decky-frontend-lib](https://github.com/SteamDeckHomebrew/decky-frontend-lib) repository for ways to accomplish your tasks.
- - Documentation and examples are still rough,
- - Decky loader primarily targets Steam Deck hardware so keep this in mind when developing your plugin.
-4. If using VSCodium/VSCode, run the `setup` and `build` and `deploy` tasks. If not using VSCodium etc. you can derive your own makefile or just manually utilize the scripts for these commands as you see fit.
-
-If you use VSCode or it's derivatives (we suggest [VSCodium](https://vscodium.com/)!) just run the `setup` and `build` tasks. It's really that simple.
-
-#### Other important information
-
-Everytime you change the frontend code (`index.tsx` etc) you will need to rebuild using the commands from step 2 above or the build task if you're using vscode or a derivative.
-
-Note: If you are receiving build errors due to an out of date library, you should run this command inside of your repository:
-
-```bash
-pnpm update @decky/ui --latest
-```
-
-### Backend support
-
-If you are developing with a backend for a plugin and would like to submit it to the [decky-plugin-database](https://github.com/SteamDeckHomebrew/decky-plugin-database) you will need to have all backend code located in ``backend/src``, with backend being located in the root of your git repository.
-When building your plugin, the source code will be built and any finished binary or binaries will be output to ``backend/out`` (which is created during CI.)
-If your buildscript, makefile or any other build method does not place the binary files in the ``backend/out`` directory they will not be properly picked up during CI and your plugin will not have the required binaries included for distribution.
-
-Example:
-In our makefile used to demonstrate the CI process of building and distributing a plugin backend, note that the makefile explicitly creates the `out` folder (``backend/out``) and then compiles the binary into that folder. Here's the relevant snippet.
-
-```make
-hello:
- mkdir -p ./out
- gcc -o ./out/hello ./src/main.c
-```
-
-The CI does create the `out` folder itself but we recommend creating it yourself if possible during your build process to ensure the build process goes smoothly.
-
-Note: When locally building your plugin it will be placed into a folder called 'out' this is different from the concept described above.
-
-The out folder is not sent to the final plugin, but is then put into a ``bin`` folder which is found at the root of the plugin's directory.
-More information on the bin folder can be found below in the distribution section below.
-
-### Distribution
-
-We recommend following the instructions found in the [decky-plugin-database](https://github.com/SteamDeckHomebrew/decky-plugin-database) on how to get your plugin up on the plugin store. This is the best way to get your plugin in front of users.
-You can also choose to do distribution via a zip file containing the needed files, if that zip file is uploaded to a URL it can then be downloaded and installed via decky-loader.
-
-**NOTE: We do not currently have a method to install from a downloaded zip file in "game-mode" due to lack of a usable file-picking dialog.**
-
-Layout of a plugin zip ready for distribution:
-```
-pluginname-v1.0.0.zip (version number is optional but recommended for users sake)
- |
- pluginname/ <directory>
- | | |
- | | bin/ <directory> (optional)
- | | |
- | | binary (optional)
- | |
- | dist/ <directory> [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]
-```
-
-Note regarding licenses: Including a license is required for the plugin store if your chosen license requires the license to be included alongside usage of source-code/binaries!
-
-Standard procedure for licenses is to have your chosen license at the top of the file, and to leave the original license for the plugin-template at the bottom. If this is not the case on submission to the plugin database, you will be asked to fix this discrepancy.
-
-We cannot and will not distribute your plugin on the Plugin Store if it's license requires it's inclusion but you have not included a license to be re-distributed with your plugin in the root of your git repository.
+[PancakeTAS](https://github.com/PancakeTAS/lsfg-vk) for creating the lsfg-vk compatibility layer. \ No newline at end of file
diff --git a/assets/image.png b/assets/image.png
new file mode 100644
index 0000000..51680a3
--- /dev/null
+++ b/assets/image.png
Binary files differ
diff --git a/assets/lsfg-vk.png b/assets/lsfg-vk.png
new file mode 100644
index 0000000..0395798
--- /dev/null
+++ b/assets/lsfg-vk.png
Binary files differ
diff --git a/justfile b/justfile
new file mode 100644
index 0000000..e108534
--- /dev/null
+++ b/justfile
@@ -0,0 +1,11 @@
+default:
+ echo "Available recipes: build, test, clean"
+
+build:
+ rm -rf node_modules && .vscode/build.sh
+
+test:
+ scp "/Users/kurt/Developer/decky-plugin-template/out/lsfg-vk Installer.zip" deck@192.168.0.6:~/Desktop
+
+clean:
+ rm -rf node_modules dist \ No newline at end of file
diff --git a/main.py b/main.py
index 65a10da..3d24f94 100644
--- a/main.py
+++ b/main.py
@@ -1,4 +1,8 @@
import os
+import tarfile
+import shutil
+import subprocess
+import tempfile
# The decky plugin module is located at decky-loader/plugin
# For easy intellisense checkout the decky-loader code repo
@@ -7,35 +11,183 @@ import decky
import asyncio
class Plugin:
- # A normal method. It can be called from the TypeScript side using @decky/api.
- async def add(self, left: int, right: int) -> int:
- return left + right
+ async def install_lsfg_vk(self) -> dict:
+ """Install lsfg-vk by extracting the tar.gz file to ~/.local"""
+ try:
+ # Get the path to the lsfg-vk-latest.tar.gz file in the bin directory
+ plugin_dir = os.path.dirname(os.path.realpath(__file__))
+ tar_path = os.path.join(plugin_dir, "bin", "lsfg-vk-latest.tar.gz")
+
+ # Check if the tar.gz file exists
+ if not os.path.exists(tar_path):
+ decky.logger.error(f"lsfg-vk-latest.tar.gz not found at {tar_path}")
+ return {"success": False, "error": "lsfg-vk-latest.tar.gz file not found"}
+
+ # Get the user's home directory
+ user_home = os.path.expanduser("~")
+ local_lib_dir = os.path.join(user_home, ".local", "lib")
+ local_share_dir = os.path.join(user_home, ".local", "share", "vulkan", "implicit_layer.d")
+
+ # Create directories if they don't exist
+ os.makedirs(local_lib_dir, exist_ok=True)
+ os.makedirs(local_share_dir, exist_ok=True)
+
+ # Extract the tar.gz file
+ with tarfile.open(tar_path, 'r:gz') as tar_ref:
+ # Use /tmp for temporary extraction since we may not have write permissions in plugin dir
+ with tempfile.TemporaryDirectory() as temp_dir:
+ tar_ref.extractall(temp_dir)
+
+ # Look for the extracted files and copy them to the correct locations
+ for root, dirs, files in os.walk(temp_dir):
+ for file in files:
+ src_file = os.path.join(root, file)
+ if file.endswith('.so'):
+ # Copy library files to ~/.local/lib
+ dst_file = os.path.join(local_lib_dir, file)
+ shutil.copy2(src_file, dst_file)
+ decky.logger.info(f"Copied {file} to {dst_file}")
+ elif file.endswith('.json'):
+ # Copy JSON files to ~/.local/share/vulkan/implicit_layer.d
+ dst_file = os.path.join(local_share_dir, file)
+ shutil.copy2(src_file, dst_file)
+ decky.logger.info(f"Copied {file} to {dst_file}")
+
+ # temp_dir will be automatically cleaned up
+
+ decky.logger.info("lsfg-vk installed successfully")
+ return {"success": True, "message": "lsfg-vk installed successfully"}
+
+ except Exception as e:
+ decky.logger.error(f"Error installing lsfg-vk: {str(e)}")
+ return {"success": False, "error": str(e)}
- async def long_running(self):
- await asyncio.sleep(15)
- # Passing through a bunch of random data, just as an example
- await decky.emit("timer_event", "Hello from the backend!", True, 2)
+ async def check_lsfg_vk_installed(self) -> dict:
+ """Check if lsfg-vk is already installed"""
+ try:
+ user_home = os.path.expanduser("~")
+ lib_file = os.path.join(user_home, ".local", "lib", "liblsfg-vk.so")
+ json_file = os.path.join(user_home, ".local", "share", "vulkan", "implicit_layer.d", "VkLayer_LS_frame_generation.json")
+
+ lib_exists = os.path.exists(lib_file)
+ json_exists = os.path.exists(json_file)
+
+ return {
+ "installed": lib_exists and json_exists,
+ "lib_exists": lib_exists,
+ "json_exists": json_exists,
+ "lib_path": lib_file,
+ "json_path": json_file
+ }
+
+ except Exception as e:
+ decky.logger.error(f"Error checking lsfg-vk installation: {str(e)}")
+ return {"installed": False, "error": str(e)}
+
+ async def uninstall_lsfg_vk(self) -> dict:
+ """Uninstall lsfg-vk by removing the installed files"""
+ try:
+ user_home = os.path.expanduser("~")
+ lib_file = os.path.join(user_home, ".local", "lib", "liblsfg-vk.so")
+ json_file = os.path.join(user_home, ".local", "share", "vulkan", "implicit_layer.d", "VkLayer_LS_frame_generation.json")
+
+ removed_files = []
+
+ # Remove library file if it exists
+ if os.path.exists(lib_file):
+ os.remove(lib_file)
+ removed_files.append(lib_file)
+ decky.logger.info(f"Removed {lib_file}")
+
+ # Remove JSON file if it exists
+ if os.path.exists(json_file):
+ os.remove(json_file)
+ removed_files.append(json_file)
+ decky.logger.info(f"Removed {json_file}")
+
+ if not removed_files:
+ return {"success": True, "message": "No lsfg-vk files found to remove"}
+
+ decky.logger.info("lsfg-vk uninstalled successfully")
+ return {
+ "success": True,
+ "message": f"lsfg-vk uninstalled successfully. Removed {len(removed_files)} files.",
+ "removed_files": removed_files
+ }
+
+ except Exception as e:
+ decky.logger.error(f"Error uninstalling lsfg-vk: {str(e)}")
+ return {"success": False, "error": str(e)}
+
+ async def check_lossless_scaling_dll(self) -> dict:
+ """Check if Lossless Scaling DLL is available at the expected paths"""
+ try:
+ # Check environment variable first
+ dll_path = os.getenv("LSFG_DLL_PATH")
+ if dll_path and dll_path.strip():
+ dll_path_str = dll_path.strip()
+ if os.path.exists(dll_path_str):
+ return {
+ "detected": True,
+ "path": dll_path_str,
+ "source": "LSFG_DLL_PATH environment variable"
+ }
+
+ # Check XDG_DATA_HOME path
+ data_dir = os.getenv("XDG_DATA_HOME")
+ if data_dir and data_dir.strip():
+ dll_path_str = os.path.join(data_dir.strip(), "Steam", "steamapps", "common", "Lossless Scaling", "Lossless.dll")
+ if os.path.exists(dll_path_str):
+ return {
+ "detected": True,
+ "path": dll_path_str,
+ "source": "XDG_DATA_HOME Steam directory"
+ }
+
+ # Check HOME/.local/share path
+ home_dir = os.getenv("HOME")
+ if home_dir and home_dir.strip():
+ dll_path_str = os.path.join(home_dir.strip(), ".local", "share", "Steam", "steamapps", "common", "Lossless Scaling", "Lossless.dll")
+ if os.path.exists(dll_path_str):
+ return {
+ "detected": True,
+ "path": dll_path_str,
+ "source": "HOME/.local/share Steam directory"
+ }
+
+ # DLL not found in any expected location
+ return {
+ "detected": False,
+ "path": None,
+ "source": None,
+ "message": "Lossless Scaling DLL not found in expected locations"
+ }
+
+ except Exception as e:
+ decky.logger.error(f"Error checking Lossless Scaling DLL: {str(e)}")
+ return {
+ "detected": False,
+ "path": None,
+ "source": None,
+ "error": str(e)
+ }
# Asyncio-compatible long-running code, executed in a task when the plugin is loaded
async def _main(self):
- self.loop = asyncio.get_event_loop()
- decky.logger.info("Hello World!")
+ decky.logger.info("lsfg-vk Installer loaded!")
# Function called first during the unload process, utilize this to handle your plugin being stopped, but not
# completely removed
async def _unload(self):
- decky.logger.info("Goodnight World!")
+ decky.logger.info("lsfg-vk Installer unloading")
pass
# Function called after `_unload` during uninstall, utilize this to clean up processes and other remnants of your
# plugin that may remain on the system
async def _uninstall(self):
- decky.logger.info("Goodbye World!")
+ decky.logger.info("lsfg-vk Installer uninstalled")
pass
- async def start_timer(self):
- self.loop.create_task(self.long_running())
-
# Migrations that should be performed before entering `_main()`.
async def _migration(self):
decky.logger.info("Migrating")
diff --git a/package.json b/package.json
index 9501ba3..6b60965 100644
--- a/package.json
+++ b/package.json
@@ -39,6 +39,15 @@
"react-icons": "^5.3.0",
"tslib": "^2.7.0"
},
+ "remote_binary_bundling" : true,
+ "remote_binary":
+ [
+ {
+ "name": "lsfg-vk-latest.tar.gz",
+ "url": "https://github.com/xXJSONDeruloXx/lsfg-vk/releases/download/latest/lsfg-vk-latest.tar.gz",
+ "sha256hash": "913e7bba9f1b58339df0b03cd9a8c4dd8d165912224a869b9ee8dd983ab8f3f7"
+ }
+ ],
"pnpm": {
"peerDependencyRules": {
"ignoreMissing": [
diff --git a/plugin.json b/plugin.json
index 568bded..1873005 100644
--- a/plugin.json
+++ b/plugin.json
@@ -1,11 +1,11 @@
{
- "name": "Example Plugin",
- "author": "John Doe",
+ "name": "Lossless Scaling",
+ "author": "Kurt Himebauch",
"flags": ["debug", "_root"],
"api_version": 1,
"publish": {
- "tags": ["template", "root"],
- "description": "Decky example plugin.",
- "image": "https://opengraph.githubassets.com/1/SteamDeckHomebrew/PluginLoader"
+ "tags": ["installer", "vulkan", "lsfg"],
+ "description": "Install lsfg-vk Vulkan layer for frame generation.",
+ "image": "https://github.com/xXJSONDeruloXx/decky-lossless-scaling/blob/main/assets/lsfg-vk.png?raw=true"
}
}
diff --git a/py_modules/.keep b/py_modules/.keep
deleted file mode 100644
index e69de29..0000000
--- a/py_modules/.keep
+++ /dev/null
diff --git a/src/index.tsx b/src/index.tsx
index 16cd6cb..1ab6f62 100755
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -2,114 +2,232 @@ import {
ButtonItem,
PanelSection,
PanelSectionRow,
- Navigation,
staticClasses
} from "@decky/ui";
import {
- addEventListener,
- removeEventListener,
callable,
definePlugin,
toaster,
- // routerHook
} from "@decky/api"
-import { useState } from "react";
-import { FaShip } from "react-icons/fa";
+import { useState, useEffect } from "react";
+import { FaDownload, FaTrash } from "react-icons/fa";
+import { GiPlasticDuck } from "react-icons/gi";
-// import logo from "../assets/logo.png";
+// Function to install lsfg-vk
+const installLsfgVk = callable<[], { success: boolean; error?: string; message?: string }>("install_lsfg_vk");
-// This function calls the python function "add", which takes in two numbers and returns their sum (as a number)
-// Note the type annotations:
-// the first one: [first: number, second: number] is for the arguments
-// the second one: number is for the return value
-const add = callable<[first: number, second: number], number>("add");
+// Function to uninstall lsfg-vk
+const uninstallLsfgVk = callable<[], { success: boolean; error?: string; message?: string; removed_files?: string[] }>("uninstall_lsfg_vk");
-// This function calls the python function "start_timer", which takes in no arguments and returns nothing.
-// It starts a (python) timer which eventually emits the event 'timer_event'
-const startTimer = callable<[], void>("start_timer");
+// Function to check if lsfg-vk is installed
+const checkLsfgVkInstalled = callable<[], { installed: boolean; lib_exists: boolean; json_exists: boolean; lib_path: string; json_path: string; error?: string }>("check_lsfg_vk_installed");
+
+// Function to check if Lossless Scaling DLL is available
+const checkLosslessScalingDll = callable<[], { detected: boolean; path?: string; source?: string; message?: string; error?: string }>("check_lossless_scaling_dll");
function Content() {
- const [result, setResult] = useState<number | undefined>();
+ const [isInstalled, setIsInstalled] = useState<boolean>(false);
+ const [isInstalling, setIsInstalling] = useState<boolean>(false);
+ const [isUninstalling, setIsUninstalling] = useState<boolean>(false);
+ const [installationStatus, setInstallationStatus] = useState<string>("");
+ const [dllDetected, setDllDetected] = useState<boolean>(false);
+ const [dllDetectionStatus, setDllDetectionStatus] = useState<string>("");
+
+ // Check installation status on component mount
+ useEffect(() => {
+ const checkInstallation = async () => {
+ try {
+ const status = await checkLsfgVkInstalled();
+ setIsInstalled(status.installed);
+ if (status.installed) {
+ setInstallationStatus("lsfg-vk is installed");
+ } else {
+ setInstallationStatus("lsfg-vk is not installed");
+ }
+ } catch (error) {
+ setInstallationStatus("Error checking installation status");
+ }
+ };
+
+ const checkDllDetection = async () => {
+ try {
+ const result = await checkLosslessScalingDll();
+ setDllDetected(result.detected);
+ if (result.detected) {
+ setDllDetectionStatus(`Lossless Scaling App detected (${result.source})`);
+ } else {
+ setDllDetectionStatus(result.message || "Lossless Scaling App not detected");
+ }
+ } catch (error) {
+ setDllDetectionStatus("Error checking Lossless Scaling App");
+ }
+ };
+
+ checkInstallation();
+ checkDllDetection();
+ }, []);
+
+ const handleInstall = async () => {
+ setIsInstalling(true);
+ setInstallationStatus("Installing lsfg-vk...");
+
+ try {
+ const result = await installLsfgVk();
+ if (result.success) {
+ setIsInstalled(true);
+ setInstallationStatus("lsfg-vk installed successfully!");
+ toaster.toast({
+ title: "Installation Complete",
+ body: "lsfg-vk has been installed successfully"
+ });
+ } else {
+ setInstallationStatus(`Installation failed: ${result.error}`);
+ toaster.toast({
+ title: "Installation Failed",
+ body: result.error || "Unknown error occurred"
+ });
+ }
+ } catch (error) {
+ setInstallationStatus(`Installation failed: ${error}`);
+ toaster.toast({
+ title: "Installation Failed",
+ body: `Error: ${error}`
+ });
+ } finally {
+ setIsInstalling(false);
+ }
+ };
- const onClick = async () => {
- const result = await add(Math.random(), Math.random());
- setResult(result);
+ const handleUninstall = async () => {
+ setIsUninstalling(true);
+ setInstallationStatus("Uninstalling lsfg-vk...");
+
+ try {
+ const result = await uninstallLsfgVk();
+ if (result.success) {
+ setIsInstalled(false);
+ setInstallationStatus("lsfg-vk uninstalled successfully!");
+ toaster.toast({
+ title: "Uninstallation Complete",
+ body: result.message || "lsfg-vk has been uninstalled successfully"
+ });
+ } else {
+ setInstallationStatus(`Uninstallation failed: ${result.error}`);
+ toaster.toast({
+ title: "Uninstallation Failed",
+ body: result.error || "Unknown error occurred"
+ });
+ }
+ } catch (error) {
+ setInstallationStatus(`Uninstallation failed: ${error}`);
+ toaster.toast({
+ title: "Uninstallation Failed",
+ body: `Error: ${error}`
+ });
+ } finally {
+ setIsUninstalling(false);
+ }
};
return (
- <PanelSection title="Panel Section">
+ <PanelSection title="lsfg-vk Installation">
<PanelSectionRow>
- <ButtonItem
- layout="below"
- onClick={onClick}
- >
- {result ?? "Add two numbers via Python"}
- </ButtonItem>
+ <div style={{ marginBottom: "8px", fontSize: "14px" }}>
+ <div style={{
+ color: dllDetected ? "#4CAF50" : "#F44336",
+ fontWeight: "bold",
+ marginBottom: "4px"
+ }}>
+ {dllDetectionStatus}
+ </div>
+ <div style={{
+ color: isInstalled ? "#4CAF50" : "#FF9800"
+ }}>
+ Status: {installationStatus}
+ </div>
+ </div>
</PanelSectionRow>
+
<PanelSectionRow>
<ButtonItem
layout="below"
- onClick={() => startTimer()}
+ onClick={isInstalled ? handleUninstall : handleInstall}
+ disabled={isInstalling || isUninstalling}
>
- {"Start Python timer"}
+ {isInstalling ? (
+ <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
+ <div>Installing...</div>
+ </div>
+ ) : isUninstalling ? (
+ <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
+ <div>Uninstalling...</div>
+ </div>
+ ) : isInstalled ? (
+ <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
+ <FaTrash />
+ <div>Uninstall lsfg-vk</div>
+ </div>
+ ) : (
+ <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
+ <FaDownload />
+ <div>Install lsfg-vk</div>
+ </div>
+ )}
</ButtonItem>
</PanelSectionRow>
-
- {/* <PanelSectionRow>
- <div style={{ display: "flex", justifyContent: "center" }}>
- <img src={logo} />
+
+ <PanelSectionRow>
+ <div style={{
+ fontSize: "13px",
+ marginTop: "12px",
+ padding: "8px",
+ backgroundColor: "rgba(255, 255, 255, 0.05)",
+ borderRadius: "4px"
+ }}>
+ <div style={{ fontWeight: "bold", marginBottom: "6px" }}>
+ Usage Instructions:
+ </div>
+ <div style={{ marginBottom: "4px" }}>
+ Add to your game's launch options in Steam:
+ </div>
+ <div style={{
+ fontFamily: "monospace",
+ backgroundColor: "rgba(0, 0, 0, 0.3)",
+ padding: "4px",
+ borderRadius: "2px",
+ fontSize: "12px",
+ marginBottom: "6px"
+ }}>
+ ENABLE_LSFG=1 LSFG_MULTIPLIER=2 %COMMAND%
+ </div>
+ <div style={{ fontSize: "11px", opacity: 0.8 }}>
+ • ENABLE_LSFG=1 - Enables frame generation
+ <br />
+ • LSFG_MULTIPLIER=2-4 - FPS multiplier (start with 2)
+ <br />
+ • LSFG_FLOW_SCALE=1.0 - Flow scale (optional, for performance)
+ </div>
</div>
- </PanelSectionRow> */}
-
- {/*<PanelSectionRow>
- <ButtonItem
- layout="below"
- onClick={() => {
- Navigation.Navigate("/decky-plugin-test");
- Navigation.CloseSideMenus();
- }}
- >
- Router
- </ButtonItem>
- </PanelSectionRow>*/}
+ </PanelSectionRow>
</PanelSection>
);
};
export default definePlugin(() => {
- console.log("Template plugin initializing, this is called once on frontend startup")
-
- // serverApi.routerHook.addRoute("/decky-plugin-test", DeckyPluginRouterTest, {
- // exact: true,
- // });
-
- // Add an event listener to the "timer_event" event from the backend
- const listener = addEventListener<[
- test1: string,
- test2: boolean,
- test3: number
- ]>("timer_event", (test1, test2, test3) => {
- console.log("Template got timer_event with:", test1, test2, test3)
- toaster.toast({
- title: "template got timer_event",
- body: `${test1}, ${test2}, ${test3}`
- });
- });
+ console.log("lsfg-vk Installer plugin initializing")
return {
// The name shown in various decky menus
- name: "Test Plugin",
+ name: "lsfg-vk Installer",
// The element displayed at the top of your plugin's menu
- titleView: <div className={staticClasses.Title}>Decky Example Plugin</div>,
+ titleView: <div className={staticClasses.Title}>lsfg-vk Installer</div>,
// The content of your plugin's menu
content: <Content />,
// The icon displayed in the plugin list
- icon: <FaShip />,
+ icon: <GiPlasticDuck />,
// The function triggered when your plugin unloads
onDismount() {
- console.log("Unloading")
- removeEventListener("timer_event", listener);
- // serverApi.routerHook.removeRoute("/decky-plugin-test");
+ console.log("lsfg-vk Installer unloading")
},
};
});