chore: consolidate parallel-session WIP (HOLD — do not push)

Local snapshot of intermingled in-flight work, committed to unblock the encode
refactor (a clean ffmpeg_win.rs for the vbv-dedup follow-on). These hunks span
the same files and can't be cleanly split here; the commit bundles three
distinct workstreams that each belong in their own PR:

  - logging rework (~43 files: level re-tiering, structured fields, `?e`,
    hot-path flood latches)
  - conflicting-host detection (detect.rs + detect/{linux,windows}.rs + wiring
    in main.rs/mgmt.rs/Cargo.toml/docs/packaging)
  - standby-sink DWM-stall attribution (windows/display_events.rs + capture/
    vdisplay wiring)

NOT verified as a combination. NOT to be pushed until the refactor is done and
these are re-verified and reorganized into their proper per-workstream PRs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 12:42:53 +02:00
parent d466e3e2b2
commit 11045a0f70
62 changed files with 1595 additions and 214 deletions
+3 -3
View File
@@ -181,9 +181,9 @@ pub fn panel_off_except(exclude_gdi: &str) -> u32 {
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)"
tracing::debug!(
"DDC/CI: no physical panel accepted the DPMS-off command \
(no DDC/CI-capable panel besides the virtual display)"
);
}
acked
@@ -0,0 +1,337 @@
//! OS display-event listener — the attribution sensor for the periodic-stutter disturbance class.
//!
//! The capture-stall watch ([`crate::capture::windows::idd_push`]) can SAY "DWM stopped composing
//! on a stable period", but not WHY. Field evidence (Apollo's Stuttering Clinic, Apollo #384,
//! Tom's HW "stutter from disabled-but-connected monitors") points at a connected-but-idle sink
//! (standby TV/monitor, active HDMI cable, KVM/AVR) re-probing the link every few seconds; the GPU
//! driver services each probe below the topology layer, and on some boxes Windows additionally
//! tears down + re-arrives the monitor's devnode each time. This module timestamps everything
//! Windows lets user mode see of that reaction so the stall log can name the disturbance instead
//! of guessing:
//!
//! - `WM_DEVICECHANGE` + `RegisterDeviceNotificationW(GUID_DEVINTERFACE_MONITOR)`: monitor device
//! interface arrival/removal — fires on devnode churn even when the final topology is unchanged
//! (the "reaction cascade" class `pnp_disable_monitors` suppresses), with the interface path
//! naming WHICH monitor pulsed.
//! - `DBT_DEVNODES_CHANGED`: the broadcast catch-all for PnP tree churn (no payload).
//! - `WM_DISPLAYCHANGE`: an actual mode/topology commit reached the desktop (this one does NOT
//! fire for a pure probe with no mode delta — its absence is itself a signal).
//!
//! A pure driver-internal probe (EDID/DDC read, DP link retrain) emits NONE of these — that
//! absence, paired with metronomic stalls, is what discriminates "driver services a standby sink
//! below the OS" from "Windows re-enumerates the monitor". Kernel-precise attribution (DxgKrnl ETW
//! event 272 `DxgkCbIndicateChildStatus`) is a possible follow-up; this listener is the cheap
//! always-on first stage.
//!
//! The listener thread also keeps a cached CCD target inventory (refreshed on each event + a slow
//! timer), so the capture thread can name connected-but-inactive external displays — the prime
//! suspects — without ever touching the CCD lock itself (the display-config lock is exactly what
//! stalls during churn; the capture thread must never block on it).
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
use std::collections::VecDeque;
use std::sync::{Mutex, Once, OnceLock};
use std::time::Instant;
use windows::core::PCWSTR;
use windows::Win32::Devices::Display::GUID_DEVINTERFACE_MONITOR;
use windows::Win32::Foundation::{HANDLE, HWND, LPARAM, LRESULT, WPARAM};
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
use windows::Win32::UI::WindowsAndMessaging::{
CreateWindowExW, DefWindowProcW, DispatchMessageW, GetMessageW, RegisterClassW,
RegisterDeviceNotificationW, SetTimer, DBT_DEVICEARRIVAL, DBT_DEVICEREMOVECOMPLETE,
DBT_DEVNODES_CHANGED, DBT_DEVTYP_DEVICEINTERFACE, DEVICE_NOTIFY_WINDOW_HANDLE,
DEV_BROADCAST_DEVICEINTERFACE_W, DEV_BROADCAST_HDR, MSG, WINDOW_EX_STYLE, WM_DEVICECHANGE,
WM_DISPLAYCHANGE, WM_TIMER, WNDCLASSW, WS_OVERLAPPED,
};
/// One OS-visible display event, timestamped at receipt.
#[derive(Clone)]
pub(crate) struct DisplayEvent {
pub at: Instant,
pub kind: DisplayEventKind,
/// Monitor device instance id for arrival/removal (e.g. `DISPLAY\GSM83CD\...`), else `None`.
pub detail: Option<String>,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum DisplayEventKind {
/// A monitor device interface ARRIVED — a sink (re)connected as Windows sees it.
MonitorArrival,
/// A monitor device interface was REMOVED — a sink dropped as Windows sees it.
MonitorRemoval,
/// PnP device-tree churn (broadcast, no payload) — re-enumeration passed through.
DevNodesChanged,
/// A mode/topology commit reached the desktop (resolution/layout actually changed).
DisplayChange,
}
impl DisplayEventKind {
fn label(self) -> &'static str {
match self {
Self::MonitorArrival => "monitor-arrival",
Self::MonitorRemoval => "monitor-removal",
Self::DevNodesChanged => "devnodes-changed",
Self::DisplayChange => "display-change",
}
}
}
struct State {
/// Recent events, oldest-first, capped at [`RING_CAP`].
events: VecDeque<DisplayEvent>,
/// Cached CCD target inventory (see module docs for why the cache exists).
inventory: Vec<crate::win_display::TargetInventory>,
}
/// Ring depth: at the observed worst case (a probe cycle every ~2 s, ≤4 events per cycle) this
/// holds well over a minute of history — the stall correlator only ever asks about the last gap.
const RING_CAP: usize = 128;
fn state() -> &'static Mutex<State> {
static STATE: OnceLock<Mutex<State>> = OnceLock::new();
STATE.get_or_init(|| {
Mutex::new(State {
events: VecDeque::with_capacity(RING_CAP),
inventory: Vec::new(),
})
})
}
/// Start the listener thread (idempotent). Degraded-not-fatal: if window/registration creation
/// fails the ring just stays empty — the stall log then reports "listener unavailable" naturally
/// via empty summaries, and streaming is unaffected.
pub(crate) fn spawn_once() {
static ONCE: Once = Once::new();
ONCE.call_once(|| {
let spawned = std::thread::Builder::new()
.name("pf-display-events".into())
.spawn(pump);
if let Err(e) = spawned {
tracing::warn!(
error = %e,
"display-event listener thread failed to spawn — stall logs won't carry OS event attribution"
);
}
});
}
/// Events with `from <= at <= to`, oldest-first.
pub(crate) fn events_between(from: Instant, to: Instant) -> Vec<DisplayEvent> {
let st = state().lock().unwrap();
st.events
.iter()
.filter(|e| e.at >= from && e.at <= to)
.cloned()
.collect()
}
/// Compact one-line summary for log fields: `"monitor-removal x2 (DISPLAY\GSM83CD\...),
/// devnodes-changed x1"`; `"none"` when empty.
pub(crate) fn summarize(events: &[DisplayEvent]) -> String {
if events.is_empty() {
return "none".into();
}
let mut out: Vec<String> = Vec::new();
for kind in [
DisplayEventKind::MonitorArrival,
DisplayEventKind::MonitorRemoval,
DisplayEventKind::DevNodesChanged,
DisplayEventKind::DisplayChange,
] {
let hits: Vec<&DisplayEvent> = events.iter().filter(|e| e.kind == kind).collect();
if hits.is_empty() {
continue;
}
let detail = hits
.iter()
.rev()
.find_map(|e| e.detail.as_deref())
.map(|d| format!(" ({d})"))
.unwrap_or_default();
out.push(format!("{} x{}{}", kind.label(), hits.len(), detail));
}
out.join(", ")
}
/// The prime suspects for link-probe disturbances, from the cached inventory: external physical
/// displays that are CONNECTED but not part of the desktop (standby TV / input-switched monitor).
/// Rendered as `"<friendly> (<connector>)"`. Never blocks on the CCD lock.
pub(crate) fn connected_inactive_externals() -> Vec<String> {
let st = state().lock().unwrap();
st.inventory
.iter()
.filter(|t| t.external_physical && !t.active)
.map(|t| format!("{} ({})", t.friendly, t.tech))
.collect()
}
fn push_event(kind: DisplayEventKind, detail: Option<String>) {
let mut st = state().lock().unwrap();
if st.events.len() >= RING_CAP {
st.events.pop_front();
}
st.events.push_back(DisplayEvent {
at: Instant::now(),
kind,
detail,
});
}
fn refresh_inventory() {
// SAFETY: `target_inventory` only runs read-only CCD queries over locals (see its SAFETY doc);
// called from the listener thread, never the capture thread.
let inv = unsafe { crate::win_display::target_inventory() };
if !inv.is_empty() {
state().lock().unwrap().inventory = inv;
}
}
/// Inventory refresh timer: 15 s keeps the "connected-but-inactive" suspect list fresh enough for
/// a warn that rate-limits to 30 s, without measurable CCD traffic.
const INVENTORY_TIMER_MS: u32 = 15_000;
/// `DBT_DEVNODES_CHANGED` arrives as `wParam` on `WM_DEVICECHANGE` without a registration; the
/// interface arrivals/removals need the `RegisterDeviceNotificationW` below.
unsafe extern "system" fn wnd_proc(
hwnd: HWND,
msg: u32,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
match msg {
WM_DISPLAYCHANGE => {
// lParam packs the new primary resolution — worth carrying: it distinguishes a real
// mode change from a same-mode re-commit when reading a field log.
let (w, h) = (
(lparam.0 & 0xffff) as u32,
((lparam.0 >> 16) & 0xffff) as u32,
);
push_event(DisplayEventKind::DisplayChange, Some(format!("{w}x{h}")));
refresh_inventory();
LRESULT(0)
}
WM_DEVICECHANGE => {
let event = wparam.0 as u32;
if event == DBT_DEVNODES_CHANGED {
push_event(DisplayEventKind::DevNodesChanged, None);
refresh_inventory();
} else if event == DBT_DEVICEARRIVAL || event == DBT_DEVICEREMOVECOMPLETE {
let kind = if event == DBT_DEVICEARRIVAL {
DisplayEventKind::MonitorArrival
} else {
DisplayEventKind::MonitorRemoval
};
// SAFETY: for these two events lParam is documented to point at a
// DEV_BROADCAST_HDR (or be 0 — checked). We only read the header fields, and only
// reinterpret as DEV_BROADCAST_DEVICEINTERFACE_W after checking dbch_devicetype,
// reading at most dbch_size bytes — the size the sender declared.
let detail = unsafe {
let hdr = lparam.0 as *const DEV_BROADCAST_HDR;
if hdr.is_null() || (*hdr).dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE {
None
} else {
let di = hdr as *const DEV_BROADCAST_DEVICEINTERFACE_W;
let head = std::mem::offset_of!(DEV_BROADCAST_DEVICEINTERFACE_W, dbcc_name);
let bytes = ((*hdr).dbch_size as usize).saturating_sub(head);
let name = std::slice::from_raw_parts(
std::ptr::addr_of!((*di).dbcc_name).cast::<u16>(),
(bytes / 2).min(512),
);
let end = name.iter().position(|&c| c == 0).unwrap_or(name.len());
crate::monitor_devnode::instance_id_from_interface_path(
&String::from_utf16_lossy(&name[..end]),
)
}
};
push_event(kind, detail);
refresh_inventory();
}
LRESULT(0)
}
WM_TIMER => {
refresh_inventory();
LRESULT(0)
}
// SAFETY: default handling for everything else — the standard wndproc tail call.
_ => unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) },
}
}
/// The listener thread: a hidden TOP-LEVEL window (message-ONLY windows receive neither
/// `WM_DISPLAYCHANGE` nor broadcast `WM_DEVICECHANGE` — the classic pitfall) + a device-interface
/// registration for monitors + a blocking message pump. Runs for the process lifetime.
fn pump() {
refresh_inventory(); // baseline before any event arrives
let class: Vec<u16> = "pf-display-events\0".encode_utf16().collect();
// SAFETY: straight-line Win32 window bring-up on this thread. `class` outlives every use of
// its pointer (it lives to the end of the fn, the pump loops forever). All handles passed on
// are the ones the preceding calls returned; failure of any step returns out of the thread
// (degraded mode, see `spawn_once`). The DEV_BROADCAST_DEVICEINTERFACE_W filter is a fully
// initialised local read synchronously by RegisterDeviceNotificationW.
unsafe {
let Ok(hinstance) = GetModuleHandleW(None) else {
tracing::warn!(
"display-event listener: GetModuleHandleW failed — no OS event attribution"
);
return;
};
let wc = WNDCLASSW {
lpfnWndProc: Some(wnd_proc),
hInstance: hinstance.into(),
lpszClassName: PCWSTR(class.as_ptr()),
..Default::default()
};
if RegisterClassW(&wc) == 0 {
tracing::warn!(
"display-event listener: RegisterClassW failed — no OS event attribution"
);
return;
}
let hwnd = match CreateWindowExW(
WINDOW_EX_STYLE(0),
PCWSTR(class.as_ptr()),
PCWSTR(class.as_ptr()),
WS_OVERLAPPED, // hidden: never shown, never gets focus — exists only to receive broadcasts
0,
0,
0,
0,
None,
None,
Some(wc.hInstance),
None,
) {
Ok(w) => w,
Err(e) => {
tracing::warn!(error = %e, "display-event listener: CreateWindowExW failed — no OS event attribution");
return;
}
};
let filter = DEV_BROADCAST_DEVICEINTERFACE_W {
dbcc_size: std::mem::size_of::<DEV_BROADCAST_DEVICEINTERFACE_W>() as u32,
dbcc_devicetype: DBT_DEVTYP_DEVICEINTERFACE.0,
dbcc_classguid: GUID_DEVINTERFACE_MONITOR,
..Default::default()
};
if let Err(e) = RegisterDeviceNotificationW(
HANDLE(hwnd.0),
std::ptr::from_ref(&filter).cast(),
DEVICE_NOTIFY_WINDOW_HANDLE,
) {
// DBT_DEVNODES_CHANGED and WM_DISPLAYCHANGE still arrive — partial attribution.
tracing::warn!(error = %e, "display-event listener: monitor-interface registration failed — arrival/removal detail unavailable");
}
SetTimer(Some(hwnd), 1, INVENTORY_TIMER_MS, None);
tracing::debug!(
"display-event listener running (monitor hot-plug + display-change attribution)"
);
let mut msg = MSG::default();
while GetMessageW(&mut msg, None, 0, 0).as_bool() {
DispatchMessageW(&msg);
}
}
}
@@ -7,13 +7,16 @@
//! (Apollo #368's Device-Manager refresh at every hitch; our own reporter's TV where unplugging the
//! cable removes a metronomic ~4 s double-jolt) says this reaction cascade is the expensive part.
//!
//! This module disables the deactivated monitors' devnodes for the stream's duration
//! This module disables physical monitors' devnodes for the stream's duration
//! (`CM_Disable_DevNode` with `CM_DISABLE_PERSIST`, so a devnode that hot-plug re-arrives STAYS
//! disabled — that persistence is the whole point) and re-enables them at teardown before the CCD
//! restore. Selection is precise: only the monitors on targets the isolate actually deactivated,
//! mapped CCD target → monitor device interface path (`DISPLAYCONFIG_TARGET_DEVICE_NAME`) → PnP
//! instance id — never "every monitor but ours", so co-installed third-party virtual displays are
//! untouched.
//! restore. Two precise selectors, never "every monitor but ours" (co-installed third-party
//! virtual displays are untouched): [`disable_for_deactivated`] — monitors on targets the
//! `Exclusive` isolate actually deactivated, mapped CCD target → monitor device interface path
//! (`DISPLAYCONFIG_TARGET_DEVICE_NAME`) → PnP instance id; and [`disable_connected_inactive`] —
//! external physical monitors that are connected but not part of the desktop in ANY topology (the
//! standby-TV case: never active, so the first selector can't see it, yet it probes the link all
//! the same — the exact class in the field reports above).
//!
//! Crash safety: instance ids are journaled to `<config>/pnp-disabled-monitors.json` BEFORE the
//! disable and cleared after a successful re-enable; [`startup_recover`] re-enables leftovers when
@@ -62,7 +65,8 @@ fn write_journal(ids: &[String]) {
/// `\\?\DISPLAY#GSM83CD#5&367fb4cb&0&UID4352#{guid}` → `DISPLAY\GSM83CD\5&367fb4cb&0&UID4352`.
/// The standard device-interface-path → instance-id transform: strip the `\\?\` prefix and the
/// trailing `#{interface-class-guid}`, then `#` separators become `\`.
fn instance_id_from_interface_path(path: &str) -> Option<String> {
// pub(crate): `display_events` applies the same transform to DBT_DEVICEARRIVAL interface paths.
pub(crate) fn instance_id_from_interface_path(path: &str) -> Option<String> {
let rest = path.strip_prefix(r"\\?\")?;
let cut = rest.rfind("#{")?;
Some(rest[..cut].replace('#', "\\"))
@@ -156,8 +160,44 @@ pub fn disable_for_deactivated(
),
}
}
journal_and_disable(targets)
}
/// Disable the devnodes of every EXTERNAL PHYSICAL monitor that is connected but NOT part of the
/// desktop — regardless of who deactivated it. This is the standby-TV case the deactivated-set
/// selection above structurally misses: a TV that was never active has no pre-isolate active path,
/// yet its standby wake events (auto input scan, Instant-On HPD cycling) drive the same Windows
/// reaction cascade. Selection stays allowlist-precise via
/// [`crate::win_display::TargetInventory::external_physical`] — internal panels and
/// indirect/virtual targets (ours or third-party) can never be picked, and `keep_target_ids`
/// (the managed virtual set) is excluded belt-and-braces. Runs AFTER the topology action so the
/// active flags it reads are the settled ones. Journals like [`disable_for_deactivated`]; the
/// caller merges the returned ids into the same teardown list.
pub fn disable_connected_inactive(keep_target_ids: &[u32]) -> Vec<String> {
// SAFETY: `target_inventory` only runs read-only CCD queries over local buffers (see its
// docs); no borrowed memory crosses the call.
let inventory = unsafe { crate::win_display::target_inventory() };
let mut targets: Vec<(String, String)> = Vec::new();
for t in &inventory {
if t.active || !t.external_physical || keep_target_ids.contains(&t.target_id) {
continue;
}
let Some(id) = instance_id_from_interface_path(&t.monitor_device_path) else {
continue;
};
let hit = (id, format!("{} ({})", t.friendly, t.tech));
if !targets.contains(&hit) {
targets.push(hit);
}
}
journal_and_disable(targets)
}
/// Shared tail of the two selectors: crash-journal FIRST, then disable, returning what actually
/// got disabled (the teardown re-enable list).
fn journal_and_disable(targets: Vec<(String, String)>) -> Vec<String> {
if targets.is_empty() {
tracing::info!("PnP-disable: no physical monitor devnodes to disable");
tracing::debug!("PnP-disable: no physical monitor devnodes to disable");
return Vec::new();
}
// Journal FIRST (union with any outstanding ids), so a crash between here and the disable
+9 -3
View File
@@ -370,7 +370,7 @@ fn supervise(stop: HANDLE, session_ev: HANDLE) -> Result<()> {
let session = unsafe { WTSGetActiveConsoleSessionId() };
if session == 0xFFFF_FFFF {
// No interactive session yet (boot / fully logged out). Wait, but wake on stop/session.
tracing::info!("no active console session — waiting");
tracing::debug!("no active console session — waiting");
if wait_any(&[stop, session_ev], 3000) == Some(0) {
break;
}
@@ -388,7 +388,10 @@ fn supervise(stop: HANDLE, session_ev: HANDLE) -> Result<()> {
let child = match unsafe { spawn_host(session, &cmdline, &workdir, job_h) } {
Ok(child) => child,
Err(e) => {
tracing::error!("failed to launch host into session {session}: {e:#}");
tracing::error!(
session,
"failed to launch host into the active console session: {e:#}"
);
if wait_one(stop, 3000) {
break;
}
@@ -452,7 +455,10 @@ fn supervise(stop: HANDLE, session_ev: HANDLE) -> Result<()> {
_ => {
// Child exited on its own — relaunch (with a small crash-loop backoff). The `child`
// drop closes its (already-exited) handles.
tracing::warn!("host process exited — relaunching");
tracing::warn!(
pid = child.pid,
"host process exited on its own — relaunching"
);
}
}
@@ -17,10 +17,20 @@ use windows::core::PCWSTR;
use windows::Win32::Devices::Display::{
DisplayConfigGetDeviceInfo, DisplayConfigSetDeviceInfo, GetDisplayConfigBufferSizes,
QueryDisplayConfig, SetDisplayConfig, DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO,
DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME, DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE,
DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO, DISPLAYCONFIG_MODE_INFO,
DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE, DISPLAYCONFIG_PATH_INFO,
DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE, DISPLAYCONFIG_SOURCE_DEVICE_NAME, QDC_ALL_PATHS,
DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME, DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME,
DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE, DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO,
DISPLAYCONFIG_MODE_INFO, DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE,
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPONENT_VIDEO,
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPOSITE_VIDEO,
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED,
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DVI,
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HD15, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI,
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_LVDS,
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDI, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDTVDONGLE,
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SVIDEO, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EMBEDDED,
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EXTERNAL, DISPLAYCONFIG_PATH_INFO,
DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE, DISPLAYCONFIG_SOURCE_DEVICE_NAME,
DISPLAYCONFIG_TARGET_DEVICE_NAME, DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY, QDC_ALL_PATHS,
QDC_ONLY_ACTIVE_PATHS, SDC_ALLOW_CHANGES, SDC_APPLY, SDC_FORCE_MODE_ENUMERATION,
SDC_SAVE_TO_DATABASE, SDC_TOPOLOGY_EXTEND, SDC_USE_SUPPLIED_DISPLAY_CONFIG,
};
@@ -249,7 +259,7 @@ pub(crate) unsafe fn set_advanced_color(target_id: u32, enable: bool) -> bool {
s.header.id = p.targetInfo.id;
s.Anonymous.value = enable as u32; // bit 0 = enableAdvancedColor
let rc = DisplayConfigSetDeviceInfo(&s.header);
tracing::info!(
tracing::debug!(
target_id,
enable,
rc,
@@ -260,7 +270,7 @@ pub(crate) unsafe fn set_advanced_color(target_id: u32, enable: bool) -> bool {
}
tracing::warn!(
target_id,
"set_advanced_color: target not found in active paths"
"virtual-display advanced-color: target not in active paths"
);
false
}
@@ -508,6 +518,124 @@ pub(crate) unsafe fn count_other_active(keep_target_ids: &[u32]) -> Option<u32>
)
}
/// One CONNECTED display target from a full (`QDC_ALL_PATHS`) CCD sweep — the disturbance-
/// attribution inventory. `external_physical` is the load-bearing bit: a standby TV/monitor on a
/// real connector is the prime suspect for the periodic link-probe stutter class, while internal
/// panels and indirect/virtual targets (our own IDD included) are not.
pub(crate) struct TargetInventory {
pub target_id: u32,
/// Whether any active path drives this target (part of the desktop right now).
pub active: bool,
/// External physical connector (HDMI/DP/DVI/…): candidate for standby link-probe churn.
pub external_physical: bool,
/// Short connector label for logs (`"HDMI"`, `"DisplayPort"`, `"internal-panel"`, …).
pub tech: &'static str,
/// The monitor's friendly name (`"LG TV SSCR2"`); empty when the EDID carries none.
pub friendly: String,
/// Monitor device interface path — maps to the PnP instance id (`monitor_devnode`).
pub monitor_device_path: String,
}
/// Classify a CCD output technology: `(external physical?, log label)`. Allowlist, not blocklist:
/// new/unknown/indirect technologies read as non-external, so a co-installed third-party virtual
/// display can never be mistaken for a physical suspect (same precision rule as `monitor_devnode`).
fn output_tech_class(tech: DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY) -> (bool, &'static str) {
match tech {
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI => (true, "HDMI"),
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL => (true, "DisplayPort"),
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DVI => (true, "DVI"),
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HD15 => (true, "VGA"),
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EXTERNAL => (true, "UDI"),
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDI => (true, "SDI"),
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPONENT_VIDEO => (true, "component"),
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPOSITE_VIDEO => (true, "composite"),
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SVIDEO => (true, "S-Video"),
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDTVDONGLE => (true, "TV-dongle"),
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL
| DISPLAYCONFIG_OUTPUT_TECHNOLOGY_LVDS
| DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED
| DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EMBEDDED => (false, "internal-panel"),
_ => (false, "virtual/other"),
}
}
fn utf16z_str(buf: &[u16]) -> String {
let len = buf.iter().position(|&c| c == 0).unwrap_or(buf.len());
String::from_utf16_lossy(&buf[..len])
}
/// Sweep EVERY connected display target (`QDC_ALL_PATHS`, deduped from the source×target path
/// matrix to unique targets) with its name, connector class and active state. Read-only CCD; can
/// briefly serialize on the display-config lock during topology churn — callers must keep it OFF
/// the capture thread (`display_events` runs it on its own listener thread and caches).
pub(crate) unsafe fn target_inventory() -> Vec<TargetInventory> {
let mut np = 0u32;
let mut nm = 0u32;
if GetDisplayConfigBufferSizes(QDC_ALL_PATHS, &mut np, &mut nm).is_err() {
return Vec::new();
}
let mut paths = vec![DISPLAYCONFIG_PATH_INFO::default(); np as usize];
let mut modes = vec![DISPLAYCONFIG_MODE_INFO::default(); nm as usize];
if QueryDisplayConfig(
QDC_ALL_PATHS,
&mut np,
paths.as_mut_ptr(),
&mut nm,
modes.as_mut_ptr(),
None,
)
.is_err()
{
return Vec::new();
}
paths.truncate(np as usize);
// Targets driven by an ACTIVE path. `(LUID parts, target id)` keys: target ids are only
// unique per adapter.
let active: Vec<(u32, i32, u32)> = paths
.iter()
.filter(|p| p.flags & DISPLAYCONFIG_PATH_ACTIVE != 0)
.map(|p| {
(
p.targetInfo.adapterId.LowPart,
p.targetInfo.adapterId.HighPart,
p.targetInfo.id,
)
})
.collect();
let mut seen: Vec<(u32, i32, u32)> = Vec::new();
let mut out = Vec::new();
for p in &paths {
let t = &p.targetInfo;
let key = (t.adapterId.LowPart, t.adapterId.HighPart, t.id);
// `targetAvailable` == a monitor is connected; an ACTIVE target is included regardless
// (the flag reads FALSE transiently right after a removal).
if (!t.targetAvailable.as_bool() && !active.contains(&key)) || seen.contains(&key) {
continue;
}
seen.push(key);
let mut req = DISPLAYCONFIG_TARGET_DEVICE_NAME::default();
req.header.r#type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
req.header.size = size_of::<DISPLAYCONFIG_TARGET_DEVICE_NAME>() as u32;
req.header.adapterId = t.adapterId;
req.header.id = t.id;
// `req` is a properly-sized DISPLAYCONFIG_TARGET_DEVICE_NAME local whose header
// (type/size/adapterId/id) is fully initialised; the API writes only within the struct.
if DisplayConfigGetDeviceInfo(&mut req.header) != 0 {
continue; // target with no queryable monitor — nothing to attribute to
}
let (external_physical, tech) = output_tech_class(req.outputTechnology);
out.push(TargetInventory {
target_id: t.id,
active: active.contains(&key),
external_physical,
tech,
friendly: utf16z_str(&req.monitorFriendlyDeviceName),
monitor_device_path: utf16z_str(&req.monitorDevicePath),
});
}
out
}
/// Robust display isolation via the CCD API. The naive GDI approach (EnumDisplayDevices +
/// ChangeDisplaySettings) MISSES displays on a hybrid box — an iGPU-attached physical monitor isn't
/// flagged `ATTACHED_TO_DESKTOP` in the GDI enum, so it's never detached and the secure desktop /
@@ -565,7 +693,7 @@ pub(crate) unsafe fn isolate_displays_ccd(keep_target_ids: &[u32]) -> Option<Sav
tracing::warn!("display isolate (CCD): {survivors} display(s) STILL active after attempt {attempt}/4 (deactivated {others}, rc={rc:#x}) — re-querying + retrying");
std::thread::sleep(std::time::Duration::from_millis(250));
}
tracing::error!("display isolate (CCD): FAILED to isolate target set {keep_target_ids:?} after 4 attempts — a non-virtual display stayed active (the field-reported exclusive-mode bug)");
tracing::error!("display isolate (CCD): failed to isolate target set {keep_target_ids:?} after 4 attempts — a non-virtual display stayed active (field-reported exclusive-mode bug)");
Some(saved)
}
@@ -754,5 +882,9 @@ pub(crate) unsafe fn restore_displays_ccd(saved: &SavedConfig) {
Some(modes.as_slice()),
SDC_APPLY | SDC_USE_SUPPLIED_DISPLAY_CONFIG | SDC_ALLOW_CHANGES,
);
tracing::info!("display isolate (CCD): restored original topology rc={rc:#x}");
if rc == 0 {
tracing::info!("display isolate (CCD): restored original topology");
} else {
tracing::warn!("display isolate (CCD): topology restore failed rc={rc:#x} — physical displays may be left deactivated");
}
}