diff options
| author | xXJSONDeruloXx <danielhimebauch@gmail.com> | 2025-07-19 19:35:30 -0400 |
|---|---|---|
| committer | xXJSONDeruloXx <danielhimebauch@gmail.com> | 2025-07-19 19:35:30 -0400 |
| commit | 1d044bf320cd0c9d03daa8213df078e834e20c49 (patch) | |
| tree | e48cb29f1d32942c04b1d40b7ddcc7cb79d24845 | |
| parent | 3ce6ba534900e68c815b4012a456842d3003c6da (diff) | |
| download | decky-lsfg-vk-1d044bf320cd0c9d03daa8213df078e834e20c49.tar.gz decky-lsfg-vk-1d044bf320cd0c9d03daa8213df078e834e20c49.zip | |
restore script env var val on mount check and UI state set
| -rw-r--r-- | package.json | 2 | ||||
| -rw-r--r-- | py_modules/lsfg_vk/config_schema.py | 67 | ||||
| -rw-r--r-- | py_modules/lsfg_vk/configuration.py | 30 | ||||
| -rw-r--r-- | tests/test_configuration.py | 83 |
4 files changed, 140 insertions, 42 deletions
diff --git a/package.json b/package.json index 8d73665..2d6fea4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "decky-lossless-scaling-vk", - "version": "0.6.6", + "version": "0.6.7", "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 54e414f..1098248 100644 --- a/py_modules/lsfg_vk/config_schema.py +++ b/py_modules/lsfg_vk/config_schema.py @@ -316,6 +316,73 @@ class ConfigurationManager: return ConfigurationManager.get_defaults() @staticmethod + def parse_script_content(script_content: str) -> Dict[str, Union[bool, int, str]]: + """Parse launch script content to extract environment variable values + + Args: + script_content: Content of the launch script file + + Returns: + Dict containing parsed script-only field values + """ + script_values = {} + + try: + lines = script_content.split('\n') + + for line in lines: + line = line.strip() + + # Skip comments, empty lines, and non-export lines + if not line or line.startswith('#') or not line.startswith('export '): + continue + + # Parse export statements: export VAR=value + if '=' in line: + # Remove 'export ' prefix + export_line = line[7:] # len('export ') = 7 + key, value = export_line.split('=', 1) + key = key.strip() + value = value.strip() + + # Map environment variables to config field names + if key == "DXVK_FRAME_RATE": + try: + script_values["dxvk_frame_rate"] = int(value) + except ValueError: + pass + elif key == "PROTON_USE_WOW64": + script_values["enable_wow64"] = value == "1" + elif key == "SteamDeck": + script_values["disable_steamdeck_mode"] = value == "0" + + except Exception: + # If parsing fails, return empty dict (will use defaults) + pass + + return script_values + + @staticmethod + def merge_config_with_script(toml_config: ConfigurationData, script_values: Dict[str, Union[bool, int, str]]) -> ConfigurationData: + """Merge TOML configuration with script environment variable values + + Args: + toml_config: Configuration loaded from TOML file + script_values: Environment variable values parsed from script + + Returns: + Complete configuration with script values overlaid on TOML config + """ + merged_config = dict(toml_config) + + # Update script-only fields with values from script + for field_name in SCRIPT_ONLY_FIELDS.keys(): + if field_name in script_values: + merged_config[field_name] = script_values[field_name] + + return cast(ConfigurationData, merged_config) + + @staticmethod def create_config_from_args(dll: str, multiplier: int, flow_scale: float, performance_mode: bool, hdr_mode: bool, experimental_present_mode: str = "fifo", diff --git a/py_modules/lsfg_vk/configuration.py b/py_modules/lsfg_vk/configuration.py index 1336f21..da765e0 100644 --- a/py_modules/lsfg_vk/configuration.py +++ b/py_modules/lsfg_vk/configuration.py @@ -14,26 +14,34 @@ class ConfigurationService(BaseService): """Service for managing TOML-based lsfg configuration""" def get_config(self) -> ConfigurationResponse: - """Read current TOML configuration + """Read current TOML configuration merged with launch script environment variables Returns: ConfigurationResponse with current configuration or error """ try: + # Get TOML configuration (with defaults if file doesn't exist) if not self.config_file_path.exists(): # Return default configuration with DLL detection if file doesn't exist from .dll_detection import DllDetectionService dll_service = DllDetectionService(self.log) - config = ConfigurationManager.get_defaults_with_dll_detection(dll_service) - return { - "success": True, - "config": config, - "message": "Using default configuration (config file not found)", - "error": None - } + toml_config = ConfigurationManager.get_defaults_with_dll_detection(dll_service) + else: + content = self.config_file_path.read_text(encoding='utf-8') + toml_config = ConfigurationManager.parse_toml_content(content) + + # Get script environment variables (if script exists) + script_values = {} + if self.lsfg_script_path.exists(): + try: + script_content = self.lsfg_script_path.read_text(encoding='utf-8') + script_values = ConfigurationManager.parse_script_content(script_content) + self.log.info(f"Parsed script values: {script_values}") + except Exception as e: + self.log.warning(f"Failed to parse launch script: {str(e)}") - content = self.config_file_path.read_text(encoding='utf-8') - config = ConfigurationManager.parse_toml_content(content) + # Merge TOML config with script values + config = ConfigurationManager.merge_config_with_script(toml_config, script_values) return { "success": True, @@ -151,7 +159,7 @@ class ConfigurationService(BaseService): ConfigurationResponse with success status """ try: - # Get current config + # Get current merged config (TOML + script) current_response = self.get_config() if not current_response["success"] or current_response["config"] is None: # If we can't read current config, use defaults with DLL detection diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 9b306e5..3e0ad79 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -7,46 +7,69 @@ from pathlib import Path from unittest.mock import Mock from lsfg_vk.configuration import ConfigurationService +from lsfg_vk.config_schema import ConfigurationManager def test_parse_script_content(): - """Test parsing of script content""" - mock_logger = Mock() + """Test parsing of script content with current environment variable format""" - with tempfile.TemporaryDirectory() as temp_dir: - temp_home = Path(temp_dir) - - # Create service with mocked home directory - service = ConfigurationService(logger=mock_logger) - service.user_home = temp_home - service.lsfg_script_path = temp_home / "lsfg" - - # Test script content - script_content = """#!/bin/bash + # Test script content matching current format + script_content = """#!/bin/bash +# lsfg-vk launch script generated by decky-lossless-scaling-vk plugin +# This script sets up the environment for lsfg-vk to work with the plugin configuration +export PROTON_USE_WOW64=1 +export SteamDeck=0 +export DXVK_FRAME_RATE=18 +export LSFG_PROCESS=decky-lsfg-vk +exec "$@" +""" + + script_values = ConfigurationManager.parse_script_content(script_content) + + assert script_values["enable_wow64"] is True + assert script_values["disable_steamdeck_mode"] is True # SteamDeck=0 means disable + assert script_values["dxvk_frame_rate"] == 18 -export ENABLE_LSFG=1 -export LSFG_MULTIPLIER=3 -export LSFG_FLOW_SCALE=1.5 -export LSFG_HDR=1 -# export LSFG_PERF_MODE=1 -export MESA_VK_WSI_PRESENT_MODE=immediate # - disable vsync +def test_parse_script_content_minimal(): + """Test parsing when only required exports are present""" + + script_content = """#!/bin/bash +# lsfg-vk launch script generated by decky-lossless-scaling-vk plugin +export LSFG_PROCESS=decky-lsfg-vk exec "$@" """ - - config = service._parse_script_content(script_content) - - assert config["enable_lsfg"] is True - assert config["multiplier"] == 3 - assert config["flow_scale"] == 1.5 - assert config["hdr"] is True - assert config["perf_mode"] is False # commented out - assert config["immediate_mode"] is True + + script_values = ConfigurationManager.parse_script_content(script_content) + + # Should be empty dict since no tracked env vars are present + assert script_values == {} -def test_parse_script_content_all_commented(): - """Test parsing when all optional features are commented out""" - mock_logger = Mock() +def test_merge_config_with_script(): + """Test merging TOML config with script environment variables""" + + # Get defaults + toml_config = ConfigurationManager.get_defaults() + + # Script values from parsing + script_values = { + "enable_wow64": True, + "disable_steamdeck_mode": True, + "dxvk_frame_rate": 30 + } + + merged = ConfigurationManager.merge_config_with_script(toml_config, script_values) + + # TOML fields should be preserved + assert merged["multiplier"] == 1 # default from TOML + assert merged["flow_scale"] == 0.8 # default from TOML + assert merged["performance_mode"] is True # default from TOML + + # Script fields should be overlaid + assert merged["enable_wow64"] is True + assert merged["disable_steamdeck_mode"] is True + assert merged["dxvk_frame_rate"] == 30 with tempfile.TemporaryDirectory() as temp_dir: temp_home = Path(temp_dir) |
