The couch tiles stop minting a console window #411

Merged
enricobuehler merged 1 commits from worktree-win-msix-console-flash into main 2026-08-27 11:34:20 +00:00
Owner

Field report: launching the "Punktfunk Console" entry on Windows shows a terminal window before the couch UI appears.

Root cause

punktfunk-session.exe is deliberately console-subsystem — its stdout IS the shell↔session contract. Both couch entry points are GUI-subsystem processes with no console to lend it, so a spawn without CREATE_NO_WINDOW makes Windows mint a fresh one: a black terminal that then sits behind the couch UI for the whole session.

The shell's own stream spawns (clients/windows/src/spawn.rs) have carried the flag since the Windows port. The two --browse hand-offs never did:

  • punktfunk-console.exe — the "Punktfunk Console" MSIX tile (AppxManifest.xml) and the Inno installer's {userprograms}\Punktfunk Console shortcut
  • punktfunk-client --console — the Steam-shortcut / HTPC-startup-item path

Both are shipping today.

The second bug, in the same spawn

🛑 CREATE_NO_WINDOW on its own turns the cosmetic bug into a crash. stdout was inherited. With a console that was merely invisible output; with the flag, the inherited handle is invalid — and match_window (clients/session/src/main.rs:501) println!s the settled window size regardless of --json-status, so a launched title would panic the child mid-stream on the first report. stdout is now an explicit Stdio::null(), not an inheritance.

The shape of the fix

The two hand-offs were copy-paste twins, which is precisely how they drifted apart on one flag — so they become one file, clients/windows/src/couch.rs, that both roots include: the standalone punktfunk-console.exe bin by #[path], the shell as a module. session_binary() folds in there too, so there is one resolver rather than two. Net −41 lines.

Verification

  • couch.rs type-checks and passes clippy -D warnings -W pedantic against x86_64-pc-windows-msvc, via a scratch crate that pulls the file in over a stubbed parent module — the crate itself cannot be cross-checked from macOS (SDL3, aws-lc-rs do not cross-build).
  • cargo check -p punktfunk-client-windows (the non-Windows stub build) is clean.
  • rustfmt --check clean on all four touched files.

On-glass verification on Windows is owed — please confirm the Console tile comes up with no terminal behind it, and that launching a title from the couch library still survives the match_window report.

Two notes for the reviewer

  1. The plain "Punktfunk" tile was reported as flashing a console too, and this PR does not explain that one. Audited: main()AttachConsole (attaches to an existing parent console, never allocates; a no-op under MSIX) → identity → deeplink → windows_reactor::bootstrap() (a no-op under package identity) → SDL in-process → WinUI. No child process is spawned on that path at all. The console window's title bar names the process that owns it; that string is what the next round needs.
  2. Same trap, still latent: crates/pf-paths/src/lib.rs spawns icacls.exe at four sites with no creation flags. punktfunk-tray is GUI-subsystem and depends on pf-paths — it currently only calls config_dir()/published_mgmt_port() so it dodges them, but any tray call to create_private_dir/write_secret_file would pop 2–4 console windows. Left out of this PR deliberately: not reachable today, and not the reported bug.
Field report: launching the **"Punktfunk Console"** entry on Windows shows a terminal window before the couch UI appears. ## Root cause `punktfunk-session.exe` is deliberately **console-subsystem** — its stdout IS the shell↔session contract. Both couch entry points are GUI-subsystem processes with no console to lend it, so a spawn without `CREATE_NO_WINDOW` makes Windows mint a fresh one: a black terminal that then sits behind the couch UI for the whole session. The shell's own stream spawns (`clients/windows/src/spawn.rs`) have carried the flag since the Windows port. The two `--browse` hand-offs never did: - `punktfunk-console.exe` — the **"Punktfunk Console" MSIX tile** (`AppxManifest.xml`) *and* the Inno installer's `{userprograms}\Punktfunk Console` shortcut - `punktfunk-client --console` — the Steam-shortcut / HTPC-startup-item path Both are shipping today. ## The second bug, in the same spawn 🛑 **`CREATE_NO_WINDOW` on its own turns the cosmetic bug into a crash.** stdout was inherited. With a console that was merely invisible output; with the flag, the inherited handle is **invalid** — and `match_window` (`clients/session/src/main.rs:501`) `println!`s the settled window size **regardless of `--json-status`**, so a launched title would panic the child mid-stream on the first report. stdout is now an explicit `Stdio::null()`, not an inheritance. ## The shape of the fix The two hand-offs were copy-paste twins, which is precisely how they drifted apart on one flag — so they become **one file**, `clients/windows/src/couch.rs`, that both roots include: the standalone `punktfunk-console.exe` bin by `#[path]`, the shell as a module. `session_binary()` folds in there too, so there is one resolver rather than two. Net −41 lines. ## Verification - `couch.rs` type-checks **and** passes `clippy -D warnings -W pedantic` against `x86_64-pc-windows-msvc`, via a scratch crate that pulls the file in over a stubbed parent module — the crate itself cannot be cross-checked from macOS (SDL3, aws-lc-rs do not cross-build). - `cargo check -p punktfunk-client-windows` (the non-Windows stub build) is clean. - `rustfmt --check` clean on all four touched files. ⏳ **On-glass verification on Windows is owed** — please confirm the Console tile comes up with no terminal behind it, and that launching a title from the couch library still survives the `match_window` report. ## Two notes for the reviewer 1. The **plain "Punktfunk" tile was reported as flashing a console too, and this PR does not explain that one.** Audited: `main()` → `AttachConsole` (attaches to an existing parent console, never allocates; a no-op under MSIX) → identity → deeplink → `windows_reactor::bootstrap()` (a no-op under package identity) → SDL in-process → WinUI. No child process is spawned on that path at all. The console window's title bar names the process that owns it; that string is what the next round needs. 2. **Same trap, still latent:** `crates/pf-paths/src/lib.rs` spawns `icacls.exe` at four sites with no creation flags. `punktfunk-tray` is GUI-subsystem and depends on pf-paths — it currently only calls `config_dir()`/`published_mgmt_port()` so it dodges them, but any tray call to `create_private_dir`/`write_secret_file` would pop 2–4 console windows. Left out of this PR deliberately: not reachable today, and not the reported bug.
enricobuehler added 1 commit 2026-08-27 11:05:53 +00:00
fix(clients/windows): the couch tiles stop minting a console window
ci / rust-arm64 (pull_request) Successful in 2m37s
ci / web (pull_request) Successful in 1m49s
ci / docs-site (pull_request) Successful in 1m22s
ci / bun-nix (pull_request) Successful in 1m12s
ci / docs-drift (pull_request) Successful in 34s
ci / rust (pull_request) Successful in 8m59s
windows-client / client (x64, , x86_64-pc-windows-msvc, C:\t) (pull_request) Successful in 6m15s
windows-client / client (arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (pull_request) Successful in 2m39s
de19a98f1b
`punktfunk-session` keeps the CONSOLE subsystem on purpose — its stdout is the
shell/session contract. Both couch entry points are GUI processes with no console
to lend it, so a spawn without CREATE_NO_WINDOW makes Windows mint one: a black
terminal that then sits behind the couch UI for the whole session. The shell's
own stream spawns (spawn.rs) have carried the flag since the port; the two
`--browse` hand-offs never did — the "Punktfunk Console" MSIX tile and
`punktfunk-client --console` (Steam shortcut, HTPC startup item) hit it every launch.

The two hand-offs were copy-paste twins, which is how they drifted apart on one
flag, so they become one file (couch.rs) that both roots include — the standalone
`punktfunk-console.exe` bin by `#[path]`, the shell as a module. `session_binary()`
folds in there too, so there is one resolver rather than two.

Second, quieter bug in the same spawn: stdout was inherited. With a console that
was merely invisible output; with CREATE_NO_WINDOW the inherited handle is invalid,
and `match_window` reports the settled window size on stdout REGARDLESS of
`--json-status` — a `println!` onto a dead handle panics the child mid-stream. So
stdout is an explicit `Stdio::null()`, not an inheritance.

Verified: couch.rs type-checks and clippies clean against x86_64-pc-windows-msvc in
an isolated harness (the crate itself cannot be cross-checked from macOS — SDL3 and
aws-lc-rs do not cross-build). On-glass verification on Windows is owed.
enricobuehler merged commit 0b473cdb38 into main 2026-08-27 11:34:20 +00:00
enricobuehler deleted branch worktree-win-msix-console-flash 2026-08-27 11:34:26 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unom/punktfunk#411