Added fallbacks for fsync issues on CachyOS.
This commit is contained in:
@@ -4,4 +4,4 @@ from .paths import AppPaths
|
|||||||
|
|
||||||
__all__ = ["AppPaths", "__version__"]
|
__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
|
probe = report.login_server
|
||||||
print(f"Launcher: {report.launcher_version} ({report.launcher_module})")
|
print(f"Launcher: {report.launcher_version} ({report.launcher_module})")
|
||||||
print(f"Python: {report.python_executable}")
|
print(f"Python: {report.python_executable}")
|
||||||
|
print(f"Host: {report.distribution_id} ({report.kernel_release})")
|
||||||
status = "REACHABLE" if probe.reachable else "UNREACHABLE"
|
status = "REACHABLE" if probe.reachable else "UNREACHABLE"
|
||||||
latency = f", {probe.latency_ms:.1f} ms" if probe.latency_ms is not None else ""
|
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})")
|
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'})")
|
print(f"Runtime: {report.runtime_kind} ({report.runtime_path or 'unknown path'})")
|
||||||
else:
|
else:
|
||||||
print(f"Runtime: unavailable ({report.runtime_error or 'selection failed'})")
|
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"
|
prefix_status = "ready" if report.prefix_ready else "not ready"
|
||||||
directx_status = "ready" if report.directx_ready else "not ready"
|
directx_status = "ready" if report.directx_ready else "not ready"
|
||||||
print(f"Prefix: {prefix_status}; DirectX helpers {directx_status}")
|
print(f"Prefix: {prefix_status}; DirectX helpers {directx_status}")
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from datetime import datetime
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import platform
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
from typing import Mapping
|
from typing import Mapping
|
||||||
@@ -25,6 +26,8 @@ WINETRICKS_CODEPAGE_PROBE = (
|
|||||||
'winetricks_early_wine_arch cmd.exe /c "chcp 65001 > nul & echo %$1%"'
|
'winetricks_early_wine_arch cmd.exe /c "chcp 65001 > nul & echo %$1%"'
|
||||||
)
|
)
|
||||||
WINETRICKS_PROTON7_PROBE = 'winetricks_early_wine_arch cmd.exe /c "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):
|
class RuntimeErrorPT(RuntimeError):
|
||||||
@@ -238,7 +241,11 @@ def run_game(
|
|||||||
WINDOWS_GAME_EXE,
|
WINDOWS_GAME_EXE,
|
||||||
"PTEU_REBORN",
|
"PTEU_REBORN",
|
||||||
]
|
]
|
||||||
environment = _proton_environment(paths, selection)
|
environment = _proton_environment(
|
||||||
|
paths,
|
||||||
|
selection,
|
||||||
|
synchronization=resolve_proton_sync_mode(settings.proton_sync),
|
||||||
|
)
|
||||||
if use_gamebox:
|
if use_gamebox:
|
||||||
game_command = build_gamebox_command(game_command, settings)
|
game_command = build_gamebox_command(game_command, settings)
|
||||||
environment["PYTHONPATH"] = _runtime_python_path(
|
environment["PYTHONPATH"] = _runtime_python_path(
|
||||||
@@ -274,10 +281,12 @@ def _umu_environment(paths: AppPaths) -> dict[str, str]:
|
|||||||
def _proton_environment(
|
def _proton_environment(
|
||||||
paths: AppPaths,
|
paths: AppPaths,
|
||||||
selection: RuntimeSelection,
|
selection: RuntimeSelection,
|
||||||
|
*,
|
||||||
|
synchronization: str = RUNTIME_DEFAULT_SYNC,
|
||||||
) -> dict[str, str]:
|
) -> dict[str, str]:
|
||||||
if not selection.steam_root:
|
if not selection.steam_root:
|
||||||
raise RuntimeErrorPT("Steam root is missing for Proton")
|
raise RuntimeErrorPT("Steam root is missing for Proton")
|
||||||
return {
|
environment = {
|
||||||
"STEAM_COMPAT_CLIENT_INSTALL_PATH": selection.steam_root,
|
"STEAM_COMPAT_CLIENT_INSTALL_PATH": selection.steam_root,
|
||||||
"STEAM_COMPAT_DATA_PATH": str(paths.compat_data),
|
"STEAM_COMPAT_DATA_PATH": str(paths.compat_data),
|
||||||
"STEAM_COMPAT_INSTALL_PATH": str(paths.runtime_view),
|
"STEAM_COMPAT_INSTALL_PATH": str(paths.runtime_view),
|
||||||
@@ -288,6 +297,57 @@ def _proton_environment(
|
|||||||
"PROTON_LOG": "0",
|
"PROTON_LOG": "0",
|
||||||
"DXVK_STATE_CACHE_PATH": str(paths.dxvk_cache),
|
"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:
|
def _ensure_proton_game_link(paths: AppPaths) -> None:
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class Settings:
|
|||||||
scaler: str = "fit"
|
scaler: str = "fit"
|
||||||
filter: str = "linear"
|
filter: str = "linear"
|
||||||
runtime: str = "auto"
|
runtime: str = "auto"
|
||||||
|
proton_sync: str = "auto"
|
||||||
proton_path: str | None = None
|
proton_path: str | None = None
|
||||||
game_source: str | None = None
|
game_source: str | None = None
|
||||||
|
|
||||||
@@ -50,6 +51,7 @@ def load_settings(paths: AppPaths) -> Settings:
|
|||||||
scaler=_string(value, "scaler", "fit"),
|
scaler=_string(value, "scaler", "fit"),
|
||||||
filter=_string(value, "filter", "linear"),
|
filter=_string(value, "filter", "linear"),
|
||||||
runtime=_string(value, "runtime", "auto"),
|
runtime=_string(value, "runtime", "auto"),
|
||||||
|
proton_sync=_string(value, "proton_sync", "auto"),
|
||||||
proton_path=_optional_string(value, "proton_path"),
|
proton_path=_optional_string(value, "proton_path"),
|
||||||
game_source=_optional_string(value, "game_source"),
|
game_source=_optional_string(value, "game_source"),
|
||||||
)
|
)
|
||||||
@@ -95,6 +97,8 @@ def _validate(settings: Settings) -> None:
|
|||||||
raise ValueError(f"unsupported Gamescope filter: {settings.filter}")
|
raise ValueError(f"unsupported Gamescope filter: {settings.filter}")
|
||||||
if settings.runtime not in {"auto", "proton7", "umu"}:
|
if settings.runtime not in {"auto", "proton7", "umu"}:
|
||||||
raise ValueError(f"unsupported runtime selection: {settings.runtime}")
|
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:
|
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)
|
scaler_value = tk.StringVar(value=current_settings.scaler)
|
||||||
filter_value = tk.StringVar(value=current_settings.filter)
|
filter_value = tk.StringVar(value=current_settings.filter)
|
||||||
runtime_value = tk.StringVar(value=current_settings.runtime)
|
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 "")
|
proton_path_value = tk.StringVar(value=current_settings.proton_path or "")
|
||||||
game_source_value = tk.StringVar(value=current_settings.game_source 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(),
|
scaler=scaler_value.get(),
|
||||||
filter=filter_value.get(),
|
filter=filter_value.get(),
|
||||||
runtime=runtime_value.get(),
|
runtime=runtime_value.get(),
|
||||||
|
proton_sync=proton_sync_value.get(),
|
||||||
proton_path=proton_path_value.get().strip() or None,
|
proton_path=proton_path_value.get().strip() or None,
|
||||||
game_source=game_source_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,
|
runtime_value,
|
||||||
("auto", "proton7", "umu"),
|
("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(
|
overrides_card = ttk.LabelFrame(
|
||||||
content,
|
content,
|
||||||
|
|||||||
Reference in New Issue
Block a user