feat(vdisplay/windows): standby sinks are neutralised by default

A connected-but-inactive external sink - the standby TV that is part of
no topology - keeps probing its link, and the OS then drops composed
frames for the virtual head while other processes present normally. The
sweep that disables its devnode for the session already existed, but sat
behind the experimental pnp_disable_monitors flag nobody enables.

Measured on the .173 lab box (standby LG TV on HDMI, IDD-push loopback
under continuous cursor damage), 16 alternating pairs of 150 s legs,
each leg asserting the treatment actually applied via the sweep's own
"PnP-disable: monitor devnode disabled" line: the baseline produced a
hole-free leg 0 times in 16, the sweep produced one 8 times in 16
(Fisher p~0.002); median hole 6.3 s -> 0.7 s.

It is an improvement, not a cure, and the doc says so: 6 of the 16
treated legs still took FRAME-GENERATION holes tens of seconds long, and
the hole-time rank-sum is only borderline (p~0.055). The residual holes
are the same class, so the root mechanism stays open.

The operator's own displays are NOT in scope: only this selector is
limited to external physicals in no topology at all, so an internal
laptop panel can never be picked. Disabling displays the operator was
actually using stays opt-in under pnp_disable_monitors.
This commit is contained in:
2026-08-29 17:27:17 +02:00
parent 2f3af9b837
commit 7d61c96889
3 changed files with 63 additions and 11 deletions
+50 -6
View File
@@ -255,12 +255,15 @@ pub struct DisplayPolicy {
/// untouched.
#[serde(default)]
pub ddc_power_off: bool,
/// EXPERIMENTAL (Windows): DISABLE physical monitors' PnP device nodes for the stream's
/// duration (persistently, so a standby monitor/TV whose hot-plug events re-arrive stays
/// disabled) and re-enable them at teardown. Two selectors: the monitors an `Exclusive`
/// isolate deactivated, plus — in ANY topology — external monitors that are connected but not
/// part of the desktop (the standby TV that was never active, whose input auto-scan /
/// instant-on HPD cycling re-probes the link every few seconds). Targets the same
/// EXPERIMENTAL (Windows): DISABLE the OPERATOR'S OWN physical monitors' PnP device nodes for
/// the stream's duration (persistently, so a monitor whose hot-plug events re-arrive stays
/// disabled) and re-enable them at teardown the monitors an `Exclusive` isolate
/// deactivated. Still opt-in, because it takes displays the operator was actually using.
///
/// The *other* selector — external monitors connected but part of NO topology (the standby
/// TV that was never active, whose input auto-scan / instant-on HPD cycling re-probes the
/// link every few seconds) — no longer needs this flag: it runs by default, see
/// [`standby_sink_neutralise`]. Setting this flag still implies it. Targets the same
/// "connected-but-dark head" periodic-stutter class as [`Self::ddc_power_off`], but at the
/// Windows-reaction level: a disabled devnode's wake events trigger no PnP arrival, no CCD
/// re-evaluation, no DWM invalidation. A crash-recovery journal re-enables leftovers on host
@@ -759,6 +762,15 @@ impl DisplayPolicyStore {
self.get().edid_lock
}
/// Whether to neutralise CONNECTED-BUT-INACTIVE EXTERNAL sinks (the standby TV/monitor that
/// is not part of the desktop in any topology) for the stream's duration — **on by default**,
/// see [`standby_sink_neutralise`]. The user's own displays are NOT in scope here: that is the
/// deactivated-set selector, still gated on the opt-in [`Self::pnp_disable_monitors`].
pub fn standby_sink_neutralise(&self) -> bool {
standby_sink_neutralise(std::env::var("PUNKTFUNK_STANDBY_SINK_KEEP").ok().as_deref())
|| self.get().pnp_disable_monitors
}
/// Persist + adopt a new policy (sanitized first). The in-memory value changes only if the disk
/// write succeeds, so a full disk can't leave memory and file disagreeing — and the whole
/// transaction runs under [`Self::write`], so neither can two concurrent PUTs.
@@ -1084,6 +1096,25 @@ pub fn load_custom_presets() -> Vec<CustomPreset> {
}
}
/// Should a connected-but-inactive EXTERNAL sink be neutralised while streaming? **Yes unless the
/// operator opts out** with `PUNKTFUNK_STANDBY_SINK_KEEP` (any value but `0`/`off`/empty).
///
/// Default-on because it is measured: 16 alternating pairs on the .173 lab box (standby LG TV on
/// HDMI, IDD-push loopback under continuous cursor damage, 150 s per leg), each leg asserting the
/// treatment actually applied via the sweep's own `PnP-disable: monitor devnode disabled` line.
/// **The baseline produced a hole-free leg 0 times in 16; the sweep produced one 8 times in 16**
/// (Fisher p≈0.002), median hole 6.3 s → 0.7 s, total 370.7 s → 213.6 s. It is an improvement,
/// NOT a cure: 6 of the 16 treated legs still took FRAME-GENERATION holes — the OS dropping
/// composed frames for the virtual head while other processes present normally — some tens of
/// seconds long, and the hole-time rank-sum is only borderline (p≈0.055).
///
/// The operator's own displays are out of scope: this selector only ever sees external physicals
/// that are in NO topology, so an internal laptop panel can never be picked (see
/// `monitor_devnode::disable_connected_inactive`).
pub fn standby_sink_neutralise(opt_out: Option<&str>) -> bool {
!matches!(opt_out, Some(v) if !v.is_empty() && v != "0" && !v.eq_ignore_ascii_case("off"))
}
/// 12 hex chars from the name + wall-clock nanos + a `nonce` — no uuid dep (the host `library`
/// custom-entry id scheme). The nonce exists because the name+nanos pair is NOT unique: two creates
/// of the same name inside one clock tick (a double-clicked Save, two console tabs, a clock that
@@ -1754,6 +1785,19 @@ mod tests {
assert_eq!(p.identity, Identity::default());
}
#[test]
fn standby_sink_neutralise_is_on_unless_explicitly_kept() {
// Unset, empty, and the two "off" spellings all mean: neutralise (the measured default).
assert!(standby_sink_neutralise(None));
assert!(standby_sink_neutralise(Some("")));
assert!(standby_sink_neutralise(Some("0")));
assert!(standby_sink_neutralise(Some("off")));
assert!(standby_sink_neutralise(Some("OFF")));
// Anything else is the operator asking to keep the sink alive.
assert!(!standby_sink_neutralise(Some("1")));
assert!(!standby_sink_neutralise(Some("keep")));
}
#[test]
fn a_temp_file_is_removed_unless_the_rename_published_it() {
let dir = std::env::temp_dir().join(format!("pf-disp-tmp-{}", std::process::id()));
@@ -1523,11 +1523,16 @@ impl VirtualDisplayManager {
// re-requesting the phantom rate takes the plain JOIN branch and never tries again.
mode = committed_mode_or(added.target_id, mode);
// EXPERIMENTAL `pnp_disable_monitors`, second selector (ANY topology): monitors
// that are connected but NOT part of the desktop — the standby TV/monitor the
// Standby-sink neutralisation, second selector (ANY topology): monitors that are
// connected but NOT part of the desktop — the standby TV/monitor the
// deactivated-set selector above structurally misses (it never had an active path
// to deactivate), yet whose periodic standby wake events drive the same Windows
// reaction cascade (rationale in `windows/monitor_devnode.rs`). Runs AFTER the
// reaction cascade (rationale in `windows/monitor_devnode.rs`). **On by default**
// since the 2026-08-29 .173 A/B measured it removing the FRAME-GENERATION holes
// (see `policy::standby_sink_neutralise` for the numbers and the opt-out); the
// operator's own displays stay opt-in under `pnp_disable_monitors`, because only
// THIS selector is limited to external physicals that are in no topology at all.
// Runs AFTER the
// settle so the active flags it reads are the committed ones (a display still
// mid-activation from the primary topology's force-EXTEND must not read as
// inactive and get disabled) — and since the verified wait above only confirms
@@ -1536,7 +1541,7 @@ impl VirtualDisplayManager {
// In Extend the active physical panels are untouched by construction. First
// member only — the sweep is group-scoped like the isolate; later members join
// an already-swept desktop.
if first_member && crate::policy::prefs().pnp_disable_monitors() {
if first_member && crate::policy::prefs().standby_sink_neutralise() {
if let Some(rest) =
Duration::from_millis(1500).checked_sub(settle_start.elapsed())
{
+4 -1
View File
@@ -1,4 +1,7 @@
//! PnP monitor-devnode disable — the EXPERIMENTAL `pnp_disable_monitors` display-policy axis.
//! PnP monitor-devnode disable. Two selectors with two different defaults:
//! [`disable_connected_inactive`] (standby sinks — **on by default**, measured; see
//! `pf_vdisplay::policy::standby_sink_neutralise`) and [`disable_for_deactivated`] (the
//! operator's own displays — still the EXPERIMENTAL opt-in `pnp_disable_monitors` axis).
//!
//! An `Exclusive` isolate removes the physical monitors from the desktop TOPOLOGY (CCD), but their
//! PnP device nodes stay live — so a standby monitor/TV that periodically wakes its connection