summaryrefslogtreecommitdiff
path: root/py_modules
diff options
context:
space:
mode:
authorxXJSONDeruloXx <danielhimebauch@gmail.com>2025-09-22 09:38:20 -0400
committerxXJSONDeruloXx <danielhimebauch@gmail.com>2025-09-22 09:38:20 -0400
commit0da3682755e551a7d3c23fa979686d8dbcdd4f7b (patch)
treef07d360ebaeefba28fe5b5be730c807ce337bddc /py_modules
parent84de5901cd1fb7d89031e4e7b3b47ed805e324c8 (diff)
downloaddecky-lsfg-vk-0da3682755e551a7d3c23fa979686d8dbcdd4f7b.tar.gz
decky-lsfg-vk-0da3682755e551a7d3c23fa979686d8dbcdd4f7b.zip
bring forward old flatpak modal attempt
Diffstat (limited to 'py_modules')
-rw-r--r--py_modules/lsfg_vk/constants.py4
-rw-r--r--py_modules/lsfg_vk/flatpak_service.py400
-rw-r--r--py_modules/lsfg_vk/plugin.py63
3 files changed, 467 insertions, 0 deletions
diff --git a/py_modules/lsfg_vk/constants.py b/py_modules/lsfg_vk/constants.py
index 69989c1..614e2fc 100644
--- a/py_modules/lsfg_vk/constants.py
+++ b/py_modules/lsfg_vk/constants.py
@@ -17,6 +17,10 @@ LIB_FILENAME = "liblsfg-vk.so"
JSON_FILENAME = "VkLayer_LS_frame_generation.json"
ZIP_FILENAME = "lsfg-vk_noui.zip"
+# Flatpak files
+FLATPAK_23_08_FILENAME = "org.freedesktop.Platform.VulkanLayer.lsfg_vk_23.08.flatpak"
+FLATPAK_24_08_FILENAME = "org.freedesktop.Platform.VulkanLayer.lsfg_vk_24.08.flatpak"
+
# File extensions
SO_EXT = ".so"
JSON_EXT = ".json"
diff --git a/py_modules/lsfg_vk/flatpak_service.py b/py_modules/lsfg_vk/flatpak_service.py
index e69de29..3fb621d 100644
--- a/py_modules/lsfg_vk/flatpak_service.py
+++ b/py_modules/lsfg_vk/flatpak_service.py
@@ -0,0 +1,400 @@
+"""
+Flatpak service for managing lsfg-vk Flatpak runtime extensions.
+"""
+
+import subprocess
+import os
+from pathlib import Path
+from typing import Dict, Any, List, Optional
+
+from .base_service import BaseService
+from .constants import (
+ FLATPAK_23_08_FILENAME, FLATPAK_24_08_FILENAME, BIN_DIR, CONFIG_DIR
+)
+from .types import BaseResponse
+
+
+class FlatpakExtensionStatus(BaseResponse):
+ """Response for Flatpak extension status"""
+ def __init__(self, success: bool = False, message: str = "", error: str = "",
+ installed_23_08: bool = False, installed_24_08: bool = False):
+ super().__init__(success, message, error)
+ self.installed_23_08 = installed_23_08
+ self.installed_24_08 = installed_24_08
+
+
+class FlatpakAppInfo(BaseResponse):
+ """Response for Flatpak app information"""
+ def __init__(self, success: bool = False, message: str = "", error: str = "",
+ apps: List[Dict[str, Any]] = None, total_apps: int = 0):
+ super().__init__(success, message, error)
+ self.apps = apps or []
+ self.total_apps = total_apps
+
+
+class FlatpakOverrideResponse(BaseResponse):
+ """Response for Flatpak override operations"""
+ def __init__(self, success: bool = False, message: str = "", error: str = "",
+ app_id: str = "", operation: str = ""):
+ super().__init__(success, message, error)
+ self.app_id = app_id
+ self.operation = operation
+
+
+class FlatpakService(BaseService):
+ """Service for handling Flatpak runtime extensions and app overrides"""
+
+ def __init__(self, logger=None):
+ super().__init__(logger)
+ self.extension_id_23_08 = "org.freedesktop.Platform.VulkanLayer.lsfgvk/x86_64/23.08"
+ self.extension_id_24_08 = "org.freedesktop.Platform.VulkanLayer.lsfgvk/x86_64/24.08"
+ self.flatpak_command = None # Will be set when flatpak is detected
+ def __init__(self, logger=None):
+ super().__init__(logger)
+ self.extension_id_23_08 = "org.freedesktop.Platform.VulkanLayer.lsfgvk/x86_64/23.08"
+ self.extension_id_24_08 = "org.freedesktop.Platform.VulkanLayer.lsfgvk/x86_64/24.08"
+ self.flatpak_command = None # Will be set when flatpak is detected
+
+ def _get_clean_env(self):
+ """Get a clean environment without PyInstaller's bundled libraries"""
+ import os
+
+ # Create a clean environment without PyInstaller's bundled libraries
+ env = os.environ.copy()
+
+ # Remove LD_LIBRARY_PATH that might point to PyInstaller's bundled libs
+ if 'LD_LIBRARY_PATH' in env:
+ del env['LD_LIBRARY_PATH']
+
+ # Ensure PATH includes standard binary locations
+ standard_paths = ['/usr/bin', '/usr/local/bin', '/bin']
+ current_path = env.get('PATH', '')
+
+ # Add standard paths if they're not already there
+ path_parts = current_path.split(':') if current_path else []
+ for std_path in standard_paths:
+ if std_path not in path_parts:
+ path_parts.insert(0, std_path)
+
+ env['PATH'] = ':'.join(path_parts)
+
+ return env
+
+ def _run_flatpak_command(self, args: List[str], **kwargs):
+ """Run flatpak command with clean environment to avoid library conflicts"""
+ if self.flatpak_command is None:
+ raise FileNotFoundError("Flatpak command not available")
+
+ env = self._get_clean_env()
+
+ # Log environment info for debugging
+ self.log.info(f"Running flatpak with PATH: {env.get('PATH')}")
+ self.log.info(f"LD_LIBRARY_PATH removed: {'LD_LIBRARY_PATH' not in env}")
+
+ # Run the command with the clean environment
+ return subprocess.run([self.flatpak_command] + args, env=env, **kwargs)
+
+ def check_flatpak_available(self) -> bool:
+ """Check if flatpak command is available and store the working command"""
+ import os
+
+ # Log environment info for debugging
+ self.log.info(f"PATH: {os.environ.get('PATH', 'Not set')}")
+ self.log.info(f"HOME: {os.environ.get('HOME', 'Not set')}")
+ self.log.info(f"USER: {os.environ.get('USER', 'Not set')}")
+
+ # Try common flatpak installation paths, starting with the standard command
+ flatpak_paths = [
+ "flatpak",
+ "/usr/bin/flatpak",
+ "/var/lib/flatpak/exports/bin/flatpak",
+ "/home/deck/.local/bin/flatpak"
+ ]
+
+ for flatpak_path in flatpak_paths:
+ try:
+ result = subprocess.run([flatpak_path, "--version"],
+ capture_output=True, check=True, text=True,
+ env=self._get_clean_env())
+ self.log.info(f"Flatpak found at {flatpak_path}: {result.stdout.strip()}")
+ self.flatpak_command = flatpak_path
+ return True
+ except (subprocess.CalledProcessError, FileNotFoundError):
+ self.log.debug(f"Flatpak not found at {flatpak_path}")
+ continue
+
+ self.log.error("Flatpak command not found in any known locations")
+ self.flatpak_command = None
+ return False
+
+ def get_extension_status(self) -> FlatpakExtensionStatus:
+ """Check if lsfg-vk Flatpak extensions are installed"""
+ try:
+ if not self.check_flatpak_available():
+ error_msg = "Flatpak is not available on this system"
+ if self.flatpak_command is None:
+ error_msg += ". Command not found in PATH or common install locations."
+ self.log.error(error_msg)
+ return self._error_response(FlatpakExtensionStatus,
+ error_msg,
+ installed_23_08=False, installed_24_08=False)
+
+ # Get list of installed runtimes
+ result = self._run_flatpak_command(
+ ["list", "--runtime"],
+ capture_output=True, text=True, check=True
+ )
+
+ installed_runtimes = result.stdout
+
+ # Check for both versions by looking for the base extension name and version
+ base_extension_name = "org.freedesktop.Platform.VulkanLayer.lsfgvk"
+ installed_23_08 = False
+ installed_24_08 = False
+
+ for line in installed_runtimes.split('\n'):
+ if base_extension_name in line:
+ if "23.08" in line:
+ installed_23_08 = True
+ elif "24.08" in line:
+ installed_24_08 = True
+
+ status_msg = []
+ if installed_23_08:
+ status_msg.append("23.08 runtime extension installed")
+ if installed_24_08:
+ status_msg.append("24.08 runtime extension installed")
+
+ if not status_msg:
+ status_msg.append("No lsfg-vk runtime extensions installed")
+
+ return self._success_response(FlatpakExtensionStatus,
+ "; ".join(status_msg),
+ installed_23_08=installed_23_08,
+ installed_24_08=installed_24_08)
+
+ except subprocess.CalledProcessError as e:
+ error_msg = f"Error checking Flatpak extensions: {e.stderr if e.stderr else str(e)}"
+ self.log.error(error_msg)
+ return self._error_response(FlatpakExtensionStatus, error_msg,
+ installed_23_08=False, installed_24_08=False)
+
+ def install_extension(self, version: str) -> BaseResponse:
+ """Install a specific version of the lsfg-vk Flatpak extension"""
+ try:
+ if version not in ["23.08", "24.08"]:
+ return self._error_response(BaseResponse, "Invalid version. Must be '23.08' or '24.08'")
+
+ if not self.check_flatpak_available():
+ return self._error_response(BaseResponse, "Flatpak is not available on this system")
+
+ # Get the path to the flatpak file
+ plugin_dir = Path(__file__).parent.parent.parent
+ filename = FLATPAK_23_08_FILENAME if version == "23.08" else FLATPAK_24_08_FILENAME
+ flatpak_path = plugin_dir / BIN_DIR / filename
+
+ if not flatpak_path.exists():
+ return self._error_response(BaseResponse, f"Flatpak file not found: {flatpak_path}")
+
+ # Install the extension
+ result = self._run_flatpak_command(
+ ["install", "--user", "--noninteractive", str(flatpak_path)],
+ capture_output=True, text=True
+ )
+
+ if result.returncode != 0:
+ error_msg = f"Failed to install Flatpak extension: {result.stderr}"
+ self.log.error(error_msg)
+ return self._error_response(BaseResponse, error_msg)
+
+ self.log.info(f"Successfully installed lsfg-vk Flatpak extension {version}")
+ return self._success_response(BaseResponse, f"lsfg-vk {version} runtime extension installed successfully")
+
+ except Exception as e:
+ error_msg = f"Error installing Flatpak extension {version}: {str(e)}"
+ self.log.error(error_msg)
+ return self._error_response(BaseResponse, error_msg)
+
+ def uninstall_extension(self, version: str) -> BaseResponse:
+ """Uninstall a specific version of the lsfg-vk Flatpak extension"""
+ try:
+ if version not in ["23.08", "24.08"]:
+ return self._error_response(BaseResponse, "Invalid version. Must be '23.08' or '24.08'")
+
+ if not self.check_flatpak_available():
+ return self._error_response(BaseResponse, "Flatpak is not available on this system")
+
+ extension_id = self.extension_id_23_08 if version == "23.08" else self.extension_id_24_08
+
+ # Uninstall the extension
+ result = self._run_flatpak_command(
+ ["uninstall", "--user", "--noninteractive", extension_id],
+ capture_output=True, text=True
+ )
+
+ if result.returncode != 0:
+ error_msg = f"Failed to uninstall Flatpak extension: {result.stderr}"
+ self.log.error(error_msg)
+ return self._error_response(BaseResponse, error_msg)
+
+ self.log.info(f"Successfully uninstalled lsfg-vk Flatpak extension {version}")
+ return self._success_response(BaseResponse, f"lsfg-vk {version} runtime extension uninstalled successfully")
+
+ except Exception as e:
+ error_msg = f"Error uninstalling Flatpak extension {version}: {str(e)}"
+ self.log.error(error_msg)
+ return self._error_response(BaseResponse, error_msg)
+
+ def get_flatpak_apps(self) -> FlatpakAppInfo:
+ """Get list of installed Flatpak apps and their lsfg-vk override status"""
+ try:
+ if not self.check_flatpak_available():
+ error_msg = "Flatpak is not available on this system"
+ if self.flatpak_command is None:
+ error_msg += ". Command not found in PATH or common install locations."
+ return self._error_response(FlatpakAppInfo,
+ error_msg,
+ apps=[], total_apps=0)
+
+ # Get list of installed apps
+ result = self._run_flatpak_command(
+ ["list", "--app"],
+ capture_output=True, text=True, check=True
+ )
+
+ apps = []
+ for line in result.stdout.strip().split('\n'):
+ if not line.strip():
+ continue
+
+ # Parse flatpak list output (Name\tApp ID\tVersion\tBranch\tInstallation)
+ parts = line.split('\t')
+ if len(parts) >= 2:
+ app_name = parts[0].strip()
+ app_id = parts[1].strip()
+
+ # Check override status
+ override_status = self._check_app_override_status(app_id)
+
+ apps.append({
+ "app_id": app_id,
+ "app_name": app_name,
+ "has_filesystem_override": override_status["filesystem"],
+ "has_env_override": override_status["env"]
+ })
+
+ return self._success_response(FlatpakAppInfo,
+ f"Found {len(apps)} Flatpak applications",
+ apps=apps, total_apps=len(apps))
+
+ except subprocess.CalledProcessError as e:
+ error_msg = f"Error getting Flatpak apps: {e.stderr if e.stderr else str(e)}"
+ self.log.error(error_msg)
+ return self._error_response(FlatpakAppInfo, error_msg, apps=[], total_apps=0)
+
+ def _check_app_override_status(self, app_id: str) -> Dict[str, bool]:
+ """Check if an app has lsfg-vk overrides set"""
+ try:
+ result = self._run_flatpak_command(
+ ["override", "--user", "--show", app_id],
+ capture_output=True, text=True
+ )
+
+ if result.returncode != 0:
+ return {"filesystem": False, "env": False}
+
+ output = result.stdout
+ home_path = os.path.expanduser("~")
+ config_path = f"{home_path}/.config/lsfg-vk"
+
+ # Check for filesystem override in the [Context] section
+ # Format: filesystems=/home/kurt/.config/lsfg-vk;
+ filesystem_override = f"filesystems={config_path}" in output
+
+ # Check for environment override in the [Environment] section
+ # Format: LSFG_CONFIG=/home/kurt/.config/lsfg-vk/conf.toml
+ env_override = f"LSFG_CONFIG={config_path}/conf.toml" in output
+
+ return {"filesystem": filesystem_override, "env": env_override}
+
+ except Exception as e:
+ self.log.error(f"Error checking override status for {app_id}: {e}")
+ return {"filesystem": False, "env": False}
+
+ def set_app_override(self, app_id: str) -> FlatpakOverrideResponse:
+ """Set lsfg-vk overrides for a Flatpak app"""
+ try:
+ if not self.check_flatpak_available():
+ return self._error_response(FlatpakOverrideResponse,
+ "Flatpak is not available on this system",
+ app_id=app_id, operation="set")
+
+ home_path = os.path.expanduser("~")
+ config_path = f"{home_path}/.config/lsfg-vk"
+
+ # Set filesystem override
+ result1 = self._run_flatpak_command(
+ ["override", "--user", f"--filesystem={config_path}:rw", app_id],
+ capture_output=True, text=True
+ )
+
+ # Set environment override
+ result2 = self._run_flatpak_command(
+ ["override", "--user", f"--env=LSFG_CONFIG={config_path}/conf.toml", app_id],
+ capture_output=True, text=True
+ )
+
+ if result1.returncode != 0 or result2.returncode != 0:
+ error_msg = f"Failed to set overrides: {result1.stderr} {result2.stderr}"
+ return self._error_response(FlatpakOverrideResponse, error_msg,
+ app_id=app_id, operation="set")
+
+ self.log.info(f"Successfully set lsfg-vk overrides for {app_id}")
+ return self._success_response(FlatpakOverrideResponse,
+ f"lsfg-vk overrides set for {app_id}",
+ app_id=app_id, operation="set")
+
+ except Exception as e:
+ error_msg = f"Error setting overrides for {app_id}: {str(e)}"
+ self.log.error(error_msg)
+ return self._error_response(FlatpakOverrideResponse, error_msg,
+ app_id=app_id, operation="set")
+
+ def remove_app_override(self, app_id: str) -> FlatpakOverrideResponse:
+ """Remove lsfg-vk overrides for a Flatpak app"""
+ try:
+ if not self.check_flatpak_available():
+ return self._error_response(FlatpakOverrideResponse,
+ "Flatpak is not available on this system",
+ app_id=app_id, operation="remove")
+
+ home_path = os.path.expanduser("~")
+ config_path = f"{home_path}/.config/lsfg-vk"
+
+ # Remove filesystem override
+ result1 = self._run_flatpak_command(
+ ["override", "--user", f"--nofilesystem={config_path}", app_id],
+ capture_output=True, text=True
+ )
+
+ # Remove environment override
+ result2 = self._run_flatpak_command(
+ ["override", "--user", "--unset-env=LSFG_CONFIG", app_id],
+ capture_output=True, text=True
+ )
+
+ if result1.returncode != 0 or result2.returncode != 0:
+ error_msg = f"Failed to remove overrides: {result1.stderr} {result2.stderr}"
+ return self._error_response(FlatpakOverrideResponse, error_msg,
+ app_id=app_id, operation="remove")
+
+ self.log.info(f"Successfully removed lsfg-vk overrides for {app_id}")
+ return self._success_response(FlatpakOverrideResponse,
+ f"lsfg-vk overrides removed for {app_id}",
+ app_id=app_id, operation="remove")
+
+ except Exception as e:
+ error_msg = f"Error removing overrides for {app_id}: {str(e)}"
+ self.log.error(error_msg)
+ return self._error_response(FlatpakOverrideResponse, error_msg,
+ app_id=app_id, operation="remove") \ No newline at end of file
diff --git a/py_modules/lsfg_vk/plugin.py b/py_modules/lsfg_vk/plugin.py
index f08aa18..ccc9984 100644
--- a/py_modules/lsfg_vk/plugin.py
+++ b/py_modules/lsfg_vk/plugin.py
@@ -18,6 +18,7 @@ from .installation import InstallationService
from .dll_detection import DllDetectionService
from .configuration import ConfigurationService
from .config_schema import ConfigurationManager
+from .flatpak_service import FlatpakService
class Plugin:
@@ -35,6 +36,7 @@ class Plugin:
self.installation_service = InstallationService()
self.dll_detection_service = DllDetectionService()
self.configuration_service = ConfigurationService()
+ self.flatpak_service = FlatpakService()
# Installation methods
async def install_lsfg_vk(self) -> Dict[str, Any]:
@@ -612,6 +614,67 @@ class Plugin:
"exists": False,
"error": str(e)
}
+
+ # Flatpak management methods
+ async def check_flatpak_extension_status(self) -> Dict[str, Any]:
+ """Check status of lsfg-vk Flatpak runtime extensions
+
+ Returns:
+ FlatpakExtensionStatus dict with installation status for both runtime versions
+ """
+ return self.flatpak_service.get_extension_status()
+
+ async def install_flatpak_extension(self, version: str) -> Dict[str, Any]:
+ """Install lsfg-vk Flatpak runtime extension
+
+ Args:
+ version: Runtime version to install ("23.08" or "24.08")
+
+ Returns:
+ BaseResponse dict with success status and message/error
+ """
+ return self.flatpak_service.install_extension(version)
+
+ async def uninstall_flatpak_extension(self, version: str) -> Dict[str, Any]:
+ """Uninstall lsfg-vk Flatpak runtime extension
+
+ Args:
+ version: Runtime version to uninstall ("23.08" or "24.08")
+
+ Returns:
+ BaseResponse dict with success status and message/error
+ """
+ return self.flatpak_service.uninstall_extension(version)
+
+ async def get_flatpak_apps(self) -> Dict[str, Any]:
+ """Get list of installed Flatpak apps and their lsfg-vk override status
+
+ Returns:
+ FlatpakAppInfo dict with apps list and override status
+ """
+ return self.flatpak_service.get_flatpak_apps()
+
+ async def set_flatpak_app_override(self, app_id: str) -> Dict[str, Any]:
+ """Set lsfg-vk overrides for a Flatpak app
+
+ Args:
+ app_id: Flatpak application ID
+
+ Returns:
+ FlatpakOverrideResponse dict with operation result
+ """
+ return self.flatpak_service.set_app_override(app_id)
+
+ async def remove_flatpak_app_override(self, app_id: str) -> Dict[str, Any]:
+ """Remove lsfg-vk overrides for a Flatpak app
+
+ Args:
+ app_id: Flatpak application ID
+
+ Returns:
+ FlatpakOverrideResponse dict with operation result
+ """
+ return self.flatpak_service.remove_app_override(app_id)
# Decky Loader lifecycle methods