Reworked some umu / proton 7 maximize window and keyboard issues.
This commit is contained in:
@@ -1095,6 +1095,7 @@ def _run_and_watch(
|
|||||||
detected_at_ns: int | None = None
|
detected_at_ns: int | None = None
|
||||||
timed_out = False
|
timed_out = False
|
||||||
stop_reason = "process-exited"
|
stop_reason = "process-exited"
|
||||||
|
failure_reason: str | None = None
|
||||||
with runner_log_path.open("a", encoding="utf-8") as runner_log:
|
with runner_log_path.open("a", encoding="utf-8") as runner_log:
|
||||||
try:
|
try:
|
||||||
process = subprocess.Popen(
|
process = subprocess.Popen(
|
||||||
@@ -1151,18 +1152,26 @@ def _run_and_watch(
|
|||||||
current_state = state
|
current_state = state
|
||||||
current_analysis = analysis.to_summary()
|
current_analysis = analysis.to_summary()
|
||||||
current_text = read_current_run_log_text(state, baseline)
|
current_text = read_current_run_log_text(state, baseline)
|
||||||
if analysis.load_texture_to_material_failed:
|
if (
|
||||||
|
analysis.load_texture_to_material_failed
|
||||||
|
or analysis.asset_pack_health_failed
|
||||||
|
):
|
||||||
failure_state = state
|
failure_state = state
|
||||||
failure_trigger_mtime_ns = state.mtime_ns
|
failure_trigger_mtime_ns = state.mtime_ns
|
||||||
detected_at_ns = now_ns
|
detected_at_ns = now_ns
|
||||||
post_failure_deadline_ns = now_ns + int(
|
post_failure_deadline_ns = now_ns + int(
|
||||||
testcase.stop_after_failure_seconds * 1_000_000_000
|
testcase.stop_after_failure_seconds * 1_000_000_000
|
||||||
)
|
)
|
||||||
|
failure_reason = (
|
||||||
|
"asset-pack-health"
|
||||||
|
if analysis.asset_pack_health_failed
|
||||||
|
else "load-texture-to-material"
|
||||||
|
)
|
||||||
break
|
break
|
||||||
if post_failure_deadline_ns is not None and now_ns >= post_failure_deadline_ns:
|
if post_failure_deadline_ns is not None and now_ns >= post_failure_deadline_ns:
|
||||||
stop_reason = "load-texture-to-material"
|
stop_reason = failure_reason or "load-texture-to-material"
|
||||||
_stop_process_group(process)
|
_stop_process_group(process)
|
||||||
break
|
break
|
||||||
if now_ns >= deadline_ns:
|
if now_ns >= deadline_ns:
|
||||||
timed_out = True
|
timed_out = True
|
||||||
stop_reason = "timeout"
|
stop_reason = "timeout"
|
||||||
@@ -1216,6 +1225,9 @@ def _run_and_watch(
|
|||||||
"load_texture_to_material_failed": analysis.get(
|
"load_texture_to_material_failed": analysis.get(
|
||||||
"load_texture_to_material_failed", False
|
"load_texture_to_material_failed", False
|
||||||
),
|
),
|
||||||
|
"asset_pack_health_failed": analysis.get(
|
||||||
|
"asset_pack_health_failed", False
|
||||||
|
),
|
||||||
"import_scene_threw": analysis.get("import_scene_threw", False),
|
"import_scene_threw": analysis.get("import_scene_threw", False),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -1244,6 +1256,7 @@ def _run_and_watch(
|
|||||||
whispering_vale
|
whispering_vale
|
||||||
and not timed_out
|
and not timed_out
|
||||||
and not current_analysis.get("load_texture_to_material_failed", False)
|
and not current_analysis.get("load_texture_to_material_failed", False)
|
||||||
|
and not current_analysis.get("asset_pack_health_failed", False)
|
||||||
and not current_analysis.get("import_scene_threw", False)
|
and not current_analysis.get("import_scene_threw", False)
|
||||||
and not current_analysis.get("failed_import_scenes", [])
|
and not current_analysis.get("failed_import_scenes", [])
|
||||||
) if isinstance(current_analysis, Mapping) else False
|
) if isinstance(current_analysis, Mapping) else False
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from typing import Iterable, Mapping
|
|||||||
|
|
||||||
|
|
||||||
DEFAULT_NATIVE_LOG_PATTERNS = ("log_*.txt", "*.log")
|
DEFAULT_NATIVE_LOG_PATTERNS = ("log_*.txt", "*.log")
|
||||||
|
_ASSET_MISSING_FILE_THRESHOLD = 200
|
||||||
|
|
||||||
_SCENE_RE = re.compile(
|
_SCENE_RE = re.compile(
|
||||||
r"\[S1]\s+after import\s+(?P<scene>[^:]+):\s*"
|
r"\[S1]\s+after import\s+(?P<scene>[^:]+):\s*"
|
||||||
@@ -72,6 +73,12 @@ class NativeLogAnalysis:
|
|||||||
scenes: dict[str, list[SceneImport]] = field(default_factory=dict)
|
scenes: dict[str, list[SceneImport]] = field(default_factory=dict)
|
||||||
events: list[LogEvent] = field(default_factory=list)
|
events: list[LogEvent] = field(default_factory=list)
|
||||||
failed_files: list[LogEvent] = field(default_factory=list)
|
failed_files: list[LogEvent] = field(default_factory=list)
|
||||||
|
failed_file_find_count: int = 0
|
||||||
|
failed_file_load_count: int = 0
|
||||||
|
failed_file_read_count: int = 0
|
||||||
|
failed_asset_file_count: int = 0
|
||||||
|
failed_texture_file_count: int = 0
|
||||||
|
failed_effect_file_count: int = 0
|
||||||
failed_import_scenes: list[str] = field(default_factory=list)
|
failed_import_scenes: list[str] = field(default_factory=list)
|
||||||
load_texture_to_material_failed: bool = False
|
load_texture_to_material_failed: bool = False
|
||||||
import_scene_threw: bool = False
|
import_scene_threw: bool = False
|
||||||
@@ -83,6 +90,14 @@ class NativeLogAnalysis:
|
|||||||
module_code_sync_delta: int | None = None
|
module_code_sync_delta: int | None = None
|
||||||
banned_code_1: bool = False
|
banned_code_1: bool = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def asset_pack_health_failed(self) -> bool:
|
||||||
|
return (
|
||||||
|
self.failed_asset_file_count >= _ASSET_MISSING_FILE_THRESHOLD
|
||||||
|
or self.failed_texture_file_count >= _ASSET_MISSING_FILE_THRESHOLD // 3
|
||||||
|
or self.failed_effect_file_count >= max(25, _ASSET_MISSING_FILE_THRESHOLD // 8)
|
||||||
|
)
|
||||||
|
|
||||||
def latest_scene(self, name: str) -> SceneImport | None:
|
def latest_scene(self, name: str) -> SceneImport | None:
|
||||||
wanted = _scene_key(name)
|
wanted = _scene_key(name)
|
||||||
for scene_name, imports in self.scenes.items():
|
for scene_name, imports in self.scenes.items():
|
||||||
@@ -98,6 +113,13 @@ class NativeLogAnalysis:
|
|||||||
},
|
},
|
||||||
"load_texture_to_material_failed": self.load_texture_to_material_failed,
|
"load_texture_to_material_failed": self.load_texture_to_material_failed,
|
||||||
"import_scene_threw": self.import_scene_threw,
|
"import_scene_threw": self.import_scene_threw,
|
||||||
|
"failed_file_find_count": self.failed_file_find_count,
|
||||||
|
"failed_file_load_count": self.failed_file_load_count,
|
||||||
|
"failed_file_read_count": self.failed_file_read_count,
|
||||||
|
"failed_asset_file_count": self.failed_asset_file_count,
|
||||||
|
"failed_texture_file_count": self.failed_texture_file_count,
|
||||||
|
"failed_effect_file_count": self.failed_effect_file_count,
|
||||||
|
"asset_pack_health_failed": self.asset_pack_health_failed,
|
||||||
"failed_import_scenes": self.failed_import_scenes,
|
"failed_import_scenes": self.failed_import_scenes,
|
||||||
"failed_files": [event.to_dict() for event in self.failed_files],
|
"failed_files": [event.to_dict() for event in self.failed_files],
|
||||||
"stage_without_new_stage": self.stage_without_new_stage,
|
"stage_without_new_stage": self.stage_without_new_stage,
|
||||||
@@ -291,6 +313,18 @@ def parse_native_log_lines(lines: Iterable[str]) -> NativeLogAnalysis:
|
|||||||
event = LogEvent(f"failed_{operation}_file", line_number, raw, timestamp, value)
|
event = LogEvent(f"failed_{operation}_file", line_number, raw, timestamp, value)
|
||||||
analysis.failed_files.append(event)
|
analysis.failed_files.append(event)
|
||||||
analysis.events.append(event)
|
analysis.events.append(event)
|
||||||
|
if operation == "find":
|
||||||
|
analysis.failed_file_find_count += 1
|
||||||
|
elif operation == "load":
|
||||||
|
analysis.failed_file_load_count += 1
|
||||||
|
else:
|
||||||
|
analysis.failed_file_read_count += 1
|
||||||
|
if _is_game_asset_path(value):
|
||||||
|
analysis.failed_asset_file_count += 1
|
||||||
|
if _is_texture_path(value):
|
||||||
|
analysis.failed_texture_file_count += 1
|
||||||
|
if _is_effect_path(value):
|
||||||
|
analysis.failed_effect_file_count += 1
|
||||||
|
|
||||||
if "Stage without new stage" in raw:
|
if "Stage without new stage" in raw:
|
||||||
analysis.stage_without_new_stage = True
|
analysis.stage_without_new_stage = True
|
||||||
@@ -319,3 +353,61 @@ def _scene_key(value: str) -> str:
|
|||||||
def _timestamp(line: str) -> str | None:
|
def _timestamp(line: str) -> str | None:
|
||||||
match = _TIMESTAMP_RE.match(line)
|
match = _TIMESTAMP_RE.match(line)
|
||||||
return match.group("timestamp") if match is not None else None
|
return match.group("timestamp") if match is not None else None
|
||||||
|
|
||||||
|
|
||||||
|
def _is_game_asset_path(path: str) -> bool:
|
||||||
|
lower = path.casefold()
|
||||||
|
if not lower:
|
||||||
|
return False
|
||||||
|
if lower.startswith("save/") or lower.startswith("save\\"):
|
||||||
|
return False
|
||||||
|
if lower.startswith(("game\\", "game/")):
|
||||||
|
return True
|
||||||
|
if lower.startswith(("field\\", "field/")):
|
||||||
|
return True
|
||||||
|
if lower.startswith(("image\\", "image/")):
|
||||||
|
return True
|
||||||
|
if lower.startswith(("effect\\", "effect/")):
|
||||||
|
return True
|
||||||
|
if lower.startswith(("startimage\\", "startimage/")):
|
||||||
|
return True
|
||||||
|
return (
|
||||||
|
"\\effect\\" in lower
|
||||||
|
or "\\image\\" in lower
|
||||||
|
or "\\field\\" in lower
|
||||||
|
or "\\weapons\\" in lower
|
||||||
|
or "\\char\\" in lower
|
||||||
|
or "\\sinimage\\" in lower
|
||||||
|
or "\\skill\\" in lower
|
||||||
|
or lower.endswith(".bmp")
|
||||||
|
or lower.endswith(".tga")
|
||||||
|
or lower.endswith(".png")
|
||||||
|
or lower.endswith(".jpg")
|
||||||
|
or lower.endswith(".jpeg")
|
||||||
|
or lower.endswith(".gif")
|
||||||
|
or lower.endswith(".ase")
|
||||||
|
or lower.endswith(".part")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_texture_path(path: str) -> bool:
|
||||||
|
lower = path.casefold()
|
||||||
|
return (
|
||||||
|
lower.endswith(".bmp")
|
||||||
|
or lower.endswith(".tga")
|
||||||
|
or lower.endswith(".png")
|
||||||
|
or lower.endswith(".jpg")
|
||||||
|
or lower.endswith(".jpeg")
|
||||||
|
or lower.endswith(".gif")
|
||||||
|
or lower.endswith(".dds")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_effect_path(path: str) -> bool:
|
||||||
|
lower = path.casefold()
|
||||||
|
return (
|
||||||
|
"\\effect\\" in lower
|
||||||
|
or lower.startswith("effect\\")
|
||||||
|
or lower.startswith("effect/")
|
||||||
|
or lower.endswith(".part")
|
||||||
|
)
|
||||||
|
|||||||
@@ -1046,6 +1046,7 @@ def _current_game_log(
|
|||||||
"size": state.size,
|
"size": state.size,
|
||||||
"load_texture_to_material_failed": analysis.load_texture_to_material_failed,
|
"load_texture_to_material_failed": analysis.load_texture_to_material_failed,
|
||||||
"import_scene_threw": analysis.import_scene_threw,
|
"import_scene_threw": analysis.import_scene_threw,
|
||||||
|
"asset_pack_health_failed": analysis.asset_pack_health_failed,
|
||||||
"failed_import_scenes": analysis.failed_import_scenes,
|
"failed_import_scenes": analysis.failed_import_scenes,
|
||||||
"stage_without_new_stage": analysis.stage_without_new_stage,
|
"stage_without_new_stage": analysis.stage_without_new_stage,
|
||||||
"module_code_sync_error": analysis.module_code_sync_error,
|
"module_code_sync_error": analysis.module_code_sync_error,
|
||||||
@@ -1058,6 +1059,12 @@ def _current_game_log(
|
|||||||
"native-game-log-only; server-provided login-screen text is not "
|
"native-game-log-only; server-provided login-screen text is not "
|
||||||
"written to this log"
|
"written to this log"
|
||||||
),
|
),
|
||||||
|
"failed_file_find_count": analysis.failed_file_find_count,
|
||||||
|
"failed_file_load_count": analysis.failed_file_load_count,
|
||||||
|
"failed_file_read_count": analysis.failed_file_read_count,
|
||||||
|
"failed_asset_file_count": analysis.failed_asset_file_count,
|
||||||
|
"failed_texture_file_count": analysis.failed_texture_file_count,
|
||||||
|
"failed_effect_file_count": analysis.failed_effect_file_count,
|
||||||
"stage_without_new_stage_events": [
|
"stage_without_new_stage_events": [
|
||||||
event.to_dict()
|
event.to_dict()
|
||||||
for event in analysis.events
|
for event in analysis.events
|
||||||
@@ -1162,6 +1169,17 @@ def _print_interrupted_game_log(report: dict[str, object]) -> None:
|
|||||||
"with publisher-pristine code in this launcher version.",
|
"with publisher-pristine code in this launcher version.",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
|
elif report["asset_pack_health_failed"]:
|
||||||
|
print(
|
||||||
|
"Detected a large number of missing game-asset files. This indicates a "
|
||||||
|
"corrupted or incomplete installation that needs repair.",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"Run `pristontale-eu update --repair` and restart. If the issue "
|
||||||
|
"reappears, re-download the game files with a clean install path.",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
elif report["load_texture_to_material_failed"]:
|
elif report["load_texture_to_material_failed"]:
|
||||||
print(
|
print(
|
||||||
"Detected a texture/material failure while loading the game world.",
|
"Detected a texture/material failure while loading the game world.",
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ _X_SUBSTRUCTURE_REDIRECT_MASK = 1 << 20
|
|||||||
_X_WM_STATE_ICONIC = 3
|
_X_WM_STATE_ICONIC = 3
|
||||||
_NET_WM_STATE_REMOVE = 0
|
_NET_WM_STATE_REMOVE = 0
|
||||||
_NET_WM_STATE_SOURCE_APPLICATION = 1
|
_NET_WM_STATE_SOURCE_APPLICATION = 1
|
||||||
|
WINDOW_FOCUS_REPAIR_INTERVAL_SECONDS = 1.0
|
||||||
_P_MIN_SIZE = 1 << 4
|
_P_MIN_SIZE = 1 << 4
|
||||||
_P_MAX_SIZE = 1 << 5
|
_P_MAX_SIZE = 1 << 5
|
||||||
_MOTIF_HINTS_FUNCTIONS = 1 << 0
|
_MOTIF_HINTS_FUNCTIONS = 1 << 0
|
||||||
@@ -466,7 +467,20 @@ class _X11:
|
|||||||
and updated_normal_hints != normal_hints
|
and updated_normal_hints != normal_hints
|
||||||
)
|
)
|
||||||
motif_changed = motif_hints is not None and updated_motif_hints != motif_hints
|
motif_changed = motif_hints is not None and updated_motif_hints != motif_hints
|
||||||
if not normal_changed and not motif_changed:
|
allowed_actions = self._long_property(window, b"_NET_WM_ALLOWED_ACTIONS", 64)
|
||||||
|
allowed_actions_with_scaling = self._allowed_actions_with_scaling(
|
||||||
|
allowed_actions,
|
||||||
|
)
|
||||||
|
allowed_actions_changed = (
|
||||||
|
allowed_actions is not None
|
||||||
|
and allowed_actions_with_scaling is not None
|
||||||
|
and allowed_actions_with_scaling != allowed_actions
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
not normal_changed
|
||||||
|
and not motif_changed
|
||||||
|
and not allowed_actions_changed
|
||||||
|
):
|
||||||
return not confirm or self.window_allows_scaling(window)
|
return not confirm or self.window_allows_scaling(window)
|
||||||
|
|
||||||
confirmed = False
|
confirmed = False
|
||||||
@@ -485,6 +499,13 @@ class _X11:
|
|||||||
b"_MOTIF_WM_HINTS",
|
b"_MOTIF_WM_HINTS",
|
||||||
updated_motif_hints,
|
updated_motif_hints,
|
||||||
)
|
)
|
||||||
|
if allowed_actions_changed and allowed_actions_with_scaling is not None:
|
||||||
|
self._replace_long_property(
|
||||||
|
window,
|
||||||
|
b"_NET_WM_ALLOWED_ACTIONS",
|
||||||
|
b"_ATOM",
|
||||||
|
allowed_actions_with_scaling,
|
||||||
|
)
|
||||||
if not confirm:
|
if not confirm:
|
||||||
self.library.XFlush(self.display)
|
self.library.XFlush(self.display)
|
||||||
confirmed = True
|
confirmed = True
|
||||||
@@ -515,6 +536,16 @@ class _X11:
|
|||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
restore_errors.append(exc)
|
restore_errors.append(exc)
|
||||||
|
if allowed_actions_changed and allowed_actions is not None:
|
||||||
|
try:
|
||||||
|
self._replace_long_property(
|
||||||
|
window,
|
||||||
|
b"_NET_WM_ALLOWED_ACTIONS",
|
||||||
|
b"_ATOM",
|
||||||
|
allowed_actions,
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
restore_errors.append(exc)
|
||||||
try:
|
try:
|
||||||
self.library.XSync(self.display, 0)
|
self.library.XSync(self.display, 0)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
@@ -546,6 +577,36 @@ class _X11:
|
|||||||
vertical,
|
vertical,
|
||||||
}.issubset(actions)
|
}.issubset(actions)
|
||||||
|
|
||||||
|
def _allowed_actions_with_scaling(
|
||||||
|
self,
|
||||||
|
actions: tuple[int, ...] | None,
|
||||||
|
) -> tuple[int, ...] | None:
|
||||||
|
if actions is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
resize = self._intern_atom(
|
||||||
|
b"_NET_WM_ACTION_RESIZE",
|
||||||
|
only_if_exists=True,
|
||||||
|
)
|
||||||
|
horizontal = self._intern_atom(
|
||||||
|
b"_NET_WM_ACTION_MAXIMIZE_HORZ",
|
||||||
|
only_if_exists=True,
|
||||||
|
)
|
||||||
|
vertical = self._intern_atom(
|
||||||
|
b"_NET_WM_ACTION_MAXIMIZE_VERT",
|
||||||
|
only_if_exists=True,
|
||||||
|
)
|
||||||
|
if resize == 0 or horizontal == 0 or vertical == 0:
|
||||||
|
return actions
|
||||||
|
|
||||||
|
updated_actions = list(actions)
|
||||||
|
known_actions = set(actions)
|
||||||
|
for atom in (resize, horizontal, vertical):
|
||||||
|
if atom not in known_actions:
|
||||||
|
updated_actions.append(atom)
|
||||||
|
known_actions.add(atom)
|
||||||
|
return tuple(updated_actions)
|
||||||
|
|
||||||
def _wait_for_scaling_actions(self, window: int) -> bool:
|
def _wait_for_scaling_actions(self, window: int) -> bool:
|
||||||
deadline = time.monotonic() + WINDOW_ACTION_CONFIRM_TIMEOUT_SECONDS
|
deadline = time.monotonic() + WINDOW_ACTION_CONFIRM_TIMEOUT_SECONDS
|
||||||
while not self.window_allows_scaling(window):
|
while not self.window_allows_scaling(window):
|
||||||
@@ -595,6 +656,7 @@ class _X11:
|
|||||||
watched_atoms = {
|
watched_atoms = {
|
||||||
self._intern_atom(b"WM_NORMAL_HINTS", only_if_exists=False),
|
self._intern_atom(b"WM_NORMAL_HINTS", only_if_exists=False),
|
||||||
self._intern_atom(b"_MOTIF_WM_HINTS", only_if_exists=False),
|
self._intern_atom(b"_MOTIF_WM_HINTS", only_if_exists=False),
|
||||||
|
self._intern_atom(b"_NET_WM_ALLOWED_ACTIONS", only_if_exists=False),
|
||||||
}
|
}
|
||||||
changed = False
|
changed = False
|
||||||
events_read = 0
|
events_read = 0
|
||||||
@@ -831,6 +893,17 @@ class _X11:
|
|||||||
)
|
)
|
||||||
self.library.XSync(self.display, 0)
|
self.library.XSync(self.display, 0)
|
||||||
|
|
||||||
|
def window_is_focused(self, window: int) -> bool:
|
||||||
|
focused = ctypes.c_ulong()
|
||||||
|
revert_to = ctypes.c_int()
|
||||||
|
if self.library.XGetInputFocus(
|
||||||
|
self.display,
|
||||||
|
ctypes.byref(focused),
|
||||||
|
ctypes.byref(revert_to),
|
||||||
|
) != 1:
|
||||||
|
return False
|
||||||
|
return int(focused.value) == window
|
||||||
|
|
||||||
def activate_window(self, window: int) -> bool:
|
def activate_window(self, window: int) -> bool:
|
||||||
"""Ask the window manager to restore and activate one client."""
|
"""Ask the window manager to restore and activate one client."""
|
||||||
|
|
||||||
@@ -1111,6 +1184,12 @@ class _X11:
|
|||||||
ctypes.c_ulong,
|
ctypes.c_ulong,
|
||||||
]
|
]
|
||||||
self.library.XSetInputFocus.restype = ctypes.c_int
|
self.library.XSetInputFocus.restype = ctypes.c_int
|
||||||
|
self.library.XGetInputFocus.argtypes = [
|
||||||
|
ctypes.c_void_p,
|
||||||
|
ctypes.POINTER(ctypes.c_ulong),
|
||||||
|
ctypes.POINTER(ctypes.c_int),
|
||||||
|
]
|
||||||
|
self.library.XGetInputFocus.restype = ctypes.c_int
|
||||||
self.library.XSendEvent.argtypes = [
|
self.library.XSendEvent.argtypes = [
|
||||||
ctypes.c_void_p,
|
ctypes.c_void_p,
|
||||||
ctypes.c_ulong,
|
ctypes.c_ulong,
|
||||||
@@ -1250,6 +1329,27 @@ def _focus_game_window(mapper: _X11, window: int) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _repair_game_window_focus(mapper: _X11, window: int) -> bool:
|
||||||
|
try:
|
||||||
|
if mapper.window_is_focused(window):
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
mapper.activate_window(window)
|
||||||
|
time.sleep(WINDOW_ACTIVATION_DELAY_SECONDS)
|
||||||
|
mapper.focus_window(window)
|
||||||
|
return mapper.window_is_focused(window)
|
||||||
|
except Exception as exc:
|
||||||
|
print(
|
||||||
|
f"Warning: could not repair game-window focus ({exc}); continuing "
|
||||||
|
"normally",
|
||||||
|
file=sys.stderr,
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _enable_game_window_scaling(mapper: _X11, window: int) -> bool:
|
def _enable_game_window_scaling(mapper: _X11, window: int) -> bool:
|
||||||
"""Enable confirmed resize/maximize actions without risking game startup."""
|
"""Enable confirmed resize/maximize actions without risking game startup."""
|
||||||
|
|
||||||
@@ -1301,7 +1401,8 @@ def _maintain_game_window_scaling(
|
|||||||
return int(process.wait())
|
return int(process.wait())
|
||||||
|
|
||||||
repair_failures = 0
|
repair_failures = 0
|
||||||
next_repair_at = 0.0
|
next_scaling_repair_at = 0.0
|
||||||
|
next_focus_repair_at = time.monotonic() + WINDOW_FOCUS_REPAIR_INTERVAL_SECONDS
|
||||||
while process.poll() is None:
|
while process.poll() is None:
|
||||||
try:
|
try:
|
||||||
hints_changed = mapper.wait_for_scaling_hint_change(window)
|
hints_changed = mapper.wait_for_scaling_hint_change(window)
|
||||||
@@ -1313,13 +1414,17 @@ def _maintain_game_window_scaling(
|
|||||||
flush=True,
|
flush=True,
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
if not hints_changed:
|
|
||||||
continue
|
|
||||||
now = time.monotonic()
|
now = time.monotonic()
|
||||||
if now < next_repair_at:
|
if now >= next_focus_repair_at:
|
||||||
time.sleep(next_repair_at - now)
|
_repair_game_window_focus(mapper, window)
|
||||||
|
next_focus_repair_at = time.monotonic() + WINDOW_FOCUS_REPAIR_INTERVAL_SECONDS
|
||||||
|
if not hints_changed and now < next_scaling_repair_at:
|
||||||
|
continue
|
||||||
try:
|
try:
|
||||||
mapper.enable_window_scaling(window, confirm=False)
|
mapper.enable_window_scaling(
|
||||||
|
window,
|
||||||
|
confirm=False,
|
||||||
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
repair_failures += 1
|
repair_failures += 1
|
||||||
print(
|
print(
|
||||||
@@ -1337,7 +1442,7 @@ def _maintain_game_window_scaling(
|
|||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
repair_failures = 0
|
repair_failures = 0
|
||||||
next_repair_at = time.monotonic() + WINDOW_SCALING_REPAIR_INTERVAL_SECONDS
|
next_scaling_repair_at = time.monotonic() + WINDOW_SCALING_REPAIR_INTERVAL_SECONDS
|
||||||
return int(process.wait())
|
return int(process.wait())
|
||||||
|
|
||||||
|
|
||||||
@@ -1517,7 +1622,7 @@ def run_game_window(
|
|||||||
game_window,
|
game_window,
|
||||||
borderless=borderless,
|
borderless=borderless,
|
||||||
)
|
)
|
||||||
if startup_stable and not borderless:
|
if not borderless:
|
||||||
return _maintain_game_window_scaling(
|
return _maintain_game_window_scaling(
|
||||||
mapper,
|
mapper,
|
||||||
process,
|
process,
|
||||||
|
|||||||
Reference in New Issue
Block a user