feat(host,web): experimental DDC/CI monitor power-off for Exclusive sessions
ci / web (push) Successful in 47s
ci / docs-site (push) Successful in 1m7s
decky / build-publish (push) Successful in 19s
apple / swift (push) Successful in 1m10s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 15s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 12s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 30s
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 9s
apple / screenshots (push) Successful in 5m28s
ci / bench (push) Successful in 6m40s
docker / deploy-docs (push) Successful in 20s
windows-host / package (push) Successful in 8m45s
android / android (push) Successful in 12m43s
deb / build-publish (push) Successful in 13m1s
arch / build-publish (push) Successful in 14m36s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 13m5s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 16m20s
ci / rust (push) Successful in 22m12s

The sole-virtual-display stutter investigation's active experiment: when the
Exclusive isolate deactivates a physical monitor, the dark-but-connected head
keeps getting serviced (monitor standby auto-input-scan / DP link churn) at a
seconds-scale cadence — the leading suspect for the periodic double-jolt. A
panel commanded off over DDC/CI (the VESA monitor-control channel in the video
cable) believes it has an owner and, on cooperating firmware, stops probing.

- New `ddc_power_off` display-policy axis (default off): orthogonal to presets
  like game_session, stored in display-settings.json, surfaced in GET/PUT
  /display/settings + the enforced list, carried through the layout transform.
- windows/ddc.rs: VCP 0xD6 power-mode control via the dxva2 Physical Monitor
  API. Deliberately DPMS-off (0x04, DDC stays responsive, signal return wakes)
  and never power-button-off (0x05, bricks-until-button on many monitors).
  Probe-before-write; every failure is skip-and-log — monitors without DDC/CI,
  OSD-disabled, or behind docks/KVMs degrade to a logged no-op.
- Manager wiring: panels commanded off immediately BEFORE the Exclusive CCD
  isolate (an HMONITOR — and with it the DDC channel — only exists while the
  display is active); teardown wakes them right after the CCD restore, where
  returning signal alone already wakes most firmware.
- Web console: an Experimental-badged on/off control on the display card,
  applied immediately like the game-session axis and preserved across preset
  switches; EN/DE strings incl. the wake-failure escape hatch (press the
  monitor's power button once, turn the option off).

Diagnostic value on top of the fix: if this kills a reporter's stutter, the
churn is monitor-firmware-initiated; if only topology=primary/extend does, the
driver services dark heads regardless — the two remaining root-cause classes.

Verified: Linux 258 tests + clippy + fmt clean; Windows (RTX box) 220/220 +
clippy clean; web tsc + production build clean; openapi.json regenerated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 12:32:36 +02:00
parent f68f6bc590
commit cca5008805
9 changed files with 322 additions and 8 deletions
+201
View File
@@ -0,0 +1,201 @@
//! DDC/CI monitor panel power control — the EXPERIMENTAL `ddc_power_off` display-policy axis.
//!
//! DDC/CI is the VESA command channel to the monitor itself: an I²C bus inside the video cable
//! (dedicated pins on VGA/DVI/HDMI, tunneled over the AUX channel on DisplayPort) whose MCCS
//! "VCP codes" expose the monitor's OSD knobs to software. VCP 0xD6 is the power mode; we command
//! `0x04` (DPMS off — panel + backlight dark, firmware still listening) and never `0x05`
//! (power-button off — many monitors kill their DDC controller in that state and need a physical
//! button press to come back).
//!
//! Why: the "periodic double-jolt while the virtual display is the SOLE active display" stutter
//! class (Apollo #179/#358/#368/#563/#776 and our own field report). When an `Exclusive` isolate
//! deactivates the physical monitor, its link drops and the monitor falls into its no-signal flow:
//! standby with periodic auto-input-scan / link probing that the GPU driver services with
//! display-subsystem stalls at a seconds-scale cadence. A panel commanded off over DDC/CI believes
//! it has an owner and (on cooperating firmware) stops probing. This is deliberately shipped as an
//! experiment: whether it helps discriminates *who initiates* the churn — monitor firmware (DDC-off
//! fixes it) vs. the driver servicing a dark head regardless (only a driven link fixes it, i.e.
//! topology `primary`/`extend`).
//!
//! Everything here is best-effort and warn-and-continue: monitors without DDC/CI support (or with
//! it disabled in the OSD), docks/KVMs that don't pass the channel through, and laptop-internal
//! panels (ACPI backlight, no DDC) all simply probe as unsupported and are skipped. Each DDC
//! transaction can block for tens of ms — callers run at session acquire/teardown, never on the
//! frame path.
use windows::Win32::Devices::Display::{
DestroyPhysicalMonitors, GetNumberOfPhysicalMonitorsFromHMONITOR,
GetPhysicalMonitorsFromHMONITOR, GetVCPFeatureAndVCPFeatureReply, SetVCPFeature,
PHYSICAL_MONITOR,
};
use windows::Win32::Foundation::LPARAM;
use windows::Win32::Graphics::Gdi::{
EnumDisplayMonitors, GetMonitorInfoW, HDC, HMONITOR, MONITORINFOEXW,
};
/// MCCS VCP code 0xD6 — display power mode.
const VCP_POWER_MODE: u8 = 0xD6;
/// VCP 0xD6 value: on.
const POWER_ON: u32 = 0x01;
/// VCP 0xD6 value: DPMS off (dark panel, DDC controller stays responsive). Deliberately NOT 0x05.
const POWER_OFF: u32 = 0x04;
/// One active display: its HMONITOR and GDI device name (`\\.\DISPLAYn`).
struct ActiveMonitor {
hmon: HMONITOR,
device: String,
}
/// Enumerate the active displays (HMONITOR + GDI name). HMONITORs are only valid while a display
/// is part of the desktop — which is exactly why the off-command must run BEFORE a CCD isolate
/// and the on-command AFTER the restore.
fn active_monitors() -> Vec<ActiveMonitor> {
unsafe extern "system" fn collect(
hmon: HMONITOR,
_hdc: HDC,
_rect: *mut windows::Win32::Foundation::RECT,
data: LPARAM,
) -> windows::core::BOOL {
// SAFETY: `data` is the `&mut Vec<ActiveMonitor>` passed by `active_monitors` below,
// valid for the duration of the synchronous EnumDisplayMonitors call that invokes us.
let out = unsafe { &mut *(data.0 as *mut Vec<ActiveMonitor>) };
let mut info = MONITORINFOEXW::default();
info.monitorInfo.cbSize = std::mem::size_of::<MONITORINFOEXW>() as u32;
// SAFETY: `hmon` is the live monitor handle the enumeration just handed us; `info` is a
// properly-sized MONITORINFOEXW local whose cbSize is set, which GetMonitorInfoW requires
// to safely write the extended (szDevice) variant.
if unsafe { GetMonitorInfoW(hmon, &mut info.monitorInfo) }.as_bool() {
let len = info
.szDevice
.iter()
.position(|&c| c == 0)
.unwrap_or(info.szDevice.len());
out.push(ActiveMonitor {
hmon,
device: String::from_utf16_lossy(&info.szDevice[..len]),
});
}
true.into() // keep enumerating
}
let mut out: Vec<ActiveMonitor> = Vec::new();
// SAFETY: `collect` matches MONITORENUMPROC; `&mut out` outlives the synchronous enumeration
// and is only dereferenced inside the callback (single-threaded — user32 invokes it inline).
let _ = unsafe {
EnumDisplayMonitors(
None,
None,
Some(collect),
LPARAM(&mut out as *mut Vec<ActiveMonitor> as isize),
)
};
out
}
/// Apply `value` to VCP 0xD6 on every physical monitor behind `hmon` that answers a 0xD6 probe.
/// Returns how many panels acknowledged the set. `device` is for the log lines only.
fn set_power(hmon: HMONITOR, device: &str, value: u32) -> u32 {
let mut n = 0u32;
// SAFETY: `hmon` is a live monitor handle from the enumeration; `n` is a valid out-param.
if unsafe { GetNumberOfPhysicalMonitorsFromHMONITOR(hmon, &mut n) }.is_err() || n == 0 {
return 0;
}
let mut phys = vec![PHYSICAL_MONITOR::default(); n as usize];
// SAFETY: `phys` is sized to exactly the count the API just reported for this handle.
if unsafe { GetPhysicalMonitorsFromHMONITOR(hmon, &mut phys) }.is_err() {
return 0;
}
let mut acked = 0u32;
for p in &phys {
// PHYSICAL_MONITOR is `packed(1)` (dxva2 header pragma) — copy the fields OUT by value
// before touching them; a reference into a packed field is rejected (E0793, UB).
let handle = p.hPhysicalMonitor;
let desc_raw = p.szPhysicalMonitorDescription;
let len = desc_raw
.iter()
.position(|&c| c == 0)
.unwrap_or(desc_raw.len());
let desc = String::from_utf16_lossy(&desc_raw[..len]);
// Probe first: a monitor without DDC/CI (or with it disabled in the OSD, or behind a
// dock/KVM that drops the channel) fails here and is skipped — never blind-write to a
// bus we can't read.
let (mut current, mut max) = (0u32, 0u32);
// SAFETY: `handle` is the live physical-monitor handle (valid until
// DestroyPhysicalMonitors below); the value pointers are valid locals ('None' for the
// code-type out-param we don't need).
let probe = unsafe {
GetVCPFeatureAndVCPFeatureReply(
handle,
VCP_POWER_MODE,
None,
&mut current,
Some(&mut max),
)
};
if probe == 0 {
tracing::debug!(
device,
monitor = desc,
"DDC/CI: no reply to the power-mode (0xD6) probe — skipping (no DDC/CI, \
disabled in the OSD, or not passed through)"
);
continue;
}
// SAFETY: as the probe above — same live physical-monitor handle, plain value args.
let set = unsafe { SetVCPFeature(handle, VCP_POWER_MODE, value) };
if set == 0 {
tracing::warn!(
device,
monitor = desc,
value,
"DDC/CI: power-mode set failed after a successful probe"
);
} else {
tracing::info!(
device,
monitor = desc,
from = current,
to = value,
"DDC/CI: panel power mode commanded"
);
acked += 1;
}
}
// SAFETY: `phys` holds exactly the handles GetPhysicalMonitorsFromHMONITOR opened for us;
// each is destroyed once, here.
if let Err(e) = unsafe { DestroyPhysicalMonitors(&phys) } {
tracing::debug!(device, "DDC/CI: DestroyPhysicalMonitors failed: {e}");
}
acked
}
/// Command every physical panel EXCEPT `exclude_gdi` (the virtual display) off via DDC/CI
/// (VCP 0xD6 → DPMS off). Call while the physical displays are still ACTIVE — i.e. immediately
/// before the `Exclusive` CCD isolate. Returns how many panels acknowledged.
pub fn panel_off_except(exclude_gdi: &str) -> u32 {
let mut acked = 0;
for m in active_monitors() {
if m.device.eq_ignore_ascii_case(exclude_gdi) {
continue;
}
acked += set_power(m.hmon, &m.device, POWER_OFF);
}
if acked == 0 {
tracing::info!(
"DDC/CI: no panel accepted the off command — the experiment is a no-op on this box \
(monitors without DDC/CI, or none besides the virtual display)"
);
}
acked
}
/// Best-effort wake: command ON to every physical panel that answers. Call AFTER the CCD restore
/// has re-activated the physical paths — the returning signal alone wakes DPMS-off panels on most
/// firmware; this is the belt-and-braces for the rest.
pub fn panel_on_all() -> u32 {
let mut acked = 0;
for m in active_monitors() {
acked += set_power(m.hmon, &m.device, POWER_ON);
}
acked
}