Added fallbacks for fsync issues on CachyOS.
This commit is contained in:
@@ -4,4 +4,4 @@ from .paths import AppPaths
|
||||
|
||||
__all__ = ["AppPaths", "__version__"]
|
||||
|
||||
__version__ = "0.5.2"
|
||||
__version__ = "0.5.3"
|
||||
|
||||
@@ -300,6 +300,7 @@ def _login_diagnostics(paths: AppPaths, timeout_seconds: float, as_json: bool) -
|
||||
probe = report.login_server
|
||||
print(f"Launcher: {report.launcher_version} ({report.launcher_module})")
|
||||
print(f"Python: {report.python_executable}")
|
||||
print(f"Host: {report.distribution_id} ({report.kernel_release})")
|
||||
status = "REACHABLE" if probe.reachable else "UNREACHABLE"
|
||||
latency = f", {probe.latency_ms:.1f} ms" if probe.latency_ms is not None else ""
|
||||
print(f"Login server: {status} ({probe.host}:{probe.port}{latency})")
|
||||
@@ -317,6 +318,11 @@ def _login_diagnostics(paths: AppPaths, timeout_seconds: float, as_json: bool) -
|
||||
print(f"Runtime: {report.runtime_kind} ({report.runtime_path or 'unknown path'})")
|
||||
else:
|
||||
print(f"Runtime: unavailable ({report.runtime_error or 'selection failed'})")
|
||||
if report.proton_sync_effective is not None:
|
||||
print(
|
||||
"Proton sync: "
|
||||
f"{report.proton_sync_configured} -> {report.proton_sync_effective}"
|
||||
)
|
||||
prefix_status = "ready" if report.prefix_ready else "not ready"
|
||||
directx_status = "ready" if report.directx_ready else "not ready"
|
||||
print(f"Prefix: {prefix_status}; DirectX helpers {directx_status}")
|
||||
|
||||
@@ -7,6 +7,7 @@ from datetime import datetime
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import platform
|
||||
import shutil
|
||||
import sys
|
||||
from typing import Mapping
|
||||
@@ -25,6 +26,8 @@ WINETRICKS_CODEPAGE_PROBE = (
|
||||
'winetricks_early_wine_arch cmd.exe /c "chcp 65001 > nul & echo %$1%"'
|
||||
)
|
||||
WINETRICKS_PROTON7_PROBE = 'winetricks_early_wine_arch cmd.exe /c "echo %$1%"'
|
||||
RUNTIME_DEFAULT_SYNC = "runtime-default"
|
||||
CACHYOS_MARKER = "cachyos"
|
||||
|
||||
|
||||
class RuntimeErrorPT(RuntimeError):
|
||||
@@ -238,7 +241,11 @@ def run_game(
|
||||
WINDOWS_GAME_EXE,
|
||||
"PTEU_REBORN",
|
||||
]
|
||||
environment = _proton_environment(paths, selection)
|
||||
environment = _proton_environment(
|
||||
paths,
|
||||
selection,
|
||||
synchronization=resolve_proton_sync_mode(settings.proton_sync),
|
||||
)
|
||||
if use_gamebox:
|
||||
game_command = build_gamebox_command(game_command, settings)
|
||||
environment["PYTHONPATH"] = _runtime_python_path(
|
||||
@@ -274,10 +281,12 @@ def _umu_environment(paths: AppPaths) -> dict[str, str]:
|
||||
def _proton_environment(
|
||||
paths: AppPaths,
|
||||
selection: RuntimeSelection,
|
||||
*,
|
||||
synchronization: str = RUNTIME_DEFAULT_SYNC,
|
||||
) -> dict[str, str]:
|
||||
if not selection.steam_root:
|
||||
raise RuntimeErrorPT("Steam root is missing for Proton")
|
||||
return {
|
||||
environment = {
|
||||
"STEAM_COMPAT_CLIENT_INSTALL_PATH": selection.steam_root,
|
||||
"STEAM_COMPAT_DATA_PATH": str(paths.compat_data),
|
||||
"STEAM_COMPAT_INSTALL_PATH": str(paths.runtime_view),
|
||||
@@ -288,6 +297,57 @@ def _proton_environment(
|
||||
"PROTON_LOG": "0",
|
||||
"DXVK_STATE_CACHE_PATH": str(paths.dxvk_cache),
|
||||
}
|
||||
environment.update(_proton_sync_environment(synchronization))
|
||||
return environment
|
||||
|
||||
|
||||
def detect_distribution_id() -> str:
|
||||
"""Return the freedesktop distribution ID without invoking distro tools."""
|
||||
|
||||
try:
|
||||
value = platform.freedesktop_os_release().get("ID", "")
|
||||
except OSError:
|
||||
return ""
|
||||
return value.strip().casefold()
|
||||
|
||||
|
||||
def resolve_proton_sync_mode(
|
||||
configured: str,
|
||||
*,
|
||||
distribution_id: str | None = None,
|
||||
kernel_release: str | None = None,
|
||||
) -> str:
|
||||
"""Resolve the configured mode, applying the narrow CachyOS compatibility fallback."""
|
||||
|
||||
if configured not in {"auto", "fsync", "esync"}:
|
||||
raise ValueError(f"unsupported Proton synchronization mode: {configured}")
|
||||
if configured != "auto":
|
||||
return configured
|
||||
distribution = detect_distribution_id() if distribution_id is None else distribution_id
|
||||
kernel = platform.release() if kernel_release is None else kernel_release
|
||||
if CACHYOS_MARKER in distribution.casefold() or CACHYOS_MARKER in kernel.casefold():
|
||||
return "esync"
|
||||
return RUNTIME_DEFAULT_SYNC
|
||||
|
||||
|
||||
def _proton_sync_environment(synchronization: str) -> dict[str, str]:
|
||||
if synchronization == RUNTIME_DEFAULT_SYNC:
|
||||
return {}
|
||||
if synchronization == "esync":
|
||||
return {
|
||||
"PROTON_NO_FSYNC": "1",
|
||||
"WINEFSYNC": "0",
|
||||
"PROTON_NO_ESYNC": "0",
|
||||
"WINEESYNC": "1",
|
||||
}
|
||||
if synchronization == "fsync":
|
||||
return {
|
||||
"PROTON_NO_FSYNC": "0",
|
||||
"WINEFSYNC": "1",
|
||||
"PROTON_NO_ESYNC": "0",
|
||||
"WINEESYNC": "1",
|
||||
}
|
||||
raise ValueError(f"unsupported resolved Proton synchronization mode: {synchronization}")
|
||||
|
||||
|
||||
def _ensure_proton_game_link(paths: AppPaths) -> None:
|
||||
|
||||
@@ -22,6 +22,7 @@ class Settings:
|
||||
scaler: str = "fit"
|
||||
filter: str = "linear"
|
||||
runtime: str = "auto"
|
||||
proton_sync: str = "auto"
|
||||
proton_path: str | None = None
|
||||
game_source: str | None = None
|
||||
|
||||
@@ -50,6 +51,7 @@ def load_settings(paths: AppPaths) -> Settings:
|
||||
scaler=_string(value, "scaler", "fit"),
|
||||
filter=_string(value, "filter", "linear"),
|
||||
runtime=_string(value, "runtime", "auto"),
|
||||
proton_sync=_string(value, "proton_sync", "auto"),
|
||||
proton_path=_optional_string(value, "proton_path"),
|
||||
game_source=_optional_string(value, "game_source"),
|
||||
)
|
||||
@@ -95,6 +97,8 @@ def _validate(settings: Settings) -> None:
|
||||
raise ValueError(f"unsupported Gamescope filter: {settings.filter}")
|
||||
if settings.runtime not in {"auto", "proton7", "umu"}:
|
||||
raise ValueError(f"unsupported runtime selection: {settings.runtime}")
|
||||
if settings.proton_sync not in {"auto", "fsync", "esync"}:
|
||||
raise ValueError(f"unsupported Proton synchronization mode: {settings.proton_sync}")
|
||||
|
||||
|
||||
def _integer(value: dict[str, object], key: str, default: int) -> int:
|
||||
|
||||
@@ -1371,6 +1371,7 @@ def run_dashboard(paths: AppPaths) -> None:
|
||||
scaler_value = tk.StringVar(value=current_settings.scaler)
|
||||
filter_value = tk.StringVar(value=current_settings.filter)
|
||||
runtime_value = tk.StringVar(value=current_settings.runtime)
|
||||
proton_sync_value = tk.StringVar(value=current_settings.proton_sync)
|
||||
proton_path_value = tk.StringVar(value=current_settings.proton_path or "")
|
||||
game_source_value = tk.StringVar(value=current_settings.game_source or "")
|
||||
|
||||
@@ -1511,6 +1512,7 @@ def run_dashboard(paths: AppPaths) -> None:
|
||||
scaler=scaler_value.get(),
|
||||
filter=filter_value.get(),
|
||||
runtime=runtime_value.get(),
|
||||
proton_sync=proton_sync_value.get(),
|
||||
proton_path=proton_path_value.get().strip() or None,
|
||||
game_source=game_source_value.get().strip() or None,
|
||||
)
|
||||
@@ -1637,6 +1639,14 @@ def run_dashboard(paths: AppPaths) -> None:
|
||||
runtime_value,
|
||||
("auto", "proton7", "umu"),
|
||||
)
|
||||
add_choice(
|
||||
rendering_card,
|
||||
8,
|
||||
"Proton synchronization",
|
||||
"Auto uses esync on CachyOS",
|
||||
proton_sync_value,
|
||||
("auto", "fsync", "esync"),
|
||||
)
|
||||
|
||||
overrides_card = ttk.LabelFrame(
|
||||
content,
|
||||
|
||||
Reference in New Issue
Block a user