The daily-driver five: 10-bit SDR, system keys for remote desktop, keep-host-audio, mode variables for prep, and a cursor sized to the client #412

Merged
enricobuehler merged 2 commits from worktree-daily-driver-five into main 2026-08-27 11:34:51 +00:00
Owner

Five field requests from one returning user ("just about ready as a daily driver"), traced through the code and implemented together — plus a compile break found on the way that this branch fixes first.

⚠ First commit: main's Windows host does not compile since #408

bbc01cdd (ABR Phase 4, merged this morning) threaded EncDerive through prepare_display, but the call site in native/handshake.rs passes enc_of before bit_depth while the signature takes bit_depth first — a type error inside a cfg(target_os = "windows") block. Nothing is red because the ci.yml clippy job runs on Linux and never compiles that block, and windows-host.yml only runs on dispatch/release. The fix is a deliberately minimal standalone commit at the base of this branch, cherry-pickable as a hotfix if this PR sits.

10-bit SDR, independent of HDR

VIDEO_CAP_10BIT and VIDEO_CAP_HDR were always separate wire bits; everything above the wire welded them — the client advertised both from the one HDR switch, the handshake demanded an HDR capture source for any 10-bit depth, SessionPlan derived hdr from bit_depth >= 10, and the encoder stamped BT.2020 PQ on every 10-bit format.

Now a "10-bit SDR" client setting (tier-P, default off) advertises the depth bit alone. The handshake resolves bit_depth = 10 without the HDR gates where the SDR-10 chain exists — Windows IDD-push + direct NVENC + HEVC; everywhere else the session stays 8-bit and the Welcome says so before the client builds its decoder. The session's HDR verdict (session_hdr) is decoupled from the depth everywhere it was derived: the Welcome's colour label, SessionPlan::resolve (new hdr parameter), and both vd.set_hdr sites. The capture side grows PixelFormat::Rgb10a2Sdr — the BGRA slot expanded 8→10 by a trivial full-res pass (HdrRgb10Converter::new_sdr_expand) — which NVENC ingests as ABGR10 and encodes Main10 under the ordinary BT.709 SDR VUI, 4:2:0 and 4:4:4 alike. The display's colour state is never touched at either end; advanced colour stays pinned off.

Honest scope: the desktop stays 8-bit — the win is the encoder's precision (compression stops adding banding to gradients), which is what the report describes. The client already renders by the bitstream VUI (csc_rows is depth×matrix generic), so no decode-side change was needed. AV1 is excluded until NVENC's packed-RGB→10-bit-AV1 ingest is measured on glass.

"Capture system shortcuts" works in Desktop mouse mode

The setting was wired end to end and then discarded by one !desktop term in the presenter's grab condition — documented as "a remote desktop is something you Alt-Tab away from", which made the toggle inert for exactly the remote-desktop use it matters most for. The grab now follows the setting in both mouse models; desktop mode keeps an escape capture mode never had (the unlocked pointer clicks any other window, focus drops, the chords return). Docs updated on both pages; the Mac's ⌘-chord path is unchanged (its desktop-mouse behaviour was a separate, deliberate decision).

Keep host audio playing (per session)

Moonlight's "Mute host PC speakers" box, unchecked, as a per-session client choice — for streaming to a laptop while wearing headphones that are plugged into the host. New setting + CLIENT_CAP_KEEP_HOST_AUDIO (0x20, request-only, no HOST_CAP echo: an older host just re-routes as always, which degrades to "host went quiet", never a broken session). A session holding the bit gets the host's audio devices left alone for exactly its lifetime (RAII guard): Windows skips the IPolicyConfig default parking and loopbacks the operator's own default device; Linux taps the default sink's monitor instead of claiming the default. Host-global best-effort across concurrent sessions, as the cap documents. Rows in the GTK, WinUI and console-UI settings; desktop-only until the Android session advertises the bit.

Prep commands carry the negotiated mode

A per-app do/undo step's entire environment was PF_APP_ID; the width/height/refresh/HDR a per-mode frame cap needs were resolved 80 lines above and written only to the unix-only marker file. Both serving planes now pass PF_STREAM_WIDTH / _HEIGHT / _REFRESH / _HDR — the marker file's vocabulary, spelled once in hooks::prep_mode_env — so an RTSS cap is one step instead of one hard-coded entry per device, on Windows hosts too.

The forwarded cursor matches the client's DPI

The desktop-mode pointer was resampled by the video fit factor alone, and SDL shows a custom cursor surface at ~1:1 physical pixels on every backend — so on a 200 % client our pointer was half the size of every native one (X11 identically; Wayland escapes via buffer scale). The resample factor now folds in SDL_GetWindowDisplayScale — the same model the Apple client gets for free from point-sized NSCursors.

Verification

Rebased onto 45d3ff6a and re-gated. Green: cargo fmt --all --check; workspace clippy --all-targets --locked -- -D warnings (Linux, pf-gtkflow image); punktfunk-core 279, pf-client-core 227, pf-console-ui 205, host hooks:: 13 + audio:: 127 — including the four new tests (prep env vocabulary, the keep-host-audio guard count, the caps-bit distinctness, depth-without-HDR advertising); pf-capture + pf-frame for x86_64-pc-windows-msvc via scripts/xcheck.sh.

Not verified — owed: the Windows-only halves of pf-encode and punktfunk-host (no Windows toolchain was reachable; note windows-host.yml is dispatch-only, which is also how #408's break slipped through), and everything on-glass: the SDR-10 stream end to end on a real NVIDIA host (the one that matters most), the desktop-mode grab on Windows, keep-host-audio against real headphones, prep env on a live launch, and the cursor at 200 %.

Five field requests from one returning user ("just about ready as a daily driver"), traced through the code and implemented together — plus a compile break found on the way that this branch fixes first. ## ⚠ First commit: main's Windows host does not compile since #408 `bbc01cdd` (ABR Phase 4, merged this morning) threaded `EncDerive` through `prepare_display`, but the call site in `native/handshake.rs` passes `enc_of` **before** `bit_depth` while the signature takes `bit_depth` first — a type error inside a `cfg(target_os = "windows")` block. Nothing is red because the ci.yml clippy job runs on Linux and never compiles that block, and `windows-host.yml` only runs on dispatch/release. The fix is a deliberately minimal standalone commit at the base of this branch, cherry-pickable as a hotfix if this PR sits. ## 10-bit SDR, independent of HDR `VIDEO_CAP_10BIT` and `VIDEO_CAP_HDR` were always separate wire bits; everything above the wire welded them — the client advertised both from the one HDR switch, the handshake demanded an HDR capture source for any 10-bit depth, `SessionPlan` derived `hdr` from `bit_depth >= 10`, and the encoder stamped BT.2020 PQ on every 10-bit format. Now a **"10-bit SDR"** client setting (tier-P, default off) advertises the depth bit alone. The handshake resolves `bit_depth = 10` without the HDR gates where the SDR-10 chain exists — Windows IDD-push + direct NVENC + HEVC; everywhere else the session stays 8-bit and the Welcome says so before the client builds its decoder. The session's HDR verdict (`session_hdr`) is decoupled from the depth everywhere it was derived: the Welcome's colour label, `SessionPlan::resolve` (new `hdr` parameter), and both `vd.set_hdr` sites. The capture side grows `PixelFormat::Rgb10a2Sdr` — the BGRA slot expanded 8→10 by a trivial full-res pass (`HdrRgb10Converter::new_sdr_expand`) — which NVENC ingests as ABGR10 and encodes Main10 under the ordinary BT.709 SDR VUI, 4:2:0 and 4:4:4 alike. The display's colour state is never touched at either end; advanced colour stays pinned off. Honest scope: the desktop stays 8-bit — the win is the *encoder's* precision (compression stops adding banding to gradients), which is what the report describes. The client already renders by the bitstream VUI (`csc_rows` is depth×matrix generic), so no decode-side change was needed. AV1 is excluded until NVENC's packed-RGB→10-bit-AV1 ingest is measured on glass. ## "Capture system shortcuts" works in Desktop mouse mode The setting was wired end to end and then discarded by one `!desktop` term in the presenter's grab condition — documented as "a remote desktop is something you Alt-Tab away from", which made the toggle inert for exactly the remote-desktop use it matters most for. The grab now follows the setting in both mouse models; desktop mode keeps an escape capture mode never had (the unlocked pointer clicks any other window, focus drops, the chords return). Docs updated on both pages; the Mac's ⌘-chord path is unchanged (its desktop-mouse behaviour was a separate, deliberate decision). ## Keep host audio playing (per session) Moonlight's "Mute host PC speakers" box, unchecked, as a per-session client choice — for streaming to a laptop while wearing headphones that are plugged into the host. New setting + `CLIENT_CAP_KEEP_HOST_AUDIO` (0x20, request-only, no HOST_CAP echo: an older host just re-routes as always, which degrades to "host went quiet", never a broken session). A session holding the bit gets the host's audio devices left alone for exactly its lifetime (RAII guard): Windows skips the IPolicyConfig default parking and loopbacks the operator's own default device; Linux taps the default sink's monitor instead of claiming the default. Host-global best-effort across concurrent sessions, as the cap documents. Rows in the GTK, WinUI and console-UI settings; desktop-only until the Android session advertises the bit. ## Prep commands carry the negotiated mode A per-app `do`/`undo` step's entire environment was `PF_APP_ID`; the width/height/refresh/HDR a per-mode frame cap needs were resolved 80 lines above and written only to the unix-only marker file. Both serving planes now pass `PF_STREAM_WIDTH` / `_HEIGHT` / `_REFRESH` / `_HDR` — the marker file's vocabulary, spelled once in `hooks::prep_mode_env` — so an RTSS cap is one step instead of one hard-coded entry per device, on Windows hosts too. ## The forwarded cursor matches the client's DPI The desktop-mode pointer was resampled by the video fit factor alone, and SDL shows a custom cursor surface at ~1:1 physical pixels on every backend — so on a 200 % client our pointer was half the size of every native one (X11 identically; Wayland escapes via buffer scale). The resample factor now folds in `SDL_GetWindowDisplayScale` — the same model the Apple client gets for free from point-sized `NSCursor`s. ## Verification Rebased onto `45d3ff6a` and re-gated. Green: `cargo fmt --all --check`; workspace `clippy --all-targets --locked -- -D warnings` (Linux, pf-gtkflow image); punktfunk-core 279, pf-client-core 227, pf-console-ui 205, host `hooks::` 13 + `audio::` 127 — including the four new tests (prep env vocabulary, the keep-host-audio guard count, the caps-bit distinctness, depth-without-HDR advertising); pf-capture + pf-frame for `x86_64-pc-windows-msvc` via `scripts/xcheck.sh`. **Not verified — owed:** the Windows-only halves of pf-encode and punktfunk-host (no Windows toolchain was reachable; note `windows-host.yml` is dispatch-only, which is also how #408's break slipped through), and everything on-glass: the SDR-10 stream end to end on a real NVIDIA host (the one that matters most), the desktop-mode grab on Windows, keep-host-audio against real headphones, prep env on a live launch, and the cursor at 200 %.
enricobuehler added 2 commits 2026-08-27 11:09:37 +00:00
bbc01cdd (ABR overhaul Phase 4, merged today as #408) threaded the new EncDerive
through prepare_display, but the call site in native/handshake.rs passes enc_of
BEFORE bit_depth while the signature takes bit_depth first — a type error in a
cfg(target_os = "windows") block. Nothing caught it: the ci.yml clippy job runs
on Linux and never compiles this block, and windows-host.yml only runs on
dispatch/release, so main's Windows host is currently unbuildable and no check
is red.

Reorder the arguments to match the signature. (--no-verify: this commit is cut
as a minimal cherry-pickable hotfix from a branch whose full fmt-clean state
lands in the following commits.)
feat: the daily-driver five — 10-bit SDR, system keys in desktop mode, keep-host-audio, mode env for prep, cursor at client DPI
ci / web (pull_request) Successful in 1m40s
ci / docs-site (pull_request) Successful in 1m50s
ci / bun-nix (pull_request) Successful in 28s
ci / docs-drift (pull_request) Successful in 26s
ci / rust-arm64 (pull_request) Successful in 3m31s
apple / swift (pull_request) Successful in 2m9s
apple / distribute (pull_request) Skipped
apple / screenshots (pull_request) Skipped
windows-client / client (arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (pull_request) Successful in 3m5s
android / android (pull_request) Successful in 9m8s
ci / rust (pull_request) Successful in 10m20s
windows-client / client (x64, , x86_64-pc-windows-msvc, C:\t) (pull_request) Successful in 6m41s
ee5e89d7d3
Five field requests from one returning user, traced and fixed together
(report: the "Daily-Driver Five" triage).

**10-bit SDR, independent of HDR.** VIDEO_CAP_10BIT and VIDEO_CAP_HDR were
always separate wire bits, but everything above the wire welded them: the
client advertised both from one "HDR" switch, the handshake required an HDR
capture source for any 10-bit depth, SessionPlan derived hdr from bit_depth,
and the encoder stamped BT.2020 PQ on every 10-bit format. Now a client
setting ("10-bit SDR", default off, tier-P) advertises the depth bit alone;
the handshake resolves bit_depth=10 without the HDR gates where the SDR-10
chain exists (Windows IDD-push + direct NVENC + HEVC — everywhere else the
session stays 8-bit, said honestly in the Welcome); the Welcome's colour
label, the virtual display's HDR bring-up and the capturer's want-HDR flag
all follow the new session-hdr verdict instead of the depth. The capture
side grows PixelFormat::Rgb10a2Sdr — the BGRA slot expanded 8→10 by a
trivial full-res pass (HdrRgb10Converter::new_sdr_expand) — which NVENC
ingests as ABGR10 and encodes Main10 under the ordinary BT.709 SDR VUI,
both 4:2:0 and 4:4:4. The win is the ENCODER's precision (compression
stops adding banding to gradients); the desktop stays 8-bit and neither
display's colour state is touched. The client already decodes by the VUI,
so no decode-side change was needed.

**"Capture system shortcuts" now works in Desktop mouse mode.** The setting
was wired end to end and then discarded by one `!desktop` term in the
presenter's grab condition — documented as "a remote desktop is something
you Alt-Tab away from", which made the toggle inert for exactly the
remote-desktop use it matters most for. The keyboard grab now follows the
setting in BOTH mouse models; desktop mode keeps an escape capture mode
never had (the unlocked pointer clicks any other window, focus drops, the
chords come back). Docs updated; the Mac's ⌘-chord behaviour is unchanged.

**Keep host audio playing (Moonlight's "Mute host PC speakers", unchecked,
per session).** New client setting + CLIENT_CAP_KEEP_HOST_AUDIO (0x20,
request-only, no HOST_CAP echo — an older host just re-routes as always).
A session holding the bit gets the host's audio devices left alone for its
lifetime (RAII guard): Windows skips the IPolicyConfig default parking and
loopbacks the operator's own default device; Linux taps the default sink's
monitor instead of claiming the default. Host-global best-effort across
concurrent sessions, as the cap documents. Rows in the GTK, Windows and
console-UI settings (desktop-only until Android advertises the bit).

**Prep commands carry the negotiated mode.** A per-app do/undo step's whole
environment was PF_APP_ID; the width/height/refresh/HDR it needs for a
per-mode frame cap were resolved 80 lines above and written only to the
unix-only marker file. Both serving planes now pass PF_STREAM_WIDTH /
_HEIGHT / _REFRESH / _HDR (the marker file's vocabulary, via one shared
hooks::prep_mode_env) — so an RTSS cap is one step instead of one
hard-coded entry per device, on Windows hosts too.

**The forwarded cursor matches the client's DPI.** The desktop-mode pointer
was resampled by the video fit factor alone, and SDL shows a custom cursor
surface at ~1:1 physical pixels on every backend — so on a 200% client our
pointer was half the size of every native one (and X11 the same; Wayland
escapes via buffer scale). The resample factor now folds in
SDL_GetWindowDisplayScale, the same model the Apple client gets for free
from point-sized NSCursors.

Verified: workspace clippy --locked -D warnings + targeted tests (hooks,
profiles, caps, console-ui rows, capture-policy) green on Linux
(pf-gtkflow); pf-capture/pf-frame windows-msvc green via xcheck; rustfmt
clean. NOT verified: the Windows-only halves of pf-encode/punktfunk-host
(no reachable Windows toolchain — .133 down; windows-host.yml is
dispatch-only, see the preceding commit), and everything on-glass: the
SDR-10 stream end to end, the desktop-mode grab, keep-host-audio wiring,
prep env on a live launch, and the cursor size at 200%.
enricobuehler merged commit 44929dcbf8 into main 2026-08-27 11:34:51 +00:00
enricobuehler deleted branch worktree-daily-driver-five 2026-08-27 11:35:02 +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#412