diff --git a/src/ptlinux/presentation/game_window.py b/src/ptlinux/presentation/game_window.py index 479ba3a..597c7f8 100644 --- a/src/ptlinux/presentation/game_window.py +++ b/src/ptlinux/presentation/game_window.py @@ -1708,6 +1708,8 @@ def _focus_game_window( focused = _repair_game_window_focus(mapper, window) else: # Host focus may have changed during the dashboard handoff delay. + # The nested monitor owns startup mapping so it can defer that step + # until the host becomes active and handle replacement login clients. focused = _repair_gamescope_game_window_focus(mapper, window) outcome = "focused" if focused else "focus_not_owned" except Exception as exc: @@ -1975,6 +1977,14 @@ def _resolve_gamescope_game_window( except Exception: return None if found is None: + # A named GE startup client can exist before Gamescope gives it + # WM_STATE. Keep following it while waiting for host focus instead of + # permanently excluding it as if it were a destroyed login window. + try: + if mapper.window_state(window) is None and mapper.window_size(window) is not None: + return window + except Exception: + pass return None replacement, title = found _apply_game_window_identity(mapper, replacement) @@ -2256,6 +2266,60 @@ def _maintain_game_window_focus( return int(process.wait()) +def _prepare_gamescope_startup_window( + mapper: _X11, + window: int, + prepared_windows: set[int], + *, + diagnostics: WindowDiagnosticRecorder | None = None, +) -> None: + """Map each new nested client once, deferring while its host is inactive.""" + + if window in prepared_windows: + return + try: + if mapper.window_state(window) is not None: + # A previously managed window losing WM_STATE later is a teardown, + # not permission to map it again. Nested iconify recovery is separate. + prepared_windows.add(window) + return + if not _gamescope_host_focus_is_allowed() or mapper.window_size(window) is None: + return + # Recheck state and host ownership after the X11 geometry round trip. + if mapper.window_state(window) is not None: + prepared_windows.add(window) + return + if not _gamescope_host_focus_is_allowed(): + return + except Exception: + return + + # GE publishes its title before mapping the initial client. Mapping only + # this nested window restores Gamescope's surface without resizing it or + # activating a desktop window. Bound attempts even if the WM never replies. + prepared_windows.add(window) + try: + mapper.map_window(window, borderless=False) + if diagnostics is not None: + diagnostics.record("nested_startup_window_mapped", window_id=window) + deadline = time.monotonic() + WINDOW_STATE_CONFIRM_TIMEOUT_SECONDS + while mapper.window_state(window) is None: + remaining = deadline - time.monotonic() + if remaining <= 0 or not _gamescope_host_focus_is_allowed(): + break + time.sleep(min(WINDOW_CONFIRM_POLL_SECONDS, remaining)) + except Exception as exc: + if diagnostics is not None: + diagnostics.record( + "nested_startup_mapping_failed", window_id=window, + error_type=type(exc).__name__, + ) + print( + f"Warning: could not map the nested startup window ({exc}); continuing normally", + file=sys.stderr, flush=True, + ) + + def _maintain_gamescope_game_window_focus( mapper: _X11, process: subprocess.Popen[bytes], @@ -2269,6 +2333,7 @@ def _maintain_gamescope_game_window_focus( active_window: int = window retired_window_ids = set(existing_window_ids) + prepared_windows: set[int] = set() next_focus_repair_at = ( time.monotonic() + GAMESCOPE_FOCUS_REPAIR_INTERVAL_SECONDS ) @@ -2301,6 +2366,9 @@ def _maintain_gamescope_game_window_focus( if resolved_window_id != active_window: retired_window_ids.add(active_window) active_window = resolved_window_id + _prepare_gamescope_startup_window( + mapper, active_window, prepared_windows, diagnostics=diagnostics, + ) if diagnostics is None: _repair_gamescope_game_window_focus(mapper, active_window) else: diff --git a/updates/stable.json b/updates/stable.json index 94e5f28..56b5c7d 100644 --- a/updates/stable.json +++ b/updates/stable.json @@ -3,15 +3,15 @@ "archive": { "layout": "flat", "name": "PristonTale-EU-1.1.2.tar.gz", - "sha256": "5e2ced0bee8bb82a8731b5e6acfa10abba3b86190a56ff5c58d2c98248f27281", - "size": 1553283, - "url": "https://git.m4ko.net/m4ko/pristontale-eu-linux/releases/download/v1.1.2/PristonTale-EU-1.1.2.tar.gz?sha256=5e2ced0bee8bb82a8731b5e6acfa10abba3b86190a56ff5c58d2c98248f27281" + "sha256": "25c75071274552ced010f410ea7c947bceb929ffa376c9ad0c03a2ed4bf9518b", + "size": 1553917, + "url": "https://git.m4ko.net/m4ko/pristontale-eu-linux/releases/download/v1.1.2/PristonTale-EU-1.1.2.tar.gz?sha256=25c75071274552ced010f410ea7c947bceb929ffa376c9ad0c03a2ed4bf9518b" }, - "archive_sha256": "5e2ced0bee8bb82a8731b5e6acfa10abba3b86190a56ff5c58d2c98248f27281", - "archive_size": 1553283, - "archive_url": "https://git.m4ko.net/m4ko/pristontale-eu-linux/releases/download/v1.1.2/PristonTale-EU-1.1.2.tar.gz?sha256=5e2ced0bee8bb82a8731b5e6acfa10abba3b86190a56ff5c58d2c98248f27281", + "archive_sha256": "25c75071274552ced010f410ea7c947bceb929ffa376c9ad0c03a2ed4bf9518b", + "archive_size": 1553917, + "archive_url": "https://git.m4ko.net/m4ko/pristontale-eu-linux/releases/download/v1.1.2/PristonTale-EU-1.1.2.tar.gz?sha256=25c75071274552ced010f410ea7c947bceb929ffa376c9ad0c03a2ed4bf9518b", "channel": "stable", - "commit": "770bbb8a88ecc0a847649aee1af2e9500951bcd6", + "commit": "531480b195518a27da3c44e59016ca4f232143a8", "compatibility": { "config_schema": 1, "manifest_schema": 1, @@ -406,8 +406,8 @@ }, { "path": "ptlinux/presentation/game_window.py", - "sha256": "a605537bb8b3a052369510fdf7709ac5bfde917be651169470f79b59887ea58f", - "size": 80275 + "sha256": "6d3a50ae513bb051452d1b5be83bb5bca12f20c9a78eef30b469c195f5d59323", + "size": 82777 }, { "path": "ptlinux/presentation/gamescope.py", @@ -706,17 +706,17 @@ }, { "path": "release.json", - "sha256": "f95fab56f77e0dff21b6f0129625de84f0dff413671126921b37afe8f629e181", + "sha256": "b9b5ed456ef4c8b3a9d0615a15576fb2e5b33307a62cf785d567e03804f7e03b", "size": 218 } ], "minimum_updater_version": "0.9.9", "notes_url": "https://git.m4ko.net/m4ko/pristontale-eu-linux/releases/tag/v1.1.2", - "published_at": "2026-10-23T19:34:51Z", + "published_at": "2026-10-23T22:23:50Z", "release": { - "notes_markdown": "# PristonTale EU Launcher 1.1.2\n\n- Improved GE-Proton keyboard focus on X11, including delayed startup and login-window changes.\n- Kept focus recovery active for fixed-size and borderless windows while respecting Alt-Tab and minimization.\n- Prevented replacement-window tracking from attaching to another game account.\n- Added GE-Proton comparison advice for Direct3D resource exhaustion while preserving PTR runtime requirements.\n- Clarified that reported launcher memory and CPU usage exclude the game process.\n\nThe reported Zorin/NVIDIA freeze still needs a gameplay retest with GE-Proton and these focus fixes.\n\n---\n\nVersion: `1.1.2` \nChannel: `stable` \nTag: `v1.1.2` \nSource commit: `770bbb8a88ecc0a847649aee1af2e9500951bcd6`\n", + "notes_markdown": "# PristonTale EU Launcher 1.1.2\n\n- Fixed Gamescope startup windows remaining hidden when GE-Proton publishes the game title before the window is mapped.\n- Retry startup preparation when host focus returns, including replacement login windows, with guards against repeated mapping and focus stealing.\n- Improved GE-Proton keyboard focus on X11, including delayed startup and login-window changes.\n- Kept focus recovery active for fixed-size and borderless windows while respecting Alt-Tab and minimization.\n- Prevented replacement-window tracking from attaching to another game account.\n- Added GE-Proton comparison advice for Direct3D resource exhaustion while preserving PTR runtime requirements.\n- Clarified that reported launcher memory and CPU usage exclude the game process.\n\nThis rebuild retains version 1.1.2. Existing 1.1.2 installations will not receive it automatically through the version-based updater.\n\nThe reported Zorin/NVIDIA freeze and automatic Gamescope shutdown still need a gameplay retest.\n\n---\n\nVersion: `1.1.2` \nChannel: `stable` \nTag: `v1.1.2` \nSource commit: `531480b195518a27da3c44e59016ca4f232143a8`\n", "release_notes_url": "https://git.m4ko.net/m4ko/pristontale-eu-linux/releases/tag/v1.1.2", - "summary": "Improved GE-Proton keyboard focus on X11, including delayed startup and login-window changes.", + "summary": "Fixed Gamescope startup windows remaining hidden when GE-Proton publishes the game title before the window is mapped.", "title": "PristonTale EU Launcher 1.1.2" }, "schema": 1, diff --git a/updates/stable.json.sig b/updates/stable.json.sig index fe64388..c581953 100644 Binary files a/updates/stable.json.sig and b/updates/stable.json.sig differ