3350d3cfbf4298a299d3223f4d6e7784be96713f
17
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
093c66cb1a |
fix(vdisplay/windows): a failed resize left a phantom monitor, and a refresh-only one silently didn't happen
Three defects in the mid-stream resize path. **A failed re-arrival put a DEAD monitor back in the slot.** `re_add` REMOVEs the old driver monitor before it ADDs the new one, so its only fallible step past that point means the old monitor is gone — yet the caller answered an `Err` by re-inserting the `Monitor` struct it had handed in, complete with a dead `key`, a departed `target_id` and a stale `gdi_name`. Every later `acquire` then joined a monitor that did not exist. It now rolls back: on ADD failure it re-ADDs at the OLD mode, so the session keeps streaming what it already had. The outcome is a three-way `ReAdd` enum because the distinction matters and a `Result` cannot carry it — `Arrived` stores the new monitor, `RolledBack` stores the RECOVERED one (a re-arrival mints a fresh `target_id`, so the struct the caller handed in is stale either way), and `Lost` means both ADDs failed and the slot must be left EMPTY so the next acquire creates a monitor rather than joining a phantom. **A refresh-only change reported success at the old refresh.** `wait_mode_advertised` compared resolution only, so a same-WxH new-Hz request answered "already advertised": the fast path skipped the `IOCTL_UPDATE_MODES` that would have taught the driver the new rate, `set_active_mode` fell back to the best advertised rate <= requested, and the resize returned Ok. It matches `dmDisplayFrequency` now, so an unadvertised refresh routes to UPDATE_MODES / the re-arrival instead. And `mon.mode = mode` recorded what was ASKED FOR. `set_active_mode` deliberately falls back to a lower advertised refresh rather than lose the client's resolution, so the field could claim a rate the display was not running — and it is what the next resize diffs against and what `/display/state` reports. It records what actually committed now, read back via the new `active_mode` (`active_resolution` delegates to it), and logs when the two differ. Deliberately NOT done by tightening `wait_mode_settled`: that gates the re-arrival fallback, so requiring an exact refresh there would force a full re-arrival every time the OS legitimately picked a lower rate. **A short IOCTL_ADD reply leaked an IddCx slot.** The IOCTL had SUCCEEDED — the driver already created the monitor and took a slot; only the reply was short — and the bail undid none of it. The slot pool is small enough that ~16 leaks wedge every later ADD at 0x80070490, which is the wedge the ghost-reap in this same function exists to recover from. It now issues the compensating REMOVE with the session id already in scope, and says so at error level if that fails too. Windows runner: clippy --all-targets clean, pf-vdisplay 55 passed, pf-capture 18 passed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
50a153b72a |
refactor(win-display): the CCD helpers were unsafe fn for serialization, not soundness
`pf-win-display`'s 21 CCD/GDI helpers were `pub unsafe fn` without a memory-safety obligation between them. Every one takes `Copy` scalars or borrowed Rust data, returns owned values, and discharges its FFI preconditions internally — `retry_set_display_config` even binds the input desktop itself, which is the one thing a caller could plausibly get wrong. Their own `# Safety` sections say as much: one reads "safe to call from any thread", and the rest say "call under the manager `state` lock". That is a SERIALIZATION requirement — calling one unlocked races the topology mutator and returns a stale answer, a correctness bug, not undefined behaviour. Encoding it with `unsafe fn` cost 55 `unsafe` blocks across THREE crates whose proofs could only restate "this takes a u32 and returns a String". That is how `unsafe` stops meaning anything: the blocks that wrap a real FFI obligation read exactly like the ones that wrap nothing. The requirement is now prose on the functions that have it, and `unsafe` marks only the FFI calls inside the helpers. win_display.rs 97 -> 63 `unsafe`, manager.rs 56 -> 30, pf_vdisplay.rs 34 -> 32, plus 10 sites in pf-capture's IDD-push paths. 55 blocks and 55 orphaned `// SAFETY:` proofs gone; `#![allow(clippy::missing_safety_doc)]` with them, since nothing is `pub unsafe fn` any more. Two things the compiler handed back once the blocks went, both of which existed only to carry a proof: the `let-else` parens in `force_mode_reenumeration` and `apply_source_positions`, and the nested `if` in `wait_mode_settled` — which is `&&` again, still short-circuiting, so the second CCD query on a 25 ms poll is unchanged. Scope note: the sweep and the plan both scoped this as two crates. It is three — pf-capture calls the same helpers from 10 sites, and `unused_unsafe` under `-D warnings` makes that non-optional. Found by re-deriving the count, which the plan asked for because the underlying finding had been REFUTED in verification; the refutation was wrong about the magnitude (22 of manager.rs's 45 blocks, against its claimed 24). Verified on the Windows runner: clippy --all-targets clean over pf-frame, pf-win-display, pf-capture and pf-vdisplay; pf-vdisplay 55 passed, pf-capture 18 passed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e7dc7e9d18 |
fix(win-display): every CCD/GDI operation now carries a proof, not just the blocks that needed one
Companion to c16f2850, which closed the same hole in pf-vdisplay. The file header claims "Every `unsafe` block in this file carries a `// SAFETY:` proof", and that was true — it just did not cover the 84 unsafe OPERATIONS sitting directly in `unsafe fn` bodies, which under edition 2021 need no block and so carry no proof. That is the whole CCD surface: every QueryDisplayConfig, every SetDisplayConfig, every DISPLAYCONFIG union read. Rather than 84 restatements of the same argument, the shared contract is stated ONCE at the top — how the buffer-size/query pair keeps pointer and count in agreement, and why the two obligations the compiler cannot see (arrays only ever come from the OS or from a SavedConfig this module built; SetDisplayConfig serialised under the manager `state` lock and bound to the input desktop) hold at every site. Per-site comments then name the site-specific fact instead of repeating the invariant. The union reads justify differently and the header says so: `modeInfoIdx` and `ADVANCED_COLOR_INFO.value` overlay same-sized POD, so no discriminant can be got wrong, while `MODE_INFO.Anonymous.sourceMode` IS discriminated by `infoType` — and every read of it is guarded by that check. Writing that down found the one place the guard does NOT gate the read: the `then_some` in set_virtual_primary_ccd evaluates eagerly, so POD-ness is what carries it there, not the check. The comment says so rather than implying a guard that is not doing the work. Two shape changes, both deliberate. `wait_mode_settled`'s two calls are nested rather than `&&`-joined so the second CCD query stays short-circuited — it polls every 25 ms and must not run while the target has no active path. And the eager union read in `set_virtual_primary_ccd` is hoisted to a `let` so its proof has somewhere to attach; `then_some` already evaluated it eagerly, so nothing changes. Union field ASSIGNMENTS are safe in Rust — only reads are — so two of them keep no block. Verified on the Windows CI runner: clippy -D warnings clean across pf-frame, pf-win-display, pf-capture and pf-vdisplay, and both crates' tests pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
622817954a |
fix(capture/win-display): a deactivated laptop panel is a named stall suspect, not a blind spot
audit / bun-audit (push) Successful in 14s
ci / web (push) Successful in 52s
windows-drivers / probe-and-proto (push) Successful in 44s
ci / docs-site (push) Successful in 1m7s
audit / cargo-audit (push) Successful in 2m18s
windows-drivers / driver-build (push) Successful in 2m21s
apple / swift (push) Successful in 4m54s
ci / bench (push) Successful in 6m41s
ci / rust-arm64 (push) Successful in 10m4s
decky / build-publish (push) Successful in 42s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 26s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 56s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 14s
deb / build-publish-host (push) Successful in 10m31s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 15s
ci / rust (push) Failing after 13m9s
deb / build-publish (push) Successful in 12m14s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m9s
android / android (push) Successful in 16m5s
arch / build-publish (push) Successful in 17m22s
deb / build-publish-client-arm64 (push) Successful in 9m46s
windows-host / package (push) Successful in 16m38s
windows-host / winget-source (push) Skipped
flatpak / build-publish (push) Successful in 7m11s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m24s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 4m30s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 17m10s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 16m38s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 4m42s
release / apple (push) Successful in 27m50s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 5m49s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 26m34s
docker / deploy-docs (push) Successful in 54s
docker / build-push-arm64cross (push) Successful in 9m53s
apple / screenshots (push) Successful in 23m52s
Field A/B (reporter, Legion 5 Pro hybrid, 2026-07-27): the ~2 s capture-stall metronome IS the exclusive isolate's own doing — deactivating the laptop panel leaves a dark-but-connected eDP head whose driver-level servicing disturbs the capture path (~1.7-2.8 s period); `topology: primary` (panel stays active) stopped it outright (16.3 stalls/min → 0). Our own .221 logged the identical signature the day before. The detector then steered AWAY from the cause: connected_inactive filtered to EXTERNAL physicals only, so the one display that mattered — on a hybrid laptop, the default config — reported as `none`. - TargetInventory: new `internal_panel` class bit (eDP/LVDS/embedded). - connected_inactive_externals → connected_inactive_physicals: internal panels join the suspect list (virtual/indirect stay excluded); a nameless panel renders as "laptop panel". - The below-OS METRONOMIC warn names the dark-panel case and its actual cures (`topology: primary`, `pnp_disable_monitors`) — the old text's "unplug it" prescriptions are impossible for an internal panel. - ddc_power_off: a zero-ack sweep now says at INFO that the axis did nothing (internal panels have no DDC/CI) instead of silently no-op'ing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
1945052e66 |
fix(capture/vdisplay): descriptor-following defers while a topology reassert is in flight
The exclusive-topology reassert's forced re-commit transiently bounces the virtual display's mode; the descriptor poller can sample that EVICTION state and recreate the ring at a mode the recovery chain is about to undo (field log 2026-07-27 10:30:44Z: hdr=true → false → recreate → recovery restores true → second recreate). New pf-win-display::topology_churn latch (deadline semantics — self-expiring, so a lost release can never wedge descriptor- following off): the watchdog holds it for interval+3s each fighting round and releases on "stable again"; poll_display_hdr consumes samples but acts on nothing while held — which also keeps the negotiated-depth pin-back from issuing a CCD write mid-eviction, where it would fight the reassert itself. The deliberate recreate_ring_in_place recovery path is unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
8362d57001 |
fix(vdisplay/windows): exclusive topology gets re-asserted — a verified isolate is not durable
ci / docs-site (push) Successful in 1m8s
ci / web (push) Successful in 1m40s
apple / swift (push) Successful in 5m9s
ci / bench (push) Successful in 6m36s
ci / rust-arm64 (push) Successful in 10m12s
deb / build-publish (push) Successful in 9m32s
decky / build-publish (push) Successful in 34s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 30s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 59s
android / android (push) Canceled after 13m40s
apple / screenshots (push) Canceled after 8m25s
arch / build-publish (push) Canceled after 13m44s
ci / rust (push) Canceled after 13m45s
deb / build-publish-host (push) Canceled after 12m5s
deb / build-publish-client-arm64 (push) Canceled after 7m5s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Canceled after 2s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Canceled after 0s
docker / build-push-arm64cross (push) Canceled after 0s
docker / deploy-docs (push) Canceled after 0s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Canceled after 2m28s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Canceled after 1m57s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 2s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 1s
windows-host / package (push) Canceled after 13m45s
The CCD isolate verifies "sole active desktop" at commit time and is never checked again. On a hybrid Intel+NVIDIA box the internal panel is re-activated moments later (the isolated topology is deliberately not saved to the CCD database so teardown can restore the user's layout, and the post-isolate resize/HDR churn — or the panel's own driver, or display-poller software — re-resolves back to the stored layout). Proven on-glass: seconds after the verify, CCD showed the panel ACTIVE beside the virtual display while the host still believed it was exclusive — cursor and windows can land off-stream, and the lock screen can land on the physical panel. A group-scoped watchdog now re-queries every PUNKTFUNK_EXCLUSIVE_REASSERT_MS (default 2 s, 0 disables) while an EXCLUSIVE isolate is live and re-issues the isolate when a non-managed display crept back, logging who-is-fighting escalation (3×WARN → 1×ERROR → DEBUG). Gated on a new GroupState.ccd_exclusive flag so a Primary group's deliberately-lit panels are never "fixed". Cycles take the state lock via try_lock with a sliced sleep, so teardown's stop+join under the state lock cannot deadlock and is bounded by ~250 ms. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
9b241d9d7b |
fix(pf-win-display): anchor the kept sources at the desktop origin when isolating
ci / docs-site (push) Successful in 1m0s
ci / web (push) Successful in 1m4s
apple / swift (push) Successful in 1m22s
decky / build-publish (push) Successful in 34s
ci / bench (push) Successful in 6m36s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 46s
apple / screenshots (push) Successful in 6m35s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 6m51s
deb / build-publish (push) Successful in 12m42s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 10s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 9s
deb / build-publish-host (push) Successful in 13m31s
android / android (push) Successful in 15m54s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 7m18s
arch / build-publish (push) Successful in 16m19s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9m34s
docker / deploy-docs (push) Successful in 29s
ci / rust (push) Successful in 20m38s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 16m51s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 16m34s
Round 3 of the field-reported exclusive-topology 0x57, with the reporter's retest logs finally separating the variable: the SAME isolate call converged rc=0 whenever a kept member already sat at (0,0), and failed 0x57 on every shape — doomed-path-carried AND keep-only escalation — whenever the doomed physical held the origin. A committable config must still contain a primary (a source pinned exactly at (0,0)); deactivating the display that held it while the kept virtual stays at its EXTEND offset supplies an origin-less desktop, which Windows rejects wholesale. Translate the kept sources rigidly so the top-left-most lands on the origin; sets already covering (0,0) are untouched so plain re-commits stay byte-identical. Also from the same logs: - restore_displays_ccd now guarantees the desk is never left all-dark: the saved snapshot can be unappliable (0x64a — it pinned a virtual target incarnation removed before teardown) or apply cleanly yet re-light nothing (snapshotted while an earlier failed teardown had the physicals off). If no external physical is active after the apply while one is connected, fall back to the database EXTEND preset. Internal panels don't count — a closed lid must not be forced back on. - the final isolate failure now names the surviving targets instead of asserting 'a non-virtual display stayed active' — the logs showed a sibling VIRTUAL display as the survivor (linger-expiry shrink), and the wording sent triage the wrong way. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
b5fa878bc6 |
fix(pf-win-display): follow the input desktop so a UAC prompt can't refuse display writes
Field-reported 2026-07-23: a consent prompt left up on an unattended host made
the box unreachable. Every connect black-screened and failed after ~60 s, and
the host had to be reached over SSH to clear the prompt.
Windows refuses ChangeDisplaySettingsEx/SetDisplayConfig issued from a thread
that is not on the desktop currently receiving input. While UAC (or the lock /
logon screen) is up that desktop is Winlogon, and the host — which the service
launches explicitly onto WinSta0\Default — got DISP_CHANGE_FAILED /
ERROR_ACCESS_DENIED for every write. A session starting in that window could
never set its virtual display's mode, so the capturer sized its ring to a stale
mode, every composed frame was dropped for a size mismatch, and bring-up burned
8 retries before giving up.
Measured on glass (RTX box, SYSTEM host in console session 2, real consent
prompt up, virtual display active):
INPUT desktop = Winlogon
UNBOUND CDS_TEST -> -1 (DISP_CHANGE_FAILED)
UNBOUND SDC_VALIDATE -> 0x5 (ERROR_ACCESS_DENIED)
BOUND CDS_TEST -> 0 (DISP_CHANGE_SUCCESSFUL)
BOUND SDC_VALIDATE -> 0x0 (ERROR_SUCCESS)
New input_desktop.rs mirrors pf-inject's sendinput.rs retry model: issue the
write, and only when it fails the way a wrong-desktop write fails, rebind this
thread to the current input desktop and retry once. A working write is never
touched, so the normal path is unchanged. The retry predicate stays narrow
(ERROR_ACCESS_DENIED only) so the unrelated 0x57 exclusive-mode topology bug is
not re-issued and mis-attributed. Unlike sendinput's dedicated injector thread,
the binding here is SCOPED — a shared display-write thread left on a Winlogon
desktop that is later destroyed would fail every subsequent write, which is the
wedge this removes.
Applied to all eight SetDisplayConfig sites and both ChangeDisplaySettingsExW
calls in set_active_mode. The isolate site now decides its supplied config once,
outside the write, so a retry re-issues the identical config rather than logging
its escalation twice.
A save is logged, because a silent one is indistinguishable in a field log from
a write that never needed saving — which made the first on-glass verification of
this change inconclusive.
Both ACCESS_DENIED diagnostics now ask which cause applies instead of naming
only the disconnected-RDP one: that phrasing sent this investigation chasing a
phantom RDP session on a host that was already service-launched in the console
session, while a consent prompt was the actual cause.
Verified on glass: connect with a consent prompt up now reaches first frame in
3.0 s (was a black screen then "Connection failed" after ~60 s), with six
"retried bound to it and it applied desktop=Winlogon" lines and rc=0x0
throughout.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
51792536d1 |
fix(pf-win-display): escalate CCD isolate to a keep-only supplied config on 0x57
apple / swift (push) Successful in 1m30s
ci / web (push) Successful in 1m8s
apple / screenshots (push) Successful in 6m59s
ci / docs-site (push) Successful in 1m6s
android / android (push) Successful in 14m35s
arch / build-publish (push) Successful in 14m20s
decky / build-publish (push) Successful in 21s
ci / bench (push) Successful in 6m4s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 18s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 18s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 16s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 17s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 15s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 15s
deb / build-publish (push) Successful in 9m41s
ci / rust (push) Successful in 20m14s
deb / build-publish-host (push) Successful in 11m30s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 21m42s
docker / deploy-docs (push) Successful in 12s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 22m2s
Field bug (Steam Deck client, exclusive topology): every isolate retry failed
SetDisplayConfig 0x57 with the physical panel left lit, while iPhone sessions
worked — their virtual monitor's connected-set already had a sole-display
topology persisted, so Windows deactivated the physical on hotplug and the
isolate was a no-op re-commit (deactivated 0). The Deck's monitor identity had
no such entry, forcing the live-deactivate path: supplying the doomed path
(inactive, modes unpinned) plus its orphaned mode entries is rejected 0x57 by
some driver/OS validation combos, so the retry loop could never converge — the
prior mode-index unpin (
|
||
|
|
d31d41b6b8 |
fix(windows/capture): match the HDR-composited cursor to the target's SDR white level
sRGB→linear alone lands cursor-white at 80 nits (scRGB 1.0) while DWM composes the surrounding SDR desktop at the user's SDR-brightness setting (~200+ nits by default) — the cursor read visibly dark on HDR. Scale by DISPLAYCONFIG_SDR_WHITE_LEVEL (new win_display::sdr_white_level_scale, queried on blend-scratch rebuilds — the CCD query stays off the per-frame path). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
5e088af7ed |
fix(pf-win-display): unpin mode indexes on paths the CCD isolate deactivates
ci / web (push) Successful in 49s
apple / swift (push) Successful in 1m24s
ci / docs-site (push) Successful in 55s
decky / build-publish (push) Successful in 21s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 10s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
apple / screenshots (push) Successful in 6m33s
ci / bench (push) Successful in 6m41s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 11s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 11s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 5m23s
android / android (push) Successful in 12m50s
docker / deploy-docs (push) Successful in 25s
deb / build-publish-host (push) Successful in 10m10s
arch / build-publish (push) Successful in 17m18s
deb / build-publish (push) Successful in 12m51s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 14m3s
ci / rust (push) Successful in 27m3s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 18m32s
Field report: with Exclusive Topology, the physical monitor sometimes stayed lit — every isolate attempt re-committed and failed SetDisplayConfig with 0x57 ERROR_INVALID_PARAMETER. Clearing DISPLAYCONFIG_PATH_ACTIVE alone leaves the deactivated path's source/target modeInfoIdx pinned to the queried mode entries, and per the SetDisplayConfig contract a path being turned off must have BOTH mode indexes set to DISPLAYCONFIG_PATH_MODE_IDX_INVALID; some driver/topology combinations (notably when the doomed path shares a source with the kept virtual path) reject the supplied config outright, so the retry loop could never converge. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
c8ee4b9902 |
fix(pf-vdisplay,pf-capture,pf-win-display): pre-split paths in the auto-merged v4 code
windows-drivers / probe-and-proto (push) Successful in 31s
ci / web (push) Successful in 43s
apple / swift (push) Failing after 1m1s
ci / docs-site (push) Successful in 1m0s
apple / screenshots (push) Has been skipped
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
decky / build-publish (push) Successful in 19s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 10s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 44s
windows-drivers / driver-build (push) Successful in 1m38s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m1s
ci / bench (push) Successful in 5m34s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 5m53s
android / android (push) Has been cancelled
arch / build-publish (push) Has been cancelled
ci / rust (push) Has been cancelled
deb / build-publish (push) Has been cancelled
windows-host / package (push) Successful in 15m6s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 18m48s
docker / deploy-docs (push) Successful in 23s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 18m28s
The rename-followed perf hunks still said crate::win_display:: (the pre-W6 layout) — point them at pf_win_display::win_display:: and widen the four helpers they call cross-crate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
1197415216 |
fix(pf-vdisplay,pf-win-display): v4 trait surface on the extracted driver.rs + cross-crate visibility
The W-refactor extracted VdisplayDriver into manager/driver.rs (the merge resolution assumed it deleted) — carry the v4 changes there: open() returns the driver's protocol version, update_modes() default-errs to the re-arrival fallback. wait_target_departed goes pub for the manager's cross-crate call. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
09849906e9 |
Merge perf/first-frame-latency: driver proto v4 + first-frame/resize latency (P0-P2)
Brings the first-frame-latency branch (P0.1 transition tracing, P1.1/P1.2 Welcome-time display prep, P2 in-place resize; pf-driver-proto v3 -> v4 with IOCTL_UPDATE_MODES) onto current main. The branch predates the W6.2/W7 splits, so git's rename detection carried most of it into the moved crates (pf-capture idd_push, pf-vdisplay manager/pf_vdisplay, pf-win-display, pf-driver-proto, the driver workspace) and the punktfunk1.rs remainder was re-homed by hand: - native/handshake.rs: welcome/start trace marks + the Welcome-time display prep spawn (the prep thread BECOMES the stream thread; hand-off via a SyncSender<SessionContext>). negotiate() gains bringup/quit/stop and returns the PrepHandle. - native.rs: bringup/resize_ms creation + the stop/quit flags hoisted BEFORE the handshake (the close watcher splits: flags pre-handshake, lifecycle events post-handshake where `hello` exists); punch_done stamp; the data plane adopts the prep thread's result or builds inline. - native/stream.rs: SessionContext/SendStats carry the trace; send_loop finishes it on the first video packet; the resize path gains the in-place fast path (try_inplace_resize) with the full rebuild as fallback, restructured so both share the post-rebuild bookkeeping; prepare_display/PreparedDisplay/ PrepHandle; build_pipeline(+retry) thread the stage marks. - session_status/mgmt: ttff_ms + last_resize_ms per session (union with the lifecycle-events fields main added to the same spots). - pf-capture: Capturer gains capture_target_id() + resize_output() defaults. - pf-vdisplay manager: perf's faster activation poll (60x50ms) + the settle floor before the PnP sweep, on main's knobs/no-trait shape. Also: packaging/windows/build-gamepad-drivers.ps1 is ASCII again (an em-dash from the pf-mouse work tripped windows-host.yml's locale-safety gate on main). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
845a97601d |
feat(pf-frame,pf-win-display): leaf primitives for the lid-closed first-frame fix
Three leaf-crate additions the IDD-push capturer (pf-capture, plan §W6 C6)
builds on — committed ahead so the capture-crate extraction and the HID
compose kick can land on top:
- pf-frame session_tuning::DisplayWakeRequest — RAII PowerCreateRequest/
PowerSetRequest(PowerRequestDisplayRequired + SystemRequired), the
service-grade 'someone is watching this screen' assertion (visible in
powercfg /requests), held for a capture session so the console cannot
drop into display-off mid-stream. Object-lifetime, unlike the
thread-bound ES_* flags in on_hot_thread. Prevention only: no power
request turns an already-off display back on — that wake is input's
job (the virtual-mouse compose kick).
- pf-win-display win_display::desktop_bounds() — the virtual-desktop
bounds as the union of every ACTIVE CCD path's source rect. From the
CCD database (global), NOT GetSystemMetrics (a per-session view), so
a non-console-session host still aims HID absolute coordinates at the
console's real layout.
- pf-win-display console_session_mismatch() — the session guard from
|
||
|
|
3d9b329084 |
fix(host): name the lid-closed/no-frames failure — display-write decode, console-session guard, driver-truth attach diagnostics
Field report (Windows laptop, lid closed, Tailscale): v0.12.0's activation fix works — the pf-vdisplay target activates in ~200ms — but the session still dies at the first-frame gate: 'driver_status=1 but no frame published within 4s'. Triage showed three independent blind spots; this names all of them at their source instead of guessing downstream: - pf-win-display: decode ChangeDisplaySettingsExW failures (-1 FAILED — a display write rejected, the wrong/remote-session signature — vs -2 BADMODE, which the old 'mode not advertised?' text conflated), and WARN on every non-zero SetDisplayConfig rc in the CCD isolate even when verification passes vacuously (the lid-closed case: nothing else active, so the INFO swallowed rc=0x5 ERROR_ACCESS_DENIED while the load-bearing COMMIT_MODES → ASSIGN_SWAPCHAIN re-commit silently never applied). Access-denied rcs get the remedy appended (console session / installed service). - host: console-session guard (interactive::console_session_mismatch) — a host outside the active console session (a hand-launched host after an RDP round-trip) fails every display write, reads the wrong session's GDI view, and its SendInput compose kicks go nowhere. Named ERROR at vdisplay acquire + appended to the first-frame timeout, instead of the misleading generic failure. (The idd_push diagnosis half of this landed in 9a36ea21; this commit adds the proto helpers + session guard it references, healing the windows-cfg build.) - proto + driver: while OPENED, driver_status_detail now carries a live packed word (bit31 live-marker | offered 15-bit | mismatch-dropped 16-bit) maintained by the publisher, so the host's first-frame timeout can tell apart: never-attached (no swap-chain worker ran), attached-but-DWM-composed- zero-frames (undamaged/powered-off desktop, kicks blocked on the secure desktop), and composed-but-every-frame-mismatched (ring sized from a stale/ foreign-session GDI mode). Zero layout change, old drivers read as 'no detail'; unit-tested pack/unpack in pf-driver-proto. Verified on winbox: cargo check + clippy -p punktfunk-host -p pf-win-display -p pf-driver-proto EXIT 0, drivers ws cargo check -p pf-vdisplay EXIT 0 (Version_Number=10.0.26100.0), cargo fmt --all --check clean; pf-driver-proto tests 13/13 pass locally. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ccc4b08d45 |
refactor(host/W6.2): extract the Windows display-topology cluster into the pf-win-display leaf crate
windows/{win_display,monitor_devnode,display_events}.rs move into
crates/pf-win-display: the CCD/GDI path-activation + mode-set + HDR
advanced-colour + source-rect helpers, the PnP monitor-devnode enable/disable
lever, and the WM_DISPLAYCHANGE / device-arrival watch. The coming pf-capture
crate's IDD-push capturer consumes all three; the host's pf-vdisplay backend
consumes win_display + monitor_devnode. A leaf lets both depend on them as a
PEER instead of the capturer reaching back into the orchestrator (plan §W6).
win_display's one external tie (crate::vdisplay::Mode) becomes the underlying
punktfunk_core::Mode; the cluster is otherwise self-contained (pf-paths for the
state file, serde_json for it, windows). pub(crate) items bump to pub at the
boundary; win_display carries a module-level allow(missing_safety_doc) to keep
the pre-carve behavior (the FFI helpers were pub(crate) unsafe fn with prose
safety docs — the lint only fires once they're pub, and this is an internal
publish=false leaf). The host imports the three modules at its crate root, so
every crate::{win_display,monitor_devnode,display_events}::* path is unchanged.
Verified: Linux clippy -D warnings (leaf empty + host
nvenc,vulkan-encode,pyrowave --all-targets); Windows clippy -D warnings
(pf-win-display --all-targets + host nvenc,amf-qsv --all-targets) Finished exit 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|