summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package.json2
-rw-r--r--py_modules/lsfg_vk/config_schema.py67
-rw-r--r--py_modules/lsfg_vk/configuration.py30
-rw-r--r--tests/test_configuration.py83
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)