Fixed issues with gamescope not properly launching.

This commit is contained in:
2026-09-06 22:57:54 +02:00
parent f53f4d0b08
commit bea9d6fda7
9 changed files with 252 additions and 18 deletions

View File

@@ -43,6 +43,8 @@ _X_PROPERTY_NOTIFY = 28
_X_PROPERTY_CHANGE_MASK = 1 << 22
_X_SUBSTRUCTURE_NOTIFY_MASK = 1 << 19
_X_SUBSTRUCTURE_REDIRECT_MASK = 1 << 20
_X_WM_STATE_WITHDRAWN = 0
_X_WM_STATE_NORMAL = 1
_X_WM_STATE_ICONIC = 3
_NET_WM_STATE_REMOVE = 0
_NET_WM_STATE_SOURCE_APPLICATION = 1
@@ -448,6 +450,18 @@ class _X11:
return None
return values[0]
def restore_normal_window_state(self, window: int) -> bool:
"""Undo a nested iconify request without raising the host window."""
self._replace_long_property(
window,
b"WM_STATE",
b"WM_STATE",
(_X_WM_STATE_NORMAL, 0),
)
self.library.XSync(self.display, 0)
return self.window_state(window) == _X_WM_STATE_NORMAL
def window_is_borderless(self, window: int) -> bool | None:
"""Return whether the client currently requests no Motif decorations."""
@@ -1463,6 +1477,54 @@ def _repair_gamescope_game_window_focus(mapper: _X11, window: int) -> bool:
return False
def _resolve_gamescope_game_window(
mapper: _X11,
window: int,
*,
window_title: str,
excluded_window_ids: Collection[int],
) -> int | None:
"""Return a valid nested client across Wine's login-window transition."""
try:
state = mapper.window_state(window)
except Exception:
state = None
if state == _X_WM_STATE_ICONIC:
try:
if not mapper.restore_normal_window_state(window):
return None
except Exception:
return None
print(
"Game login window requested minimization; kept it visible inside "
"Gamescope.",
file=sys.stderr,
flush=True,
)
return window
if state is not None and state != _X_WM_STATE_WITHDRAWN:
return window
try:
found = mapper.find_first_window(
window_title,
excluded_window_ids=set(excluded_window_ids) | {window},
)
except Exception:
return None
if found is None:
return None
replacement, title = found
_apply_game_window_identity(mapper, replacement)
print(
f"Game login window changed; following it inside Gamescope: {title}",
file=sys.stderr,
flush=True,
)
return replacement
def _enable_game_window_scaling(mapper: _X11, window: int) -> bool:
"""Enable confirmed resize/maximize actions without risking game startup."""
@@ -1587,9 +1649,14 @@ def _maintain_gamescope_game_window_focus(
mapper: _X11,
process: subprocess.Popen[bytes],
window: int,
*,
window_title: str = WINDOW_TITLE,
existing_window_ids: Collection[int] = (),
) -> int:
"""Restore nested keyboard focus after host resize/maximize operations."""
"""Keep a valid, focused nested client through login and host resizing."""
active_window: int = window
retired_window_ids = set(existing_window_ids)
next_focus_repair_at = (
time.monotonic() + GAMESCOPE_FOCUS_REPAIR_INTERVAL_SECONDS
)
@@ -1598,7 +1665,22 @@ def _maintain_gamescope_game_window_focus(
now = time.monotonic()
if now < next_focus_repair_at:
continue
_repair_gamescope_game_window_focus(mapper, window)
resolved_window = _resolve_gamescope_game_window(
mapper,
active_window,
window_title=window_title,
excluded_window_ids=retired_window_ids,
)
if resolved_window is None:
next_focus_repair_at = (
time.monotonic() + GAMESCOPE_FOCUS_REPAIR_INTERVAL_SECONDS
)
continue
resolved_window_id = resolved_window
if resolved_window_id != active_window:
retired_window_ids.add(active_window)
active_window = resolved_window_id
_repair_gamescope_game_window_focus(mapper, active_window)
next_focus_repair_at = (
time.monotonic() + GAMESCOPE_FOCUS_REPAIR_INTERVAL_SECONDS
)
@@ -1785,6 +1867,8 @@ def run_game_window(
mapper,
process,
game_window,
window_title=window_title,
existing_window_ids=existing_window_ids,
)
_prepare_game_window(
mapper,