fix(control): a launched game gets the supervisor's hidden console, not a console window of its own #589

Merged
HumanBean merged 1 commit from llun/Longshot:fix/ctl-hidden-console into main 2026-11-14 22:52:18 +00:00
Member

Type: fix · Risk: low · Tested on: Windows 11, RTX 5080 · Needs: none

Why

On Windows, every game that longshot_ctl.py launch starts as a console program opens a console window of its own, which flashes on the desktop for about a second. That covers a development build of the game, a --wrapper, and the tests' stand-in game. It came in with #47 (durable command-line sessions), which was tested on Linux only, where the same call opens no windows. The full suite now does it dozens of times: tests/test_longshot_ctl.py alone opened 28 console windows in 30 seconds here, so anyone running tests/run_all.py on Windows sees windows flashing up while it runs. See #47.

How to reproduce

Seen on: Windows 11 (build 26300), RTX 5080, driver 621.14; Windows Terminal is the default console
Settings and switches: defaults
Steps: run python -I -B tests/run_all.py test_longshot_ctl on main, and watch the desktop
Expected: no windows
Actual: a Windows Terminal window opens and closes for each stand-in game the test launches (28 in about 30 s)

What changed

The launcher's supervisor now starts with a console of its own that has no window (CREATE_NO_WINDOW), in place of no console at all (DETACHED_PROCESS), and the game it starts inherits that console. The supervisor is still independent of the caller's console and keeps its own process group, so the run still outlives the command that started it, as #47 intends.

  • control: start_session in scripts/longshot_ctl.py: the supervisor's creationflags change from CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS to CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW. Linux keeps start_new_session.
  • tests: tests/test_longshot_ctl.py's stand-in game records whether it has a console, and whether that console is a window on the desktop. On Windows, Sessions.test_profile_manifest_and_second_client expects a console with no window: [True, False] (on main it is [True, True]).

A release build is a windowed (GUI) program, and Windows gives a GUI program no console of its own, so a release build's launch is unchanged. That includes the console it opens on purpose while it prepares shaders, so a long wait isn't mistaken for a hang.

Measurements

Verdict: it fixes the windows: 28 console windows during test_longshot_ctl on main, none with this PR, and none in the whole suite. Nothing to time: the change is in how the launcher starts a process, not in the game.

Hardware: Windows 11 Pro 26300, RTX 5080 16 GB, driver 621.14, Ryzen 9 9900X3D, 64 GB
Scene: none (game-free tests); one headless launch of a release build, to the intro
Method: a small watcher listed every new visible console window (ConsoleWindowClass, Windows Terminal's CASCADIA_HOSTING_WINDOW_CLASS and PseudoConsoleWindow) with its owning process and command line, every 10 ms, while the tests ran
How to reproduce: python -I -B tests/run_all.py test_longshot_ctl on each commit, watching the desktop (or any window-listing tool)

Before: main @ 5d3ecc78 After: this PR @ e2dd39ce
New console windows during test_longshot_ctl 28 0
New console windows during the full suite (the 28 above, among others) 0
The stand-in game's console [has one, shown] [True, True] [True, False]
before (main), one of the 28:
16:16:44 PseudoConsoleWindow owner=python.exe  cmd='python.exe <temp>\ctl-session-…\fake.py --user <temp>\ctl-session-…\runs\run-…\user'
after (this PR):
ctl test 16:23:12 … suite 16:23:40 … done 16:25:04: no windows

Flags and settings

None

Testing

Tested:

  • Windows 11, RTX 5080, driver 621.14: python -I -B tests/run_all.py: 487 passed, 12 failed, 77 skipped. All 12 also fail on main on this PC: test_fpx_sticky, test_appimage_player_build, test_bake_portability, test_cpm_discovery, test_lift_cleanup_error, test_make_game_cache, test_linux_preflight, test_lift_cache, test_oot_state_tap, test_spawn_emit, test_tested_configurations, test_walk_key. test_longshot_ctl passes.
  • The new check fails on main ([True, True], "the game opened a console window") and passes here.
  • In the game: a release build (nightly-127, source 8672daee) launched through this PR's longshot_ctl.py launch --profile test --headless --ready present on the RTX 5080. It reached the intro (frame 30, 1920x1080), status answered, stop ended it (finished, exit 0, quit, process exited). The only console window was the game's own shader-preparation console, as on main. The options file beside the game was unchanged.

Not tested: a development build (a console program) launched through the launcher in the game; the stand-in game covers the same path. Linux and macOS: this changes only the Windows branch.

Must be tested: None

Risks

What could break: a launched game now has a console with no window, where before it had none. A development build's AllocConsole for shader-preparation progress now finds that console, so it shows nothing; before, the progress went to the build's own visible console window. Its output was already going to the run's log.txt. A release build is unchanged. Stopping the game is unchanged (terminate / kill).

Turning it off: revert the commit

Compatibility: Nothing changes for players. Only the command-line launcher is affected.

Checklist

Everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description) of the status line's type.
  • Why gives the reason plainly and links the issues that fit (the tracker searched).
  • Measurements has a verdict, or one line saying why there is nothing to measure.
  • Testing names what was tested, what wasn't, and what must still be tested, where and how.
  • Rebased on current main.
  • python -I -B tests/run_all.py passes (any failure that also fails on main is named).
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

When it applies

  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • A game-free test added for the fix, failing on main and passing here.
  • A check under tests/ that reads changed source updated in this PR, not weakened.

🤖 Generated with Claude Code

**Type:** fix · **Risk:** low · **Tested on:** Windows 11, RTX 5080 · **Needs:** none ## Why On Windows, every game that `longshot_ctl.py launch` starts as a console program opens a console window of its own, which flashes on the desktop for about a second. That covers a development build of the game, a `--wrapper`, and the tests' stand-in game. It came in with #47 (durable command-line sessions), which was tested on Linux only, where the same call opens no windows. The full suite now does it dozens of times: `tests/test_longshot_ctl.py` alone opened 28 console windows in 30 seconds here, so anyone running `tests/run_all.py` on Windows sees windows flashing up while it runs. See #47. ## How to reproduce **Seen on:** Windows 11 (build 26300), RTX 5080, driver 621.14; Windows Terminal is the default console **Settings and switches:** defaults **Steps:** run `python -I -B tests/run_all.py test_longshot_ctl` on `main`, and watch the desktop **Expected:** no windows **Actual:** a Windows Terminal window opens and closes for each stand-in game the test launches (28 in about 30 s) ## What changed The launcher's supervisor now starts with a console of its own that has no window (`CREATE_NO_WINDOW`), in place of no console at all (`DETACHED_PROCESS`), and the game it starts inherits that console. The supervisor is still independent of the caller's console and keeps its own process group, so the run still outlives the command that started it, as #47 intends. - control: `start_session` in `scripts/longshot_ctl.py`: the supervisor's `creationflags` change from `CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS` to `CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW`. Linux keeps `start_new_session`. - tests: `tests/test_longshot_ctl.py`'s stand-in game records whether it has a console, and whether that console is a window on the desktop. On Windows, `Sessions.test_profile_manifest_and_second_client` expects a console with no window: `[True, False]` (on `main` it is `[True, True]`). A release build is a windowed (GUI) program, and Windows gives a GUI program no console of its own, so a release build's launch is unchanged. That includes the console it opens on purpose while it prepares shaders, so a long wait isn't mistaken for a hang. ## Measurements **Verdict:** it fixes the windows: 28 console windows during `test_longshot_ctl` on `main`, none with this PR, and none in the whole suite. Nothing to time: the change is in how the launcher starts a process, not in the game. Hardware: Windows 11 Pro 26300, RTX 5080 16 GB, driver 621.14, Ryzen 9 9900X3D, 64 GB Scene: none (game-free tests); one headless launch of a release build, to the intro Method: a small watcher listed every new visible console window (`ConsoleWindowClass`, Windows Terminal's `CASCADIA_HOSTING_WINDOW_CLASS` and `PseudoConsoleWindow`) with its owning process and command line, every 10 ms, while the tests ran How to reproduce: `python -I -B tests/run_all.py test_longshot_ctl` on each commit, watching the desktop (or any window-listing tool) | | Before: main @ 5d3ecc78 | After: this PR @ e2dd39ce | |---|---|---| | New console windows during `test_longshot_ctl` | 28 | 0 | | New console windows during the full suite | (the 28 above, among others) | 0 | | The stand-in game's console `[has one, shown]` | `[True, True]` | `[True, False]` | ``` before (main), one of the 28: 16:16:44 PseudoConsoleWindow owner=python.exe cmd='python.exe <temp>\ctl-session-…\fake.py --user <temp>\ctl-session-…\runs\run-…\user' after (this PR): ctl test 16:23:12 … suite 16:23:40 … done 16:25:04: no windows ``` ### Flags and settings None ## Testing **Tested:** - Windows 11, RTX 5080, driver 621.14: `python -I -B tests/run_all.py`: 487 passed, 12 failed, 77 skipped. All 12 also fail on `main` on this PC: test_fpx_sticky, test_appimage_player_build, test_bake_portability, test_cpm_discovery, test_lift_cleanup_error, test_make_game_cache, test_linux_preflight, test_lift_cache, test_oot_state_tap, test_spawn_emit, test_tested_configurations, test_walk_key. `test_longshot_ctl` passes. - The new check fails on `main` (`[True, True]`, "the game opened a console window") and passes here. - In the game: a release build (nightly-127, source 8672daee) launched through this PR's `longshot_ctl.py launch --profile test --headless --ready present` on the RTX 5080. It reached the intro (frame 30, 1920x1080), `status` answered, `stop` ended it (`finished`, exit 0, `quit`, process exited). The only console window was the game's own shader-preparation console, as on `main`. The options file beside the game was unchanged. **Not tested:** a development build (a console program) launched through the launcher in the game; the stand-in game covers the same path. Linux and macOS: this changes only the Windows branch. **Must be tested:** None ## Risks **What could break:** a launched game now has a console with no window, where before it had none. A development build's `AllocConsole` for shader-preparation progress now finds that console, so it shows nothing; before, the progress went to the build's own visible console window. Its output was already going to the run's `log.txt`. A release build is unchanged. Stopping the game is unchanged (`terminate` / `kill`). **Turning it off:** revert the commit **Compatibility:** Nothing changes for players. Only the command-line launcher is affected. ## Checklist ### Everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`) of the status line's type. - [x] Why gives the reason plainly and links the issues that fit (the tracker searched). - [x] Measurements has a verdict, or one line saying why there is nothing to measure. - [x] Testing names what was tested, what wasn't, and what must still be tested, where and how. - [x] Rebased on current `main`. - [x] `python -I -B tests/run_all.py` passes (any failure that also fails on `main` is named). - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### When it applies - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [x] A game-free test added for the fix, failing on `main` and passing here. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(control): a launched game gets the supervisor's hidden console, not a console window of its own
Some checks are pending
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
beanstalk/approval awaiting maintainer approval
CI / Conventional Commit title (pull_request) Successful in 14s
CI / Workflow lint (actionlint) (pull_request) Successful in 20s
Third-party parts / As their notes say (pull_request) Successful in 28s
PR description / Description follows the template (pull_request_target) Successful in 14s
e2dd39cebe
On Windows, launch started its supervisor with DETACHED_PROCESS, so the supervisor had no console at all, and each
console program it started (a development build of the game, a wrapper, the tests' stand-in game) got a new console
window: test_longshot_ctl.py alone opened 28 in 30 seconds. The supervisor now starts with CREATE_NO_WINDOW: a
console of its own, apart from the caller's, with no window, which the game inherits. A release build (a windowed
program) is unchanged, its shader-preparation console included. test_longshot_ctl.py's stand-in records whether it
has a console and whether that console is shown.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
llun changed title from WIP: fix(control): a launched game gets the supervisor's hidden console, not a console window of its own to fix(control): a launched game gets the supervisor's hidden console, not a console window of its own 2026-11-14 21:28:07 +00:00
llun scheduled this pull request to auto merge when all checks succeed 2026-11-14 21:32:49 +00:00
llun force-pushed fix/ctl-hidden-console from e2dd39cebe
Some checks are pending
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
beanstalk/approval awaiting maintainer approval
CI / Conventional Commit title (pull_request) Successful in 14s
CI / Workflow lint (actionlint) (pull_request) Successful in 20s
Third-party parts / As their notes say (pull_request) Successful in 28s
PR description / Description follows the template (pull_request_target) Successful in 14s
to 763d8cd4d8
All checks were successful
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 4s
PR description / Description follows the template (pull_request_target) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 6s
Third-party parts / As their notes say (pull_request) Successful in 9s
2026-11-14 22:22:49 +00:00
Compare
HumanBean deleted branch fix/ctl-hidden-console 2026-11-14 22:52:19 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!589
No description provided.