""" Wine wrapper script generation for winetricks. Creates wrapper scripts similar to protontricks to properly set up LD_LIBRARY_PATH and other environment variables before invoking wine/wineserver. """ import os import stat import shutil import logging from pathlib import Path from typing import Optional from jackify.shared.paths import get_jackify_data_dir logger = logging.getLogger(__name__) WINE_WRAPPER_TEMPLATE = '''#!/bin/bash # Wine wrapper script generated by Jackify # Ensures proper LD_LIBRARY_PATH setup when calling Proton wine binaries PROTON_DIST_PATH="@@PROTON_DIST_PATH@@" BINARY_NAME="@@BINARY_NAME@@" # Set up LD_LIBRARY_PATH with Proton libraries first PROTON_LIB_PATH="${PROTON_DIST_PATH}/lib64:${PROTON_DIST_PATH}/lib" if [[ -n "$LD_LIBRARY_PATH" ]]; then export LD_LIBRARY_PATH="${PROTON_LIB_PATH}:${LD_LIBRARY_PATH}" else export LD_LIBRARY_PATH="${PROTON_LIB_PATH}" fi # Enable fsync/esync by default if not already set if [[ -z "$WINEFSYNC" && -z "$PROTON_NO_FSYNC" ]]; then export WINEFSYNC=1 fi if [[ -z "$WINEESYNC" && -z "$PROTON_NO_ESYNC" ]]; then export WINEESYNC=1 fi # Execute the actual Proton binary exec "${PROTON_DIST_PATH}/bin/${BINARY_NAME}" "$@" ''' class WineWrapperManager: """Manages creation of wine/wineserver wrapper scripts for winetricks.""" def __init__(self): self.logger = logging.getLogger(__name__) self._wrapper_dir: Optional[Path] = None def get_wrapper_dir(self, proton_path: str) -> Path: """Get or create the wrapper directory for a specific Proton version.""" proton_name = Path(proton_path).name.replace(" ", "_") cache_dir = get_jackify_data_dir() / "wine_wrappers" / proton_name cache_dir.mkdir(parents=True, exist_ok=True) return cache_dir def create_wrappers(self, proton_dist_path: str) -> Optional[Path]: """ Create wine and wineserver wrapper scripts for the given Proton dist path. Args: proton_dist_path: Path to Proton's dist directory (containing bin/, lib/, lib64/) Returns: Path to the wrapper directory, or None if creation failed """ try: proton_dist = Path(proton_dist_path) if not proton_dist.exists(): self.logger.error(f"Proton dist path does not exist: {proton_dist_path}") return None # Verify required binaries exist wine_bin = proton_dist / "bin" / "wine" wineserver_bin = proton_dist / "bin" / "wineserver" if not wine_bin.exists(): self.logger.error(f"Wine binary not found: {wine_bin}") return None if not wineserver_bin.exists(): self.logger.error(f"Wineserver binary not found: {wineserver_bin}") return None # Get wrapper directory based on Proton install path (parent of dist) proton_install_path = proton_dist.parent wrapper_dir = self.get_wrapper_dir(str(proton_install_path)) # Clean and recreate to ensure fresh scripts if wrapper_dir.exists(): shutil.rmtree(str(wrapper_dir)) wrapper_dir.mkdir(parents=True, exist_ok=True) # Create wrapper for each binary in Proton's bin directory binaries_to_wrap = ["wine", "wine64", "wineserver", "wineboot", "winecfg"] created_wrappers = [] for binary_name in binaries_to_wrap: binary_path = proton_dist / "bin" / binary_name if not binary_path.exists(): continue wrapper_path = wrapper_dir / binary_name wrapper_content = WINE_WRAPPER_TEMPLATE.replace( "@@PROTON_DIST_PATH@@", str(proton_dist) ).replace( "@@BINARY_NAME@@", binary_name ) wrapper_path.write_text(wrapper_content) wrapper_path.chmod(wrapper_path.stat().st_mode | stat.S_IEXEC) created_wrappers.append(binary_name) self.logger.info(f"Created wine wrappers in {wrapper_dir}: {', '.join(created_wrappers)}") self._wrapper_dir = wrapper_dir return wrapper_dir except Exception as e: self.logger.error(f"Failed to create wine wrappers: {e}", exc_info=True) return None def get_wine_wrapper_path(self, proton_dist_path: str) -> Optional[str]: """Get path to the wine wrapper script.""" wrapper_dir = self.create_wrappers(proton_dist_path) if wrapper_dir: wine_wrapper = wrapper_dir / "wine" if wine_wrapper.exists(): return str(wine_wrapper) return None def get_wineserver_wrapper_path(self, proton_dist_path: str) -> Optional[str]: """Get path to the wineserver wrapper script.""" wrapper_dir = self.create_wrappers(proton_dist_path) if wrapper_dir: wineserver_wrapper = wrapper_dir / "wineserver" if wineserver_wrapper.exists(): return str(wineserver_wrapper) return None