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>
This commit is contained in:
Generated
+13
@@ -2860,6 +2860,18 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pf-win-display"
|
||||
version = "0.12.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"pf-paths",
|
||||
"punktfunk-core",
|
||||
"serde_json",
|
||||
"tracing",
|
||||
"windows 0.62.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pf-zerocopy"
|
||||
version = "0.12.0"
|
||||
@@ -3177,6 +3189,7 @@ dependencies = [
|
||||
"pf-gpu",
|
||||
"pf-host-config",
|
||||
"pf-paths",
|
||||
"pf-win-display",
|
||||
"pf-zerocopy",
|
||||
"pipewire",
|
||||
"punktfunk-core",
|
||||
|
||||
@@ -15,6 +15,7 @@ members = [
|
||||
"crates/pf-gpu",
|
||||
"crates/pf-zerocopy",
|
||||
"crates/pf-frame",
|
||||
"crates/pf-win-display",
|
||||
"crates/pyrowave-sys",
|
||||
"clients/probe",
|
||||
"clients/linux",
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# The Windows display-topology cluster (plan §W6): CCD/GDI path activation, mode-setting, HDR
|
||||
# advanced-colour toggles, source-rect geometry ([`win_display`]); PnP monitor devnode enable/disable
|
||||
# ([`monitor_devnode`]); and the WM_DISPLAYCHANGE / device-arrival watch ([`display_events`]). A leaf
|
||||
# so the IDD-push capturer (pf-capture) and the pf-vdisplay backend (host) depend on it as a PEER
|
||||
# instead of the capturer reaching back into the host for display utilities. Windows-only content;
|
||||
# compiles to an empty lib elsewhere.
|
||||
[package]
|
||||
name = "pf-win-display"
|
||||
version = "0.12.0"
|
||||
edition = "2021"
|
||||
rust-version.workspace = true
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "punktfunk host Windows display-topology helpers: CCD/GDI mode-set + path activation, HDR advanced colour, PnP monitor devnodes, and the display-change event watch."
|
||||
publish = false
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
# `Mode` (the negotiated display mode) is the core wire type; `pf-paths` for the pnp-disabled-monitors
|
||||
# state file.
|
||||
punktfunk-core = { path = "../punktfunk-core", features = ["quic"] }
|
||||
pf-paths = { path = "../pf-paths" }
|
||||
anyhow = "1"
|
||||
tracing = "0.1"
|
||||
# The pnp-disabled-monitors state file (a `Vec<String>` of instance ids) is serialized as JSON.
|
||||
serde_json = "1"
|
||||
windows = { version = "0.62", features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Devices_DeviceAndDriverInstallation",
|
||||
"Win32_Devices_Display",
|
||||
"Win32_Graphics_Gdi",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
"Win32_System_LibraryLoader",
|
||||
] }
|
||||
+7
-7
@@ -1,6 +1,6 @@
|
||||
//! 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
|
||||
//! The capture-stall watch (the IDD-push capturer in `pf-capture`) 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
|
||||
@@ -49,7 +49,7 @@ use windows::Win32::UI::WindowsAndMessaging::{
|
||||
|
||||
/// One OS-visible display event, timestamped at receipt.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct DisplayEvent {
|
||||
pub struct DisplayEvent {
|
||||
pub at: Instant,
|
||||
pub kind: DisplayEventKind,
|
||||
/// Monitor device instance id for arrival/removal (e.g. `DISPLAY\GSM83CD\...`), else `None`.
|
||||
@@ -57,7 +57,7 @@ pub(crate) struct DisplayEvent {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum DisplayEventKind {
|
||||
pub 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.
|
||||
@@ -103,7 +103,7 @@ fn state() -> &'static Mutex<State> {
|
||||
/// 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() {
|
||||
pub fn spawn_once() {
|
||||
static ONCE: Once = Once::new();
|
||||
ONCE.call_once(|| {
|
||||
let spawned = std::thread::Builder::new()
|
||||
@@ -119,7 +119,7 @@ pub(crate) fn spawn_once() {
|
||||
}
|
||||
|
||||
/// Events with `from <= at <= to`, oldest-first.
|
||||
pub(crate) fn events_between(from: Instant, to: Instant) -> Vec<DisplayEvent> {
|
||||
pub fn events_between(from: Instant, to: Instant) -> Vec<DisplayEvent> {
|
||||
let st = state().lock().unwrap();
|
||||
st.events
|
||||
.iter()
|
||||
@@ -130,7 +130,7 @@ pub(crate) fn events_between(from: Instant, to: Instant) -> Vec<DisplayEvent> {
|
||||
|
||||
/// 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 {
|
||||
pub fn summarize(events: &[DisplayEvent]) -> String {
|
||||
if events.is_empty() {
|
||||
return "none".into();
|
||||
}
|
||||
@@ -159,7 +159,7 @@ pub(crate) fn summarize(events: &[DisplayEvent]) -> String {
|
||||
/// 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> {
|
||||
pub fn connected_inactive_externals() -> Vec<String> {
|
||||
let st = state().lock().unwrap();
|
||||
st.inventory
|
||||
.iter()
|
||||
@@ -0,0 +1,17 @@
|
||||
//! Windows display-topology helpers (plan §W6), extracted from the host's `windows/{win_display,
|
||||
//! monitor_devnode,display_events}.rs` so the IDD-push capturer (`pf-capture`) and the pf-vdisplay
|
||||
//! backend (the host) depend on them as a leaf PEER instead of the capturer reaching back into the
|
||||
//! orchestrator. Windows-only; compiles to an empty lib elsewhere.
|
||||
//!
|
||||
//! - [`win_display`]: CCD/GDI path activation, mode-setting, HDR advanced-colour toggles, and the
|
||||
//! source-desktop geometry the capturer duplicates.
|
||||
//! - [`monitor_devnode`]: PnP monitor devnode enable/disable (the parallel-display isolation lever).
|
||||
//! - [`display_events`]: the `WM_DISPLAYCHANGE` / device-arrival watch that lets a capture stall say
|
||||
//! whether an OS display event coincided with it.
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod display_events;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod monitor_devnode;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod win_display;
|
||||
+1
-1
@@ -66,7 +66,7 @@ fn write_journal(ids: &[String]) {
|
||||
/// The standard device-interface-path → instance-id transform: strip the `\\?\` prefix and the
|
||||
/// trailing `#{interface-class-guid}`, then `#` separators become `\`.
|
||||
// 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> {
|
||||
pub fn instance_id_from_interface_path(path: &str) -> Option<String> {
|
||||
let rest = path.strip_prefix(r"\\?\")?;
|
||||
let cut = rest.rfind("#{")?;
|
||||
Some(rest[..cut].replace('#', "\\"))
|
||||
+27
-21
@@ -10,6 +10,12 @@
|
||||
|
||||
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
|
||||
#![deny(clippy::undocumented_unsafe_blocks)]
|
||||
// These CCD/GDI FFI helpers were `pub(crate) unsafe fn` before the pf-win-display carve (plan §W6),
|
||||
// where `missing_safety_doc` stays silent; crossing the crate boundary makes them `pub` and would
|
||||
// demand a `# Safety` heading on each. Their callers' obligations (call on the right desktop thread
|
||||
// with a live OS target id) are stated in each fn's prose doc, and this is an internal
|
||||
// (publish=false) leaf — keep the pre-carve behavior rather than adding 12 formal headings.
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
|
||||
use std::mem::size_of;
|
||||
|
||||
@@ -41,7 +47,7 @@ use windows::Win32::Graphics::Gdi::{
|
||||
ENUM_CURRENT_SETTINGS, ENUM_DISPLAY_SETTINGS_MODE,
|
||||
};
|
||||
|
||||
use crate::vdisplay::Mode;
|
||||
use punktfunk_core::Mode;
|
||||
|
||||
/// Force the desktop into EXTEND topology - the programmatic equivalent of the Win+P / DisplaySwitch
|
||||
/// "Extend" shortcut. Windows defaults a FRESHLY-ADDED monitor into CLONE/duplicate mode when a
|
||||
@@ -52,7 +58,7 @@ use crate::vdisplay::Mode;
|
||||
/// OWN active path, so the rest of bring-up (`resolve_gdi_name` -> `set_active_mode` ->
|
||||
/// `isolate_displays_ccd`) proceeds. Best-effort + idempotent: a no-op on a single-display (already
|
||||
/// sole/extended) box, so it is safe to call unconditionally. `rc == 0` is success.
|
||||
pub(crate) unsafe fn force_extend_topology() {
|
||||
pub unsafe fn force_extend_topology() {
|
||||
// A topology flag with no supplied path/mode arrays tells the OS to recompute + apply that preset
|
||||
// for the currently-connected displays (the same code path DisplaySwitch.exe drives).
|
||||
let rc = SetDisplayConfig(None, None, SDC_APPLY | SDC_TOPOLOGY_EXTEND);
|
||||
@@ -78,7 +84,7 @@ pub(crate) unsafe fn force_extend_topology() {
|
||||
/// source not already driving another display — mode indices invalidated so `SDC_ALLOW_CHANGES` lets
|
||||
/// the OS pick modes for the new path. Returns `true` when the apply reports success; the caller
|
||||
/// still re-polls [`resolve_gdi_name`] to confirm the path actually committed.
|
||||
pub(crate) unsafe fn activate_target_path(target_id: u32) -> bool {
|
||||
pub unsafe fn activate_target_path(target_id: u32) -> bool {
|
||||
let mut np = 0u32;
|
||||
let mut nm = 0u32;
|
||||
if GetDisplayConfigBufferSizes(QDC_ALL_PATHS, &mut np, &mut nm).is_err() {
|
||||
@@ -169,7 +175,7 @@ pub(crate) unsafe fn activate_target_path(target_id: u32) -> bool {
|
||||
/// Resolve the `\\.\DisplayN` GDI name for a virtual-display target id via the CCD API. Returns `None`
|
||||
/// until the OS activates the target into the desktop topology (needs a real WDDM GPU; on a
|
||||
/// GPU-less box this stays `None` even though ADD succeeded).
|
||||
pub(crate) unsafe fn resolve_gdi_name(target_id: u32) -> Option<String> {
|
||||
pub unsafe fn resolve_gdi_name(target_id: u32) -> Option<String> {
|
||||
let mut np = 0u32;
|
||||
let mut nm = 0u32;
|
||||
if GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &mut np, &mut nm).is_err() {
|
||||
@@ -212,7 +218,7 @@ pub(crate) unsafe fn resolve_gdi_name(target_id: u32) -> Option<String> {
|
||||
///
|
||||
/// # Safety
|
||||
/// Calls the GDI/CCD APIs; safe to call from any thread.
|
||||
pub(crate) unsafe fn active_resolution(target_id: u32) -> Option<(u32, u32)> {
|
||||
pub unsafe fn active_resolution(target_id: u32) -> Option<(u32, u32)> {
|
||||
let gdi = resolve_gdi_name(target_id)?;
|
||||
let wname: Vec<u16> = gdi.encode_utf16().chain(std::iter::once(0)).collect();
|
||||
let mut dm = DEVMODEW {
|
||||
@@ -230,7 +236,7 @@ pub(crate) unsafe fn active_resolution(target_id: u32) -> Option<(u32, u32)> {
|
||||
/// secure (Winlogon) desktop makes it render SDR/composed so DXGI Desktop Duplication can capture it
|
||||
/// (the HDR fullscreen independent-flip otherwise storms `ACCESS_LOST` → black); re-enable on return so
|
||||
/// WGC keeps HDR on the normal desktop. Returns true on a successful `DisplayConfigSetDeviceInfo`.
|
||||
pub(crate) unsafe fn set_advanced_color(target_id: u32, enable: bool) -> bool {
|
||||
pub unsafe fn set_advanced_color(target_id: u32, enable: bool) -> bool {
|
||||
let mut np = 0u32;
|
||||
let mut nm = 0u32;
|
||||
if GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &mut np, &mut nm).is_err() {
|
||||
@@ -283,7 +289,7 @@ pub(crate) unsafe fn set_advanced_color(target_id: u32, enable: bool) -> bool {
|
||||
/// list (both happen transiently during a display-topology re-probe): the caller decides the fallback —
|
||||
/// the capture loop's poller keeps the last known value, since reading a blip as "HDR off" used to cost
|
||||
/// an HDR session TWO spurious ring recreates (false, then true again a poll later).
|
||||
pub(crate) unsafe fn advanced_color_enabled(target_id: u32) -> Option<bool> {
|
||||
pub unsafe fn advanced_color_enabled(target_id: u32) -> Option<bool> {
|
||||
let mut np = 0u32;
|
||||
let mut nm = 0u32;
|
||||
if GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &mut np, &mut nm).is_err() {
|
||||
@@ -324,9 +330,9 @@ pub(crate) unsafe fn advanced_color_enabled(target_id: u32) -> Option<bool> {
|
||||
/// ADVERTISES the mode; Windows otherwise activates an IDD target at a 1280x720 default, so the
|
||||
/// ACTIVE mode (what DXGI Desktop Duplication captures) must be set explicitly. CDS_TEST first so a
|
||||
/// mode the driver didn't advertise just leaves the default instead of erroring the session.
|
||||
// pub(crate) so vdisplay::pf_vdisplay can reuse this backend-neutral CCD/GDI mode-set helper
|
||||
// pub so vdisplay::pf_vdisplay can reuse this backend-neutral CCD/GDI mode-set helper
|
||||
// (a pf-vdisplay monitor's GDI name is a real OS device name, so it works unchanged).
|
||||
pub(crate) fn set_active_mode(gdi_name: &str, mode: Mode) {
|
||||
pub fn set_active_mode(gdi_name: &str, mode: Mode) {
|
||||
let wname: Vec<u16> = gdi_name.encode_utf16().chain(std::iter::once(0)).collect();
|
||||
|
||||
// Enumerate the modes the driver actually advertises for this output and pick the best match for
|
||||
@@ -461,8 +467,8 @@ pub(crate) fn set_active_mode(gdi_name: &str, mode: Mode) {
|
||||
}
|
||||
|
||||
/// Saved active display topology, for restoring on teardown.
|
||||
// pub(crate) so vdisplay::pf_vdisplay's Monitor can hold the same saved-topology type.
|
||||
pub(crate) type SavedConfig = (Vec<DISPLAYCONFIG_PATH_INFO>, Vec<DISPLAYCONFIG_MODE_INFO>);
|
||||
// pub so vdisplay::pf_vdisplay's Monitor can hold the same saved-topology type.
|
||||
pub type SavedConfig = (Vec<DISPLAYCONFIG_PATH_INFO>, Vec<DISPLAYCONFIG_MODE_INFO>);
|
||||
|
||||
/// `DISPLAYCONFIG_PATH_ACTIVE` (wingdi.h) — the `flags` bit marking a path active. The `windows` crate
|
||||
/// doesn't export it, so define it here.
|
||||
@@ -505,7 +511,7 @@ unsafe fn query_active_config() -> Option<SavedConfig> {
|
||||
/// that would still be lit besides the managed virtual set. `None` on query failure. Used to VERIFY
|
||||
/// isolation actually took, and (in the `primary` topology) to detect a physical that is ALREADY
|
||||
/// active so we can skip a force-EXTEND that would reset its refresh.
|
||||
pub(crate) unsafe fn count_other_active(keep_target_ids: &[u32]) -> Option<u32> {
|
||||
pub unsafe fn count_other_active(keep_target_ids: &[u32]) -> Option<u32> {
|
||||
let (paths, _) = query_active_config()?;
|
||||
Some(
|
||||
paths
|
||||
@@ -522,7 +528,7 @@ pub(crate) unsafe fn count_other_active(keep_target_ids: &[u32]) -> Option<u32>
|
||||
/// 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 struct TargetInventory {
|
||||
pub target_id: u32,
|
||||
/// Whether any active path drives this target (part of the desktop right now).
|
||||
pub active: bool,
|
||||
@@ -568,7 +574,7 @@ fn utf16z_str(buf: &[u16]) -> String {
|
||||
/// 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> {
|
||||
pub 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() {
|
||||
@@ -647,9 +653,9 @@ pub(crate) unsafe fn target_inventory() -> Vec<TargetInventory> {
|
||||
/// Re-issued with the grown/shrunk set on each slot add/remove while the group lives; the FIRST call's
|
||||
/// returned config is what teardown restores (the caller keeps it on the group record and discards
|
||||
/// later returns). Returns the original active config to restore on teardown.
|
||||
// pub(crate) so vdisplay::pf_vdisplay can reuse this backend-neutral CCD isolation helper
|
||||
// pub so vdisplay::pf_vdisplay can reuse this backend-neutral CCD isolation helper
|
||||
// (it operates on real OS target ids — a pf-vdisplay monitor's target_id qualifies).
|
||||
pub(crate) unsafe fn isolate_displays_ccd(keep_target_ids: &[u32]) -> Option<SavedConfig> {
|
||||
pub unsafe fn isolate_displays_ccd(keep_target_ids: &[u32]) -> Option<SavedConfig> {
|
||||
// Snapshot the ORIGINAL active config ONCE for restore-on-teardown, before any changes.
|
||||
let saved = query_active_config()?;
|
||||
|
||||
@@ -702,7 +708,7 @@ pub(crate) unsafe fn isolate_displays_ccd(keep_target_ids: &[u32]) -> Option<Sav
|
||||
/// Used by the IDD-push compose kick to dirty THE TARGET display: with parallel displays the
|
||||
/// cursor sits on ONE of them, and a cursor wiggle only dirties that one — a sibling display's
|
||||
/// kick must first know where to send the cursor (Stage W3 on-glass finding).
|
||||
pub(crate) unsafe fn source_desktop_rect(target_id: u32) -> Option<(i32, i32, i32, i32)> {
|
||||
pub unsafe fn source_desktop_rect(target_id: u32) -> Option<(i32, i32, i32, i32)> {
|
||||
let (paths, modes) = query_active_config()?;
|
||||
for p in &paths {
|
||||
if p.targetInfo.id != target_id || p.flags & DISPLAYCONFIG_PATH_ACTIVE == 0 {
|
||||
@@ -730,7 +736,7 @@ pub(crate) unsafe fn source_desktop_rect(target_id: u32) -> Option<(i32, i32, i3
|
||||
/// treats the source at `(0,0)` as primary, so auto-row's first member lands primary — the group's
|
||||
/// designated member. Paths not named stay where they are. Best-effort: a failure leaves the OS
|
||||
/// placement (mouse crossing may not match the layout table until the next apply).
|
||||
pub(crate) unsafe fn apply_source_positions(positions: &[(u32, i32, i32)]) {
|
||||
pub unsafe fn apply_source_positions(positions: &[(u32, i32, i32)]) {
|
||||
if positions.len() < 2 {
|
||||
return; // a single (or no) member sits at the origin — nothing to arrange
|
||||
}
|
||||
@@ -789,7 +795,7 @@ pub(crate) unsafe fn apply_source_positions(positions: &[(u32, i32, i32)]) {
|
||||
/// atomic CCD `SetDisplayConfig` (NOT GDI `CDS_SET_PRIMARY`, which storms
|
||||
/// `DXGI_ERROR_MODE_CHANGE_IN_PROGRESS` when another display is live — see [`set_active_mode`]).
|
||||
/// Returns the original config to restore on teardown.
|
||||
pub(crate) unsafe fn set_virtual_primary_ccd(keep_target_id: u32) -> Option<SavedConfig> {
|
||||
pub unsafe fn set_virtual_primary_ccd(keep_target_id: u32) -> Option<SavedConfig> {
|
||||
let mut np = 0u32;
|
||||
let mut nm = 0u32;
|
||||
if GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &mut np, &mut nm).is_err() {
|
||||
@@ -871,8 +877,8 @@ pub(crate) unsafe fn set_virtual_primary_ccd(keep_target_id: u32) -> Option<Save
|
||||
|
||||
/// Restore the topology saved by [`isolate_displays_ccd`] (teardown, before the virtual output is
|
||||
/// removed), re-activating the displays we deactivated.
|
||||
// pub(crate) so vdisplay::pf_vdisplay can reuse this backend-neutral CCD restore helper.
|
||||
pub(crate) unsafe fn restore_displays_ccd(saved: &SavedConfig) {
|
||||
// pub so vdisplay::pf_vdisplay can reuse this backend-neutral CCD restore helper.
|
||||
pub unsafe fn restore_displays_ccd(saved: &SavedConfig) {
|
||||
let (paths, modes) = saved;
|
||||
if paths.is_empty() {
|
||||
return;
|
||||
@@ -22,6 +22,9 @@ pf-zerocopy = { path = "../pf-zerocopy" }
|
||||
# Shared frame/format vocabulary (CapturedFrame/PixelFormat/…), HDR metadata, thread QoS, and the
|
||||
# Windows DXGI capture identity — the leaf both capture and encode speak (plan §W6).
|
||||
pf-frame = { path = "../pf-frame" }
|
||||
# Windows display-topology helpers (CCD/GDI mode-set, PnP monitor devnodes, display-change watch),
|
||||
# extracted to a leaf crate (plan §W6). Empty on non-Windows, so it lives in the main deps.
|
||||
pf-win-display = { path = "../pf-win-display" }
|
||||
# M3 native control plane (the `punktfunk/1` QUIC handshake; data plane stays native-thread UDP).
|
||||
quinn = "0.11"
|
||||
anyhow = "1"
|
||||
|
||||
@@ -32,9 +32,6 @@ mod crash;
|
||||
#[cfg(target_os = "windows")]
|
||||
#[path = "windows/ddc.rs"]
|
||||
mod ddc;
|
||||
#[cfg(target_os = "windows")]
|
||||
#[path = "windows/display_events.rs"]
|
||||
mod display_events;
|
||||
#[cfg(target_os = "linux")]
|
||||
#[path = "linux/drm_sync.rs"]
|
||||
mod drm_sync;
|
||||
@@ -56,9 +53,6 @@ mod library;
|
||||
mod log_capture;
|
||||
mod mgmt;
|
||||
mod mgmt_token;
|
||||
#[cfg(target_os = "windows")]
|
||||
#[path = "windows/monitor_devnode.rs"]
|
||||
mod monitor_devnode;
|
||||
mod native;
|
||||
mod native_pairing;
|
||||
mod pipeline;
|
||||
@@ -76,9 +70,11 @@ mod vdisplay;
|
||||
#[cfg(target_os = "windows")]
|
||||
#[path = "windows/win_adapter.rs"]
|
||||
mod win_adapter;
|
||||
// The Windows display-topology cluster (CCD/GDI mode-set, PnP monitor devnodes, the display-change
|
||||
// watch) lives in the `pf-win-display` leaf crate (plan §W6); import the modules at the crate root
|
||||
// so every existing `crate::{win_display,monitor_devnode,display_events}::*` path stays valid.
|
||||
#[cfg(target_os = "windows")]
|
||||
#[path = "windows/win_display.rs"]
|
||||
mod win_display;
|
||||
use pf_win_display::{display_events, monitor_devnode, win_display};
|
||||
// The zero-copy GPU plumbing lives in the `pf-zerocopy` leaf crate (plan §W6); this shim keeps
|
||||
// every existing `crate::zerocopy::*` path valid. `drm_fourcc` consumes the frame vocabulary, so
|
||||
// it sits with `capture` and is re-exported here for its old callers.
|
||||
|
||||
Reference in New Issue
Block a user