diff options
| author | xXJSONDeruloXx <danielhimebauch@gmail.com> | 2026-09-06 22:04:54 -0400 |
|---|---|---|
| committer | xXJSONDeruloXx <danielhimebauch@gmail.com> | 2026-09-06 22:04:54 -0400 |
| commit | 7bc5f685186b1ff03ce0f7c99e7bec411beabaeb (patch) | |
| tree | cf94a5752af7f45f6d4a87e89546705f32096766 /py_modules | |
| parent | 170d183fdafc1ec1a686c006fd6a2431c1f30c71 (diff) | |
| download | decky-lsfg-vk-7bc5f685186b1ff03ce0f7c99e7bec411beabaeb.tar.gz decky-lsfg-vk-7bc5f685186b1ff03ce0f7c99e7bec411beabaeb.zip | |
feat: make ui suck less, frfr
Diffstat (limited to 'py_modules')
| -rw-r--r-- | py_modules/lsfg_vk/config_schema.py | 52 | ||||
| -rw-r--r-- | py_modules/lsfg_vk/configuration.py | 56 | ||||
| -rw-r--r-- | py_modules/lsfg_vk/installation.py | 6 | ||||
| -rw-r--r-- | py_modules/lsfg_vk/plugin.py | 128 | ||||
| -rw-r--r-- | py_modules/lsfg_vk/types.py | 31 |
5 files changed, 28 insertions, 245 deletions
diff --git a/py_modules/lsfg_vk/config_schema.py b/py_modules/lsfg_vk/config_schema.py index 4ae1f4c..675ab61 100644 --- a/py_modules/lsfg_vk/config_schema.py +++ b/py_modules/lsfg_vk/config_schema.py @@ -4,18 +4,14 @@ import json import sys import tomllib from pathlib import Path -from typing import Any, Dict, TypedDict, cast +from typing import Any, Dict, TypedDict sys.path.insert(0, str(Path(__file__).parent.parent.parent)) -DEFAULT_PROFILE_NAME = "default" ConfigurationData = Dict[str, Any] class ProfileData(TypedDict): - # Internal compatibility field; the public API no longer exposes a - # currently selected profile. - current_profile: str profiles: Dict[str, Dict[str, Any]] global_config: Dict[str, Any] @@ -58,19 +54,6 @@ class ConfigurationManager: return {**GLOBAL_DEFAULTS, **PROFILE_DEFAULTS} @staticmethod - def get_field_names() -> list[str]: - return list(ConfigurationManager.get_defaults()) - - @staticmethod - def get_field_types() -> Dict[str, str]: - return { - "dll": "string", "no_fp16": "boolean", "active_in": "array", - "pacing_mode": "string", "multiplier": "integer", "flow_scale": "float", - "performance_mode": "boolean", "override_present_mode": "boolean", - "preserve_swapchain_image_count": "boolean", - } - - @staticmethod def validate_config(config: Dict[str, Any]) -> Dict[str, Any]: result = {**PROFILE_DEFAULTS, **GLOBAL_DEFAULTS} result.update({key: value for key, value in config.items() if key in result}) @@ -111,15 +94,6 @@ class ConfigurationManager: return ConfigurationManager.validate_config(raw) @staticmethod - def generate_toml_content(config: Dict[str, Any]) -> str: - data: ProfileData = { - "current_profile": DEFAULT_PROFILE_NAME, - "profiles": {DEFAULT_PROFILE_NAME: dict(config)}, - "global_config": {"dll": config.get("dll", ""), "no_fp16": config.get("no_fp16", False)}, - } - return ConfigurationManager.generate_toml_content_multi_profile(data) - - @staticmethod def generate_toml_content_multi_profile(profile_data: ProfileData) -> str: global_config = {**GLOBAL_DEFAULTS, **profile_data.get("global_config", {})} lines = ["version = 2", "", "[global]"] @@ -127,7 +101,9 @@ class ConfigurationManager: if dll: lines.append(f"dll = {_toml_value(dll)}") lines.append(f"allow_fp16 = {_toml_value(not bool(global_config.get('no_fp16', False)))}") - profiles = sorted(profile_data["profiles"].items(), key=lambda item: (item[0] != DEFAULT_PROFILE_NAME, item[0])) + profiles = sorted(profile_data["profiles"].items()) + if not profiles: + profiles = [("", {})] for name, raw in profiles: config = ConfigurationManager.validate_config({**raw, **global_config}) lines.extend(["", "[[profile]]", f"name = {_toml_value(name)}"]) @@ -157,16 +133,11 @@ class ConfigurationManager: profiles: Dict[str, Dict[str, Any]] = {} source_profiles = data.get("game", []) if version == 1 else data.get("profile", []) for profile in source_profiles: - name = str(profile.get("exe" if version == 1 else "name", DEFAULT_PROFILE_NAME)) - profiles[name] = ConfigurationManager._config_from_profile(profile, global_config) - if not profiles: - profiles[DEFAULT_PROFILE_NAME] = ConfigurationManager.validate_config(global_config) - elif DEFAULT_PROFILE_NAME not in profiles: - source = profiles.get("decky-lsfg-vk", next(iter(profiles.values()))) - profiles[DEFAULT_PROFILE_NAME] = {**source, "active_in": []} - if profiles.get("decky-lsfg-vk", {}).get("active_in", []) == []: - profiles.pop("decky-lsfg-vk", None) - return {"current_profile": DEFAULT_PROFILE_NAME, "profiles": profiles, "global_config": global_config} + name = str(profile.get("exe" if version == 1 else "name", "")) + config = ConfigurationManager._config_from_profile(profile, global_config) + if config["active_in"]: + profiles[name] = config + return {"profiles": profiles, "global_config": global_config} @staticmethod def is_legacy_v1(content: str) -> bool: @@ -174,8 +145,3 @@ class ConfigurationManager: return tomllib.loads(content).get("version") == 1 except tomllib.TOMLDecodeError: return False - - @staticmethod - def parse_toml_content(content: str) -> Dict[str, Any]: - data = ConfigurationManager.parse_toml_content_multi_profile(content) - return cast(Dict[str, Any], data["profiles"][DEFAULT_PROFILE_NAME]) diff --git a/py_modules/lsfg_vk/configuration.py b/py_modules/lsfg_vk/configuration.py index a4ee2cc..bec3828 100644 --- a/py_modules/lsfg_vk/configuration.py +++ b/py_modules/lsfg_vk/configuration.py @@ -2,7 +2,7 @@ import re from typing import Any, Dict from .base_service import BaseService -from .config_schema import ConfigurationManager, DEFAULT_PROFILE_NAME, ProfileData +from .config_schema import ConfigurationManager, ProfileData from .runtime_service import RuntimeService @@ -14,8 +14,7 @@ class ConfigurationService(BaseService): self.runtime_service = runtime_service or RuntimeService(logger=self.log) def _default_data(self) -> ProfileData: - defaults = ConfigurationManager.validate_config({}) - return {"current_profile": DEFAULT_PROFILE_NAME, "profiles": {DEFAULT_PROFILE_NAME: defaults}, "global_config": {"dll": "", "no_fp16": False}} + return {"profiles": {}, "global_config": {"dll": "", "no_fp16": False}} def _get_profile_data(self) -> ProfileData: if not self.config_file_path.exists(): @@ -32,8 +31,6 @@ class ConfigurationService(BaseService): name = str(game_name).strip() if not name: raise ValueError("game name is required") - if name == DEFAULT_PROFILE_NAME: - name = f"{name} ({appid})" existing = data["profiles"].get(name) if existing is not None and str(appid) not in existing.get("active_in", []): name = f"{name} ({appid})" @@ -54,14 +51,6 @@ class ConfigurationService(BaseService): def _public_config(config: Dict[str, Any]) -> Dict[str, Any]: return ConfigurationManager.validate_config(config) - def get_config(self) -> Dict[str, Any]: - try: - data = self._get_profile_data() - return self._success_response(dict, config=self._public_config(data["profiles"][DEFAULT_PROFILE_NAME])) - except Exception as error: - self.log.error(f"Error reading lsfg config: {error}") - return self._error_response(dict, str(error), config=None) - def get_game_configs(self) -> Dict[str, Any]: try: data = self._get_profile_data() @@ -71,26 +60,25 @@ class ConfigurationService(BaseService): if len(active_in) != 1 or not re.fullmatch(r"-?[0-9]+", str(active_in[0])): continue games.append({"appid": str(active_in[0]), "profile": name, "config": self._public_config(raw)}) - return self._success_response(dict, default=self._public_config(data["profiles"][DEFAULT_PROFILE_NAME]), games=games) + return self._success_response(dict, global_config=dict(data["global_config"]), games=games) except Exception as error: self.log.error(f"Error reading game configs: {error}") - return self._error_response(dict, str(error), default=None, games=[]) - - def get_game_config(self, appid: str) -> Dict[str, Any]: - try: - data = self._get_profile_data() - _, profile = self._profile_for_appid(data, appid) - return self._success_response(dict, appid=str(appid), exists=profile is not None, config=self._public_config(profile or data["profiles"][DEFAULT_PROFILE_NAME])) - except Exception as error: - return self._error_response(dict, str(error), appid=str(appid), exists=False, config=None) + return self._error_response(dict, str(error), games=[]) def update_game_config(self, appid: str, game_name: str, config: Dict[str, Any]) -> Dict[str, Any]: try: data = self._get_profile_data() old_name, _ = self._profile_for_appid(data, appid) name = self._profile_name(data, appid, game_name) - validated = self._public_config(config) + merged_config = {**data["global_config"], **config} + if not config.get("dll"): + merged_config["dll"] = data["global_config"].get("dll", "") + validated = self._public_config(merged_config) validated["active_in"] = [str(appid)] + data["global_config"] = { + "dll": validated["dll"], + "no_fp16": validated["no_fp16"], + } if old_name and old_name != name: data["profiles"].pop(old_name, None) data["profiles"][name] = validated @@ -106,27 +94,15 @@ class ConfigurationService(BaseService): if name: data["profiles"].pop(name, None) self._save_profile_data(data) - return self._success_response(dict, appid=str(appid), config=self._public_config(data["profiles"][DEFAULT_PROFILE_NAME])) + return self._success_response(dict, appid=str(appid), exists=False) except Exception as error: return self._error_response(dict, str(error), appid=str(appid), config=None) def reset_all_game_configs(self) -> Dict[str, Any]: try: data = self._get_profile_data() - data["profiles"] = {DEFAULT_PROFILE_NAME: data["profiles"][DEFAULT_PROFILE_NAME]} - self._save_profile_data(data) - return self._success_response(dict, default=self._public_config(data["profiles"][DEFAULT_PROFILE_NAME]), games=[]) - except Exception as error: - return self._error_response(dict, str(error), default=None, games=[]) - - def update_config_from_dict(self, config: Dict[str, Any]) -> Dict[str, Any]: - try: - data = self._get_profile_data() - validated = self._public_config(config) - validated["active_in"] = [] - data["profiles"][DEFAULT_PROFILE_NAME] = validated - data["global_config"] = {"dll": validated.get("dll", ""), "no_fp16": validated.get("no_fp16", False)} + data["profiles"] = {} self._save_profile_data(data) - return self._success_response(dict, config=validated) + return self._success_response(dict, global_config=dict(data["global_config"]), games=[]) except Exception as error: - return self._error_response(dict, str(error), config=None) + return self._error_response(dict, str(error), games=[]) diff --git a/py_modules/lsfg_vk/installation.py b/py_modules/lsfg_vk/installation.py index 0a728c7..09bd3a3 100644 --- a/py_modules/lsfg_vk/installation.py +++ b/py_modules/lsfg_vk/installation.py @@ -7,7 +7,7 @@ from pathlib import Path from typing import Dict from .base_service import BaseService -from .config_schema import ConfigurationManager, DEFAULT_PROFILE_NAME, ProfileData +from .config_schema import ConfigurationManager, ProfileData from .constants import ( ARCHIVE_FILENAME, BIN_DIR, @@ -141,8 +141,7 @@ class InstallationService(BaseService): else: default = dict(ConfigurationManager.get_defaults()) profile_data = ProfileData( - current_profile=DEFAULT_PROFILE_NAME, - profiles={DEFAULT_PROFILE_NAME: default}, + profiles={}, global_config={ "dll": default.get("dll", ""), "no_fp16": default.get("no_fp16", False), @@ -155,7 +154,6 @@ class InstallationService(BaseService): profile_data["profiles"][profile_name] = ConfigurationManager.validate_config( {**defaults, **raw_profile, **profile_data["global_config"]} ) - profile_data["current_profile"] = DEFAULT_PROFILE_NAME return profile_data def _resolve_dll_path(self, profile_data: ProfileData) -> bool: diff --git a/py_modules/lsfg_vk/plugin.py b/py_modules/lsfg_vk/plugin.py index 8c788a8..6977b9c 100644 --- a/py_modules/lsfg_vk/plugin.py +++ b/py_modules/lsfg_vk/plugin.py @@ -7,13 +7,11 @@ Vulkan layer for frame generation on Steam Deck. import os from typing import Dict, Any -from pathlib import Path import decky from .installation import InstallationService from .configuration import ConfigurationService -from .config_schema import ConfigurationManager from .flatpak_service import FlatpakService from .runtime_service import RuntimeService from .steam_service import SteamService @@ -63,46 +61,12 @@ class Plugin: """ return self.installation_service.uninstall() - async def get_lsfg_config(self) -> Dict[str, Any]: - """Read current lsfg script configuration - - Returns: - ConfigurationResponse dict with current configuration or error - """ - return self.configuration_service.get_config() - - async def get_config_schema(self) -> Dict[str, Any]: - """Get configuration schema information for frontend - - Returns: - Dict with field names, types, defaults, and profile information - """ - return { - "field_names": ConfigurationManager.get_field_names(), - "field_types": ConfigurationManager.get_field_types(), - "defaults": ConfigurationManager.get_defaults(), - } - - async def update_lsfg_config(self, config: Dict[str, Any]) -> Dict[str, Any]: - """Update lsfg TOML configuration using object-based API (single source of truth) - - Args: - config: Configuration data dictionary containing all settings - - Returns: - ConfigurationResponse dict with success status - """ - return self.configuration_service.update_config_from_dict(config) - async def get_game_configs(self) -> Dict[str, Any]: return self.configuration_service.get_game_configs() async def get_installed_games(self) -> Dict[str, Any]: return self.steam_service.get_installed_games() - async def get_game_config(self, appid: str) -> Dict[str, Any]: - return self.configuration_service.get_game_config(appid) - async def update_game_config(self, appid: str, game_name: str, config: Dict[str, Any]) -> Dict[str, Any]: return self.configuration_service.update_game_config(appid, game_name, config) @@ -112,72 +76,6 @@ class Plugin: async def reset_all_game_configs(self) -> Dict[str, Any]: return self.configuration_service.reset_all_game_configs() - async def _legacy_get_profiles(self) -> Dict[str, Any]: - """Get list of all profiles and current profile - - Returns: - ProfilesResponse dict with profile list and current profile - """ - return self.configuration_service.get_game_configs() - - async def _legacy_create_profile(self, profile_name: str, source_profile: str = None) -> Dict[str, Any]: - """Create a new profile - - Args: - profile_name: Name for the new profile - source_profile: Optional source profile to copy from (default: current profile) - - Returns: - ProfileResponse dict with success status - """ - return {"success": False, "error": "Named profiles were replaced by per-game AppID profiles"} - - async def _legacy_delete_profile(self, profile_name: str) -> Dict[str, Any]: - """Delete a profile - - Args: - profile_name: Name of the profile to delete - - Returns: - ProfileResponse dict with success status - """ - return {"success": False, "error": "Named profiles were replaced by per-game AppID profiles"} - - async def _legacy_rename_profile(self, old_name: str, new_name: str) -> Dict[str, Any]: - """Rename a profile - - Args: - old_name: Current profile name - new_name: New profile name - - Returns: - ProfileResponse dict with success status - """ - return {"success": False, "error": "Named profiles were replaced by per-game AppID profiles"} - - async def _legacy_set_current_profile(self, profile_name: str) -> Dict[str, Any]: - """Set the current active profile - - Args: - profile_name: Name of the profile to set as current - - Returns: - ProfileResponse dict with success status - """ - return {"success": False, "error": "There is no globally selected profile"} - - async def _legacy_update_profile_config(self, profile_name: str, config: Dict[str, Any]) -> Dict[str, Any]: - """Update configuration for a specific profile - - Args: - profile_name: Name of the profile to update - config: Configuration data dictionary containing settings - - Returns: - ConfigurationResponse dict with success status - """ - return {"success": False, "error": "Use update_game_config with a Steam AppID"} - async def get_config_file_content(self) -> Dict[str, Any]: """Get the current config file content @@ -209,32 +107,6 @@ class Plugin: "error": f"Error reading config file: {str(e)}" } - async def check_fgmod_directory(self) -> Dict[str, Any]: - """Check if the fgmod directory exists in the home directory - - Returns: - Dict with exists status and directory path - """ - try: - home_path = Path(decky.DECKY_USER_HOME) - fgmod_path = home_path / "fgmod" - - exists = fgmod_path.exists() and fgmod_path.is_dir() - - return { - "success": True, - "exists": exists, - "path": str(fgmod_path) - } - - except Exception as e: - decky.logger.error(f"Error checking fgmod directory: {e}") - return { - "success": False, - "exists": False, - "error": str(e) - } - async def check_flatpak_extension_status(self) -> Dict[str, Any]: """Check status of lsfg-vk Flatpak runtime extensions diff --git a/py_modules/lsfg_vk/types.py b/py_modules/lsfg_vk/types.py index ef6bab7..7b85708 100644 --- a/py_modules/lsfg_vk/types.py +++ b/py_modules/lsfg_vk/types.py @@ -2,8 +2,7 @@ Type definitions for the lsfg-vk plugin responses. """ -from typing import TypedDict, Optional, List, Dict, Any -from .config_schema import ConfigurationData +from typing import TypedDict, Optional, List class BaseResponse(TypedDict): @@ -53,31 +52,3 @@ class SteamBranchStatusResponse(TypedDict): target_branch: str needs_switch: bool restart_required: bool - -class ConfigurationResponse(BaseResponse): - """Response for configuration operations""" - config: Optional[ConfigurationData] - message: Optional[str] - error: Optional[str] - - -class ProfileConfig(TypedDict): - """Configuration for a single profile""" - exe: str - config: ConfigurationData - - -class ProfilesResponse(BaseResponse): - """Response for per-game upstream profiles""" - default: Optional[ConfigurationData] - games: Optional[List[Dict[str, Any]]] - message: Optional[str] - error: Optional[str] - - -class ProfileResponse(BaseResponse): - """Response for a per-game upstream profile""" - appid: Optional[str] - config: Optional[ConfigurationData] - message: Optional[str] - error: Optional[str] |
