From fb3d3d4d3414394237cc9d664d65c14be179927f Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 18 Jul 2025 20:59:47 -0400 Subject: add nerd stuff modal with dll details --- package.json | 2 +- py_modules/lsfg_vk/plugin.py | 62 +++++++++++++++++++++++++ src/api/lsfgApi.ts | 9 ++++ src/components/Content.tsx | 17 ++++++- src/components/NerdStuffModal.tsx | 95 +++++++++++++++++++++++++++++++++++++++ src/components/index.ts | 1 + 6 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 src/components/NerdStuffModal.tsx diff --git a/package.json b/package.json index d224bf6..54135fd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lossless-scaling-vk", - "version": "0.6.1", + "version": "0.6.2", "description": "Use Lossless Scaling on the Steam Deck using the lsfg-vk vulkan layer", "type": "module", "scripts": { diff --git a/py_modules/lsfg_vk/plugin.py b/py_modules/lsfg_vk/plugin.py index a44840d..60eab8c 100644 --- a/py_modules/lsfg_vk/plugin.py +++ b/py_modules/lsfg_vk/plugin.py @@ -10,6 +10,7 @@ import json import subprocess import urllib.request import ssl +import hashlib from typing import Dict, Any from pathlib import Path @@ -101,6 +102,67 @@ class Plugin: return result_dict + async def get_dll_stats(self) -> Dict[str, Any]: + """Get detailed statistics about the detected DLL + + Returns: + Dict containing DLL path, SHA256 hash, and other stats + """ + try: + # First check if DLL is detected + dll_result = self.dll_detection_service.check_lossless_scaling_dll() + + if not dll_result.get("detected") or not dll_result.get("path"): + return { + "success": False, + "error": "DLL not detected", + "dll_path": None, + "dll_sha256": None + } + + dll_path = dll_result["path"] + if dll_path is None: + return { + "success": False, + "error": "DLL path is None", + "dll_path": None, + "dll_sha256": None + } + + dll_path_obj = Path(dll_path) + + # Calculate SHA256 hash + sha256_hash = hashlib.sha256() + try: + with open(dll_path_obj, "rb") as f: + # Read file in chunks to handle large files efficiently + for chunk in iter(lambda: f.read(4096), b""): + sha256_hash.update(chunk) + dll_sha256 = sha256_hash.hexdigest() + except Exception as e: + return { + "success": False, + "error": f"Failed to calculate SHA256: {str(e)}", + "dll_path": dll_path, + "dll_sha256": None + } + + return { + "success": True, + "dll_path": dll_path, + "dll_sha256": dll_sha256, + "dll_source": dll_result.get("source"), + "error": None + } + + except Exception as e: + return { + "success": False, + "error": f"Failed to get DLL stats: {str(e)}", + "dll_path": None, + "dll_sha256": None + } + # Configuration methods async def get_lsfg_config(self) -> Dict[str, Any]: """Read current lsfg script configuration diff --git a/src/api/lsfgApi.ts b/src/api/lsfgApi.ts index 5770c43..5bf2dc2 100644 --- a/src/api/lsfgApi.ts +++ b/src/api/lsfgApi.ts @@ -28,6 +28,14 @@ export interface DllDetectionResult { error?: string; } +export interface DllStatsResult { + success: boolean; + dll_path?: string; + dll_sha256?: string; + dll_source?: string; + error?: string; +} + // Use centralized configuration data type export type LsfgConfig = ConfigurationData; @@ -77,6 +85,7 @@ export const installLsfgVk = callable<[], InstallationResult>("install_lsfg_vk") export const uninstallLsfgVk = callable<[], InstallationResult>("uninstall_lsfg_vk"); export const checkLsfgVkInstalled = callable<[], InstallationStatus>("check_lsfg_vk_installed"); export const checkLosslessScalingDll = callable<[], DllDetectionResult>("check_lossless_scaling_dll"); +export const getDllStats = callable<[], DllStatsResult>("get_dll_stats"); export const getLsfgConfig = callable<[], ConfigResult>("get_lsfg_config"); export const getConfigSchema = callable<[], ConfigSchemaResult>("get_config_schema"); export const getLaunchOption = callable<[], LaunchOptionResult>("get_launch_option"); diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 613e722..9ce4c35 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -1,5 +1,5 @@ import { useEffect } from "react"; -import { PanelSection } from "@decky/ui"; +import { PanelSection, showModal, ButtonItem, PanelSectionRow } from "@decky/ui"; import { useInstallationStatus, useDllDetection, useLsfgConfig } from "../hooks/useLsfgHooks"; import { useInstallationActions } from "../hooks/useInstallationActions"; import { StatusDisplay } from "./StatusDisplay"; @@ -9,6 +9,7 @@ import { UsageInstructions } from "./UsageInstructions"; import { WikiButton } from "./WikiButton"; import { ClipboardButton } from "./ClipboardButton"; import { PluginUpdateChecker } from "./PluginUpdateChecker"; +import { NerdStuffModal } from "./NerdStuffModal"; import { ConfigurationData } from "../config/configSchema"; export function Content() { @@ -49,6 +50,10 @@ export function Content() { handleUninstall(setIsInstalled, setInstallationStatus); }; + const handleShowNerdStuff = () => { + showModal(); + }; + return ( + + {/* Nerd Stuff Button */} + + + Nerd Stuff + + ); } diff --git a/src/components/NerdStuffModal.tsx b/src/components/NerdStuffModal.tsx new file mode 100644 index 0000000..4dd53b1 --- /dev/null +++ b/src/components/NerdStuffModal.tsx @@ -0,0 +1,95 @@ +import { useState, useEffect } from "react"; +import { + ModalRoot, + Field, + Focusable +} from "@decky/ui"; +import { getDllStats, DllStatsResult } from "../api/lsfgApi"; + +interface NerdStuffModalProps { + closeModal?: () => void; +} + +export function NerdStuffModal({ closeModal }: NerdStuffModalProps) { + const [dllStats, setDllStats] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const loadDllStats = async () => { + try { + setLoading(true); + setError(null); + const result = await getDllStats(); + setDllStats(result); + } catch (err) { + setError(err instanceof Error ? err.message : "Failed to load DLL stats"); + } finally { + setLoading(false); + } + }; + + loadDllStats(); + }, []); + + const formatSHA256 = (hash: string) => { + // Format SHA256 hash for better readability (add spaces every 8 characters) + return hash.replace(/(.{8})/g, '$1 ').trim(); + }; + + const copyToClipboard = async (text: string) => { + try { + await navigator.clipboard.writeText(text); + // Could add a toast notification here if desired + } catch (err) { + console.error("Failed to copy to clipboard:", err); + } + }; + + return ( + + {loading && ( +
Loading DLL information...
+ )} + + {error && ( +
Error: {error}
+ )} + + {!loading && !error && dllStats && ( + <> + {!dllStats.success ? ( +
{dllStats.error || "Failed to get DLL stats"}
+ ) : ( +
+ + dllStats.dll_path && copyToClipboard(dllStats.dll_path)} + onActivate={() => dllStats.dll_path && copyToClipboard(dllStats.dll_path)} + > + {dllStats.dll_path || "Not available"} + + + + + dllStats.dll_sha256 && copyToClipboard(dllStats.dll_sha256)} + onActivate={() => dllStats.dll_sha256 && copyToClipboard(dllStats.dll_sha256)} + > + {dllStats.dll_sha256 ? formatSHA256(dllStats.dll_sha256) : "Not available"} + + + + {dllStats.dll_source && ( + +
{dllStats.dll_source}
+
+ )} + +
+ )} + + )} +
+ ); +} diff --git a/src/components/index.ts b/src/components/index.ts index ed0b803..c0c4804 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -7,3 +7,4 @@ export { WikiButton } from "./WikiButton"; export { ClipboardButton } from "./ClipboardButton"; export { LaunchOptionInfo } from "./LaunchOptionInfo"; export { PluginUpdateChecker } from "./PluginUpdateChecker"; +export { NerdStuffModal } from "./NerdStuffModal"; -- cgit v1.2.3 From a96d2764f036e2ea8f8625533b2f37b8291eda31 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 18 Jul 2025 21:04:10 -0400 Subject: fix offset notches in multiplier --- src/components/ConfigurationSection.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/ConfigurationSection.tsx b/src/components/ConfigurationSection.tsx index 00717bc..a55395e 100644 --- a/src/components/ConfigurationSection.tsx +++ b/src/components/ConfigurationSection.tsx @@ -56,10 +56,10 @@ export function ConfigurationSection({ step={1} notchCount={4} notchLabels={[ - { notchIndex: 1, label: "OFF" }, - { notchIndex: 2, label: "2X" }, - { notchIndex: 3, label: "3X" }, - { notchIndex: 4, label: "4X" } + { notchIndex: 0, label: "OFF" }, + { notchIndex: 1, label: "2X" }, + { notchIndex: 2, label: "3X" }, + { notchIndex: 3, label: "4X" } ]} onChange={(value) => onConfigChange('multiplier', value)} /> -- cgit v1.2.3 From 2087af2315b14e82117e643c16686e9555aa9d9b Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 18 Jul 2025 23:33:42 -0400 Subject: rm circular logic bug with legacy enable bool --- package.json | 2 +- py_modules/lsfg_vk/config_schema.py | 34 ++++++++------------------------- py_modules/lsfg_vk/configuration.py | 7 +++---- py_modules/lsfg_vk/plugin.py | 5 ++--- src/api/lsfgApi.ts | 4 ++-- src/components/ConfigurationSection.tsx | 8 ++++---- src/components/UsageInstructions.tsx | 6 +----- src/config/configSchema.ts | 10 +--------- tests/test_configuration.py | 2 -- 9 files changed, 22 insertions(+), 56 deletions(-) diff --git a/package.json b/package.json index 54135fd..dc41930 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lossless-scaling-vk", - "version": "0.6.2", + "version": "0.6.3", "description": "Use Lossless Scaling on the Steam Deck using the lsfg-vk vulkan layer", "type": "module", "scripts": { diff --git a/py_modules/lsfg_vk/config_schema.py b/py_modules/lsfg_vk/config_schema.py index 460b0a0..45afc2d 100644 --- a/py_modules/lsfg_vk/config_schema.py +++ b/py_modules/lsfg_vk/config_schema.py @@ -38,13 +38,6 @@ class ConfigField: # Configuration schema definition CONFIG_SCHEMA: Dict[str, ConfigField] = { - "enable": ConfigField( - name="enable", - field_type=ConfigFieldType.BOOLEAN, - default=True, - description="enable/disable lsfg on every game" - ), - "dll": ConfigField( name="dll", field_type=ConfigFieldType.STRING, @@ -55,7 +48,7 @@ CONFIG_SCHEMA: Dict[str, ConfigField] = { "multiplier": ConfigField( name="multiplier", field_type=ConfigFieldType.INTEGER, - default=2, + default=1, description="change the fps multiplier" ), @@ -112,7 +105,6 @@ CONFIG_SCHEMA: Dict[str, ConfigField] = { class ConfigurationData(TypedDict): """Type-safe configuration data structure""" - enable: bool dll: str multiplier: int flow_scale: float @@ -219,25 +211,21 @@ class ConfigurationManager: # Add all configuration fields to the game section for field_name, field_def in CONFIG_SCHEMA.items(): - # Skip dll and enable fields - dll goes in global, enable is handled via multiplier - if field_name in ["dll", "enable"]: + # Skip dll field - dll goes in global section + if field_name == "dll": continue value = config[field_name] - # Handle enable field by setting multiplier to 1 when disabled - if field_name == "multiplier" and not config.get("enable", True): - value = 1 - lines.append(f"# LSFG disabled via plugin - multiplier set to 1") - else: - lines.append(f"# {field_def.description}") + # Add field description comment + lines.append(f"# {field_def.description}") # Format value based on type if isinstance(value, bool): lines.append(f"{field_name} = {str(value).lower()}") elif isinstance(value, str) and value: # Only add non-empty strings lines.append(f'{field_name} = "{value}"') - elif isinstance(value, (int, float)) and value != 0: # Only add non-zero numbers + elif isinstance(value, (int, float)): # Always include numbers, even if 0 or 1 lines.append(f"{field_name} = {value}") lines.append("") # Empty line for readability @@ -306,12 +294,7 @@ class ConfigurationManager: config[key] = value.lower() in ('true', '1', 'yes', 'on') elif field_def.field_type == ConfigFieldType.INTEGER: parsed_value = int(value) - # Handle enable field via multiplier - if key == "multiplier": - config[key] = parsed_value - config["enable"] = parsed_value != 1 - else: - config[key] = parsed_value + config[key] = parsed_value elif field_def.field_type == ConfigFieldType.FLOAT: config[key] = float(value) elif field_def.field_type == ConfigFieldType.STRING: @@ -327,7 +310,7 @@ class ConfigurationManager: return ConfigurationManager.get_defaults() @staticmethod - def create_config_from_args(enable: bool, dll: str, multiplier: int, flow_scale: float, + def create_config_from_args(dll: str, multiplier: int, flow_scale: float, performance_mode: bool, hdr_mode: bool, experimental_present_mode: str = "", experimental_fps_limit: int = 0, @@ -335,7 +318,6 @@ class ConfigurationManager: disable_steamdeck_mode: bool = False) -> ConfigurationData: """Create configuration from individual arguments""" return cast(ConfigurationData, { - "enable": enable, "dll": dll, "multiplier": multiplier, "flow_scale": flow_scale, diff --git a/py_modules/lsfg_vk/configuration.py b/py_modules/lsfg_vk/configuration.py index ae0194b..1952a49 100644 --- a/py_modules/lsfg_vk/configuration.py +++ b/py_modules/lsfg_vk/configuration.py @@ -65,7 +65,7 @@ class ConfigurationService(BaseService): "error": None } - def update_config(self, enable: bool, dll: str, multiplier: int, flow_scale: float, + def update_config(self, dll: str, multiplier: int, flow_scale: float, performance_mode: bool, hdr_mode: bool, experimental_present_mode: str = "", experimental_fps_limit: int = 0, @@ -74,7 +74,6 @@ class ConfigurationService(BaseService): """Update TOML configuration Args: - enable: Whether to enable LSFG dll: Path to Lossless.dll multiplier: LSFG multiplier value flow_scale: LSFG flow scale value @@ -91,7 +90,7 @@ class ConfigurationService(BaseService): try: # Create configuration from individual arguments config = ConfigurationManager.create_config_from_args( - enable, dll, multiplier, flow_scale, performance_mode, hdr_mode, + dll, multiplier, flow_scale, performance_mode, hdr_mode, experimental_present_mode, experimental_fps_limit, enable_wow64, disable_steamdeck_mode ) @@ -109,7 +108,7 @@ class ConfigurationService(BaseService): if not script_result["success"]: self.log.warning(f"Failed to update launch script: {script_result['error']}") - self.log.info(f"Updated lsfg TOML configuration: enable={enable}, " + self.log.info(f"Updated lsfg TOML configuration: " f"dll='{dll}', multiplier={multiplier}, flow_scale={flow_scale}, " f"performance_mode={performance_mode}, hdr_mode={hdr_mode}, " f"experimental_present_mode='{experimental_present_mode}', " diff --git a/py_modules/lsfg_vk/plugin.py b/py_modules/lsfg_vk/plugin.py index 60eab8c..4e19a2a 100644 --- a/py_modules/lsfg_vk/plugin.py +++ b/py_modules/lsfg_vk/plugin.py @@ -184,7 +184,7 @@ class Plugin: "defaults": ConfigurationManager.get_defaults() } - async def update_lsfg_config(self, enable: bool, dll: str, multiplier: int, flow_scale: float, + async def update_lsfg_config(self, dll: str, multiplier: int, flow_scale: float, performance_mode: bool, hdr_mode: bool, experimental_present_mode: str = "", experimental_fps_limit: int = 0, @@ -193,7 +193,6 @@ class Plugin: """Update lsfg TOML configuration Args: - enable: Whether to enable LSFG dll: Path to Lossless.dll multiplier: LSFG multiplier value flow_scale: LSFG flow scale value @@ -208,7 +207,7 @@ class Plugin: ConfigurationResponse dict with success status """ return self.configuration_service.update_config( - enable, dll, multiplier, flow_scale, performance_mode, hdr_mode, + dll, multiplier, flow_scale, performance_mode, hdr_mode, experimental_present_mode, experimental_fps_limit, enable_wow64, disable_steamdeck_mode ) diff --git a/src/api/lsfgApi.ts b/src/api/lsfgApi.ts index 5bf2dc2..4c23955 100644 --- a/src/api/lsfgApi.ts +++ b/src/api/lsfgApi.ts @@ -92,14 +92,14 @@ export const getLaunchOption = callable<[], LaunchOptionResult>("get_launch_opti // Updated config function using centralized configuration export const updateLsfgConfig = callable< - [boolean, string, number, number, boolean, boolean, string, number, boolean, boolean], + [string, number, number, boolean, boolean, string, number, boolean, boolean], ConfigUpdateResult >("update_lsfg_config"); // Helper function to create config update from configuration object export const updateLsfgConfigFromObject = async (config: ConfigurationData): Promise => { const args = ConfigurationManager.createArgsFromConfig(config); - return updateLsfgConfig(...args as [boolean, string, number, number, boolean, boolean, string, number, boolean, boolean]); + return updateLsfgConfig(...args as [string, number, number, boolean, boolean, string, number, boolean, boolean]); }; // Self-updater API functions diff --git a/src/components/ConfigurationSection.tsx b/src/components/ConfigurationSection.tsx index a55395e..00717bc 100644 --- a/src/components/ConfigurationSection.tsx +++ b/src/components/ConfigurationSection.tsx @@ -56,10 +56,10 @@ export function ConfigurationSection({ step={1} notchCount={4} notchLabels={[ - { notchIndex: 0, label: "OFF" }, - { notchIndex: 1, label: "2X" }, - { notchIndex: 2, label: "3X" }, - { notchIndex: 3, label: "4X" } + { notchIndex: 1, label: "OFF" }, + { notchIndex: 2, label: "2X" }, + { notchIndex: 3, label: "3X" }, + { notchIndex: 4, label: "4X" } ]} onChange={(value) => onConfigChange('multiplier', value)} /> diff --git a/src/components/UsageInstructions.tsx b/src/components/UsageInstructions.tsx index be48a82..5de1fcf 100644 --- a/src/components/UsageInstructions.tsx +++ b/src/components/UsageInstructions.tsx @@ -33,10 +33,7 @@ export function UsageInstructions({ config }: UsageInstructionsProps) { whiteSpace: "pre-wrap" }} > - {config.enable - ? "Add the launch option below (or use \"Launch Option Clipboard\") to Steam games to activate frame generation." - : "LSFG is disabled. Enable it above and add the launch option to activate frame generation." - } + Add the launch option below (or use "Launch Option Clipboard") to Steam games to activate frame generation. @@ -69,7 +66,6 @@ export function UsageInstructions({ config }: UsageInstructionsProps) { }} > {`Current Configuration: -• Enable: ${config.enable ? "Yes" : "No"} • DLL Path: ${config.dll} • Multiplier: ${config.multiplier}x • Flow Scale: ${Math.round(config.flow_scale * 100)}% diff --git a/src/config/configSchema.ts b/src/config/configSchema.ts index 8715057..fa54336 100644 --- a/src/config/configSchema.ts +++ b/src/config/configSchema.ts @@ -23,13 +23,6 @@ export interface ConfigField { // Configuration schema - must match Python CONFIG_SCHEMA export const CONFIG_SCHEMA: Record = { - enable: { - name: "enable", - fieldType: ConfigFieldType.BOOLEAN, - default: true, - description: "enable/disable lsfg on every game" - }, - dll: { name: "dll", fieldType: ConfigFieldType.STRING, @@ -40,7 +33,7 @@ export const CONFIG_SCHEMA: Record = { multiplier: { name: "multiplier", fieldType: ConfigFieldType.INTEGER, - default: 2, + default: 1, description: "change the fps multiplier" }, @@ -96,7 +89,6 @@ export const CONFIG_SCHEMA: Record = { // Type-safe configuration data structure export interface ConfigurationData { - enable: boolean; dll: string; multiplier: number; flow_scale: number; diff --git a/tests/test_configuration.py b/tests/test_configuration.py index f3fdcbe..1b10413 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -128,7 +128,6 @@ def test_config_roundtrip(): # Update config result = service.update_config( - enable=True, dll="/path/to/dll", multiplier=3, flow_scale=1.5, @@ -147,7 +146,6 @@ def test_config_roundtrip(): assert read_result["success"] is True config = read_result["config"] - assert config["enable"] is True assert config["dll"] == "/path/to/dll" assert config["multiplier"] == 3 assert config["flow_scale"] == 1.5 -- cgit v1.2.3 From 6dafc215f2728832d4ea6ef873dbfa34d58dc0e4 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 18 Jul 2025 23:42:14 -0400 Subject: fix labels --- src/components/ConfigurationSection.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/ConfigurationSection.tsx b/src/components/ConfigurationSection.tsx index 00717bc..a55395e 100644 --- a/src/components/ConfigurationSection.tsx +++ b/src/components/ConfigurationSection.tsx @@ -56,10 +56,10 @@ export function ConfigurationSection({ step={1} notchCount={4} notchLabels={[ - { notchIndex: 1, label: "OFF" }, - { notchIndex: 2, label: "2X" }, - { notchIndex: 3, label: "3X" }, - { notchIndex: 4, label: "4X" } + { notchIndex: 0, label: "OFF" }, + { notchIndex: 1, label: "2X" }, + { notchIndex: 2, label: "3X" }, + { notchIndex: 3, label: "4X" } ]} onChange={(value) => onConfigChange('multiplier', value)} /> -- cgit v1.2.3 From 75a94598341899eea5260206975686c05e793956 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Sat, 19 Jul 2025 00:48:06 -0400 Subject: fix notchindex value offset from step --- src/components/ConfigurationSection.tsx | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/src/components/ConfigurationSection.tsx b/src/components/ConfigurationSection.tsx index a55395e..ad99c28 100644 --- a/src/components/ConfigurationSection.tsx +++ b/src/components/ConfigurationSection.tsx @@ -28,24 +28,6 @@ export function ConfigurationSection({ - {/* - onConfigChange('enable', value)} - /> - */} - - {/* - onConfigChange('dll', e.target.value)} - /> - */} - onConfigChange('multiplier', value)} /> -- cgit v1.2.3