design/midstream-resolution-resize.md Phase 0 + Phase 1.
Host (Phase 0):
- H1/H5: per-backend Reconfigure acceptance gate — reject for gamescope
(all sub-modes; a resize must never relaunch the title) and under the
per-client-mode identity policy (a resize would resolve a different
display slot). Synthetic stays reconfigurable on purpose (the protocol
test source; the C-ABI roundtrip test rides it). Plus a 500 ms host-side
min-interval backstop against Reconfigure spam.
- H2: rollback/corrective ack — the data plane reports the mode actually
live after a failed rebuild (or a refresh the backend capped) through a
reconfig_result channel; the control task forwards it as a second
accepted Reconfigured so the client's mode slot self-corrects.
- H3: live stats mode — SendStats reads a packed AtomicU64 (w|h|hz)
updated on every switch instead of latching the session-start mode.
- H4: registry::retire(gen) — a mode-switch rebuild force-releases the
superseded Linux display, so linger/forever keep-alive policies don't
accumulate kept monitors at stale modes. VirtualOutput carries pool_gen
(fresh AND reused) and the Pipeline tuple threads it to the switch arm.
Client (Phase 1, default off):
- Settings: match_window policy + persisted last window size; exposed as
the Resolution tri-state (Native / Match window / explicit) in the Skia
console, GTK and WinUI settings pages.
- pf-presenter: window opens at the persisted size; Hello mode follows the
window's pixel size; D2 trigger discipline (400 ms debounce to
resize-end, ≥1 s spacing, even-floor + ≥320×200 clamp, each distinct
size requested at most once — covers rejects and host rollbacks) as a
pure, unit-tested decision; HUD line + title refresh on a switch.
- Session binary wires both --connect and --browse paths; the WinUI shell
is session-always, so this covers Windows too.
Verified: workspace tests + clippy green; synthetic --remode end-to-end;
live session-binary run (window at persisted 1000×600 → Hello 1000×600@60).
On-glass per-backend matrix (Mutter/KWin/gamescope-reject, keep-alive
accumulation) still pending before any default flip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ship the Android client's 3-tier stats-overlay semantics in every other client
(design/stats-unification.md vocabulary): Off → Compact (one line: fps · e2e ms ·
Mb/s + loss flag) → Normal (mode + e2e p50/p95 + loss counters) → Detailed
(decoder path, HDR tag, per-stage latency equation).
Apple: new StatsVerbosity in PunktfunkKit persisted under punktfunk.statsVerbosity
(migrates the legacy hudEnabled bool: explicit off → Off, else Normal). The
existing three-finger tap (TouchMouse, trackpad/pointer modes only — touch
passthrough untouched) now cycles the tiers instead of toggling, matching
Android; ⌃⌥⇧S (menu + captured-state monitor) cycles the same ladder. Tiered
StreamHUDView (compact glass pill / headline HUD / full equation HUD); the iOS
corner disconnect also shows in Compact (the pill carries no button). Tier
pickers on iOS, macOS, tvOS and the gamepad settings UI.
Session stack (Linux + Windows + Deck share punktfunk-session): shared
pf_client_core::trust::StatsVerbosity; Settings grows stats_verbosity with a
show_stats fallback, and writes keep the legacy bool in sync so pre-tier
binaries reading the same JSON agree on off vs on. Ctrl+Alt+Shift+S cycles the
tier and re-renders the OSD immediately from the last stats window; the stdout
stats: line always carries the full Detailed text so the shell status card and
scripts keep a stable shape; --stats bumps Off → Normal without demoting a
richer tier. Tier pickers in the GTK dialog, the WinUI settings page and the
console-UI settings row; shortcut copy updated (GTK shortcuts window, Windows
help, session README). The Windows legacy builtin path keeps its bool HUD.
Tests: tier migration/round-trip in trust.rs, tiered stats_text output in
pf-presenter.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Skia console UI grows from the single library coverflow into a complete
couch shell (Apple gamepad-UI parity), so a Deck/gamescope session is
self-sufficient end to end:
- Home: host carousel (saved + discovered + Add Host tile) with presence
pips, paired locks, wake & connect; A/Y/X/B per the Apple launcher.
- In-console SPAKE2 PIN pairing (previously needed the Decky plugin or a
desktop), add-host with a controller keyboard, couch settings over the
shared Settings store, wake-on-LAN overlay with retry, cancelable dials.
- Shell chrome: screen stack with push/pop entrance/exit choreography
(slide + fade + recede; aurora↔form backdrop crossfade), per-pad button
glyphs (PS shapes vs ABXY, keycaps with no pad), menu haptics, toasts,
embedded Geist typography, in-stream start banner.
- Steam Deck: our OSK never draws — fields start SDL text input (the new
Overlay::text_input_active hook) and Steam's keyboard types; a hint chip
points at STEAM + X. Other Linux gets the full gamepad keyboard tray.
- punktfunk-session: bare --browse opens Home; --browse host opens that
host's library with Home behind it (Decky launches keep working, B now
pops instead of quitting). Service threads run discovery, 10 s probes,
pairing, wake loops, fetches, and known-hosts persistence.
- Presenter contract: Launch actions carry the host, CancelConnect never
engages a won race, pad kind/list ride FrameCtx, menu events flow while
dialing so B can cancel.
Every screen renders to PNG on CPU raster for review
(PF_CONSOLE_DUMP=<dir> cargo test -p pf-console-ui -- --ignored dump).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>