mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-06-08 01:47:45 +02:00
196 lines
9.6 KiB
Python
196 lines
9.6 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Protontricks detection and version mixin.
|
|
Extracted from protontricks_handler for file-size and domain separation.
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import subprocess
|
|
from pathlib import Path
|
|
import shutil
|
|
import logging
|
|
from typing import Optional, List
|
|
import sys
|
|
|
|
from .subprocess_utils import get_clean_subprocess_env
|
|
|
|
|
|
class ProtontricksDetectionMixin:
|
|
"""Mixin providing protontricks detection, Steam dir, bundled paths, and version checks."""
|
|
|
|
def _get_steam_dir_from_libraryfolders(self) -> Optional[Path]:
|
|
"""Determine Steam installation directory from libraryfolders.vdf."""
|
|
from ..handlers.path_handler import PathHandler
|
|
vdf_paths = [
|
|
Path.home() / ".steam/steam/config/libraryfolders.vdf",
|
|
Path.home() / ".local/share/Steam/config/libraryfolders.vdf",
|
|
Path.home() / ".steam/root/config/libraryfolders.vdf",
|
|
Path.home() / ".var/app/com.valvesoftware.Steam/.local/share/Steam/config/libraryfolders.vdf",
|
|
Path.home() / ".var/app/com.valvesoftware.Steam/data/Steam/config/libraryfolders.vdf",
|
|
]
|
|
for vdf_path in vdf_paths:
|
|
if vdf_path.is_file():
|
|
steam_dir = vdf_path.parent.parent
|
|
if (steam_dir / "steamapps").exists():
|
|
self.logger.debug(f"Determined STEAM_DIR from libraryfolders.vdf: {steam_dir}")
|
|
return steam_dir
|
|
library_paths = PathHandler.get_all_steam_library_paths()
|
|
if library_paths:
|
|
first_lib = library_paths[0]
|
|
if '.var/app/com.valvesoftware.Steam' in str(first_lib):
|
|
data_steam = Path.home() / ".var/app/com.valvesoftware.Steam/data/Steam"
|
|
if (data_steam / "steamapps").exists():
|
|
self.logger.debug(f"Determined STEAM_DIR from Flatpak data path: {data_steam}")
|
|
return data_steam
|
|
if (first_lib / "steamapps").exists():
|
|
self.logger.debug(f"Determined STEAM_DIR from Flatpak library path: {first_lib}")
|
|
return first_lib
|
|
elif (first_lib / "steamapps").exists():
|
|
self.logger.debug(f"Determined STEAM_DIR from native library path: {first_lib}")
|
|
return first_lib
|
|
self.logger.warning("Could not determine STEAM_DIR from libraryfolders.vdf")
|
|
return None
|
|
|
|
def _get_bundled_winetricks_path(self) -> Optional[Path]:
|
|
"""Get path to bundled winetricks (AppImage and dev)."""
|
|
possible_paths = []
|
|
if os.environ.get('APPDIR'):
|
|
possible_paths.append(Path(os.environ['APPDIR']) / 'opt' / 'jackify' / 'tools' / 'winetricks')
|
|
module_dir = Path(__file__).parent.parent.parent
|
|
possible_paths.append(module_dir / 'tools' / 'winetricks')
|
|
for path in possible_paths:
|
|
if path.exists() and os.access(path, os.X_OK):
|
|
self.logger.debug(f"Found bundled winetricks at: {path}")
|
|
return path
|
|
self.logger.warning(f"Bundled winetricks not found. Tried paths: {possible_paths}")
|
|
return None
|
|
|
|
def _get_bundled_cabextract_path(self) -> Optional[Path]:
|
|
"""Get path to bundled cabextract (AppImage and dev)."""
|
|
possible_paths = []
|
|
if os.environ.get('APPDIR'):
|
|
possible_paths.append(Path(os.environ['APPDIR']) / 'opt' / 'jackify' / 'tools' / 'cabextract')
|
|
module_dir = Path(__file__).parent.parent.parent
|
|
possible_paths.append(module_dir / 'tools' / 'cabextract')
|
|
for path in possible_paths:
|
|
if path.exists() and os.access(path, os.X_OK):
|
|
self.logger.debug(f"Found bundled cabextract at: {path}")
|
|
return path
|
|
self.logger.warning(f"Bundled cabextract not found. Tried paths: {possible_paths}")
|
|
return None
|
|
|
|
def _get_bundled_protontricks_wrapper_path(self) -> Optional[str]:
|
|
"""Return path to bundled protontricks wrapper script if any. Returns None to use python -m fallback."""
|
|
return None
|
|
|
|
def _get_clean_subprocess_env(self):
|
|
"""Create clean environment for subprocess (remove AppImage/bundle vars)."""
|
|
env = get_clean_subprocess_env()
|
|
if 'LD_LIBRARY_PATH_ORIG' in env:
|
|
env['LD_LIBRARY_PATH'] = env['LD_LIBRARY_PATH_ORIG']
|
|
else:
|
|
env.pop('LD_LIBRARY_PATH', None)
|
|
if 'DYLD_LIBRARY_PATH' in env and hasattr(sys, '_MEIPASS'):
|
|
dyld_entries = env['DYLD_LIBRARY_PATH'].split(os.pathsep)
|
|
cleaned_dyld = [p for p in dyld_entries if not p.startswith(sys._MEIPASS)]
|
|
if cleaned_dyld:
|
|
env['DYLD_LIBRARY_PATH'] = os.pathsep.join(cleaned_dyld)
|
|
else:
|
|
env.pop('DYLD_LIBRARY_PATH', None)
|
|
return env
|
|
|
|
def _get_native_steam_service(self):
|
|
"""Get native Steam operations service instance."""
|
|
if self._native_steam_service is None:
|
|
from ..services.native_steam_operations_service import NativeSteamOperationsService
|
|
self._native_steam_service = NativeSteamOperationsService(steamdeck=self.steamdeck)
|
|
return self._native_steam_service
|
|
|
|
def detect_protontricks(self):
|
|
"""Detect if protontricks is installed (native or flatpak). Returns True if found."""
|
|
self.logger.debug("Detecting if protontricks is installed...")
|
|
protontricks_path_which = shutil.which("protontricks")
|
|
self.flatpak_path = shutil.which("flatpak")
|
|
if protontricks_path_which:
|
|
try:
|
|
with open(protontricks_path_which, 'r') as f:
|
|
content = f.read()
|
|
if "flatpak run" in content:
|
|
self.logger.debug(f"Detected Protontricks is a Flatpak wrapper at {protontricks_path_which}")
|
|
self.which_protontricks = 'flatpak'
|
|
else:
|
|
self.logger.info(f"Native Protontricks found at {protontricks_path_which}")
|
|
self.which_protontricks = 'native'
|
|
self.protontricks_path = protontricks_path_which
|
|
return True
|
|
except Exception as e:
|
|
self.logger.error(f"Error reading protontricks executable: {e}")
|
|
try:
|
|
env = self._get_clean_subprocess_env()
|
|
result_user = subprocess.run(
|
|
["flatpak", "list", "--user"],
|
|
capture_output=True, text=True, env=env
|
|
)
|
|
if result_user.returncode == 0 and "com.github.Matoking.protontricks" in result_user.stdout:
|
|
self.logger.info("Flatpak Protontricks is installed (user-level)")
|
|
self.which_protontricks = 'flatpak'
|
|
self.flatpak_install_type = 'user'
|
|
return True
|
|
result_system = subprocess.run(
|
|
["flatpak", "list", "--system"],
|
|
capture_output=True, text=True, env=env
|
|
)
|
|
if result_system.returncode == 0 and "com.github.Matoking.protontricks" in result_system.stdout:
|
|
self.logger.info("Flatpak Protontricks is installed (system-level)")
|
|
self.which_protontricks = 'flatpak'
|
|
self.flatpak_install_type = 'system'
|
|
return True
|
|
except FileNotFoundError:
|
|
self.logger.warning("'flatpak' command not found. Cannot check for Flatpak Protontricks.")
|
|
except Exception as e:
|
|
self.logger.error(f"Unexpected error checking flatpak: {e}")
|
|
self.logger.warning("Protontricks not found (native or flatpak).")
|
|
return False
|
|
|
|
def _get_flatpak_run_args(self) -> List[str]:
|
|
"""Get flatpak run arguments (--user or --system)."""
|
|
base_args = ["flatpak", "run"]
|
|
if self.flatpak_install_type == 'user':
|
|
base_args.append("--user")
|
|
elif self.flatpak_install_type == 'system':
|
|
base_args.append("--system")
|
|
return base_args
|
|
|
|
def _get_flatpak_alias_string(self, command=None) -> str:
|
|
"""Get flatpak alias string for bashrc."""
|
|
flag = f"--{self.flatpak_install_type}" if self.flatpak_install_type else ""
|
|
if command:
|
|
return f"flatpak run {flag} --command={command} com.github.Matoking.protontricks" if flag else f"flatpak run --command={command} com.github.Matoking.protontricks"
|
|
return f"flatpak run {flag} com.github.Matoking.protontricks" if flag else "flatpak run com.github.Matoking.protontricks"
|
|
|
|
def check_protontricks_version(self):
|
|
"""Check if protontricks version is sufficient (>= 1.12). Returns True if OK."""
|
|
try:
|
|
if self.which_protontricks == 'flatpak':
|
|
cmd = self._get_flatpak_run_args() + ["com.github.Matoking.protontricks", "-V"]
|
|
else:
|
|
cmd = ["protontricks", "-V"]
|
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
version_str = result.stdout.split(' ')[1].strip('()')
|
|
cleaned_version = re.sub(r'[^0-9.]', '', version_str)
|
|
self.protontricks_version = cleaned_version
|
|
version_parts = cleaned_version.split('.')
|
|
if len(version_parts) >= 2:
|
|
major, minor = int(version_parts[0]), int(version_parts[1])
|
|
if major < 1 or (major == 1 and minor < 12):
|
|
self.logger.error(f"Protontricks version {cleaned_version} is too old. Version 1.12.0 or newer is required.")
|
|
return False
|
|
return True
|
|
self.logger.error(f"Could not parse protontricks version: {cleaned_version}")
|
|
return False
|
|
except Exception as e:
|
|
self.logger.error(f"Error checking protontricks version: {e}")
|
|
return False
|