//! Safe-ish typed wrappers over the wdk-sys IddCx DDIs, dispatched through the `IddFunctions` table //! (indexed by `_IDDFUNCENUM::TableIndex`, with `IddDriverGlobals` as the implicit first arg) — //! the same model wdk-sys uses for WDF. Graduates the proven dispatch from `wdk-probe/src/iddcx_rt.rs` //! (M1 step 1) and adds the full DDI set the pf-vdisplay driver needs (M1 step 2 / §14). //! //! Each DDI pins its `(_IDDFUNCENUM index, PFN_* type)` pair in exactly ONE place (the macro invocation) //! — a wrong pairing is the only way the table dispatch can be UB, so it must never be expressed twice //! (port-plan unsafe_hotspot #1). All wrappers return the raw `NTSTATUS`; the caller classifies it with //! [`nt_success`] (or, for the HRESULT-shaped SwapChain DDIs, treats a nonzero as the documented retry //! code — handled at the call site in STEP 5). #![no_std] #![allow(non_snake_case, clippy::missing_safety_doc)] // P0 lint (audit §8): require explicit `unsafe {}` blocks inside `unsafe fn`s. #![deny(unsafe_op_in_unsafe_fn)] pub use wdk_sys::iddcx; use wdk_sys::{NTSTATUS, PWDFDEVICE_INIT, WDFDEVICE}; /// `NT_SUCCESS` — IddCx DDIs that return a true NTSTATUS are errors iff negative. #[inline] #[must_use] pub const fn nt_success(status: NTSTATUS) -> bool { status >= 0 } /// Read the IddCx DDI at `index` from the stub-provided `IddFunctions` table and reinterpret it as the /// concrete `PFN_*` type. `IddFunctions` is a flexible array (`[PFN_IDD_CX; 0]`) populated by IddCxStub /// once the driver is loaded. /// /// # Safety /// `index`/`T` must be the matching `_IDDFUNCENUM` index and `PFN_*` type for the same DDI, and the /// table must be populated (true after the driver is loaded by the IddCx runtime). #[inline] unsafe fn ddi(index: i32) -> T { let table = (&raw const iddcx::IddFunctions).cast::(); // SAFETY: `index` is a valid IddCx table slot; the slot holds a `PFN_*` whose layout is `T`. let slot = unsafe { table.add(index as usize) }; unsafe { slot.cast::().read() } } /// The IddCx driver globals (set by `IddCxStub`), passed as the implicit first arg to every DDI. /// /// # Safety /// Only valid once the driver is loaded by the IddCx runtime. #[inline] unsafe fn globals() -> iddcx::PIDD_DRIVER_GLOBALS { // SAFETY: we only read the pointer value of the stub-provided global. unsafe { (&raw const iddcx::IddDriverGlobals).read() } } /// Generate a typed outbound-DDI wrapper. Body is identical for every DDI (table lookup → call with the /// globals); only the `(index, PFN_*, args)` triple varies, and it appears here exactly once. macro_rules! iddcx_ddi { ( $(#[$meta:meta])* $name:ident ( $( $arg:ident : $aty:ty ),* $(,)? ) @ $idx:ident as $pfn:ident ) => { $(#[$meta])* /// /// # Safety /// Call only after the driver is loaded by IddCx; pointers must satisfy the IddCx contract. #[inline] pub unsafe fn $name( $( $arg: $aty ),* ) -> NTSTATUS { let f: iddcx::$pfn = unsafe { ddi(iddcx::_IDDFUNCENUM::$idx) }; let g = unsafe { globals() }; // SAFETY: dispatching a populated DDI with the stub globals and caller-valid args. unsafe { (f.unwrap())(g, $( $arg ),* ) } } }; // void-returning variant (e.g. IddCxAdapterSetRenderAdapter): the DDI's PFN returns `()`. ( $(#[$meta:meta])* $name:ident ( $( $arg:ident : $aty:ty ),* $(,)? ) @ $idx:ident as $pfn:ident -> () ) => { $(#[$meta])* /// /// # Safety /// Call only after the driver is loaded by IddCx; pointers must satisfy the IddCx contract. #[inline] pub unsafe fn $name( $( $arg: $aty ),* ) { let f: iddcx::$pfn = unsafe { ddi(iddcx::_IDDFUNCENUM::$idx) }; let g = unsafe { globals() }; // SAFETY: dispatching a populated DDI with the stub globals and caller-valid args. unsafe { (f.unwrap())(g, $( $arg ),* ) } } }; } iddcx_ddi!( /// Configure a `WDFDEVICE_INIT` for IddCx (call before `WdfDeviceCreate`). IddCxDeviceInitConfig(device_init: PWDFDEVICE_INIT, config: *const iddcx::IDD_CX_CLIENT_CONFIG) @ IddCxDeviceInitConfigTableIndex as PFN_IDDCXDEVICEINITCONFIG ); iddcx_ddi!( /// Finish IddCx device init (call after `WdfDeviceCreate`). IddCxDeviceInitialize(device: WDFDEVICE) @ IddCxDeviceInitializeTableIndex as PFN_IDDCXDEVICEINITIALIZE ); iddcx_ddi!( /// Create the WDDM adapter asynchronously; `out.AdapterObject` is the `IDDCX_ADAPTER`. IddCxAdapterInitAsync( in_args: *const iddcx::IDARG_IN_ADAPTER_INIT, out_args: *mut iddcx::IDARG_OUT_ADAPTER_INIT, ) @ IddCxAdapterInitAsyncTableIndex as PFN_IDDCXADAPTERINITASYNC ); iddcx_ddi!( /// Create a monitor on the adapter; `out.MonitorObject` is the `IDDCX_MONITOR`. IddCxMonitorCreate( adapter: iddcx::IDDCX_ADAPTER, in_args: *const iddcx::IDARG_IN_MONITORCREATE, out_args: *mut iddcx::IDARG_OUT_MONITORCREATE, ) @ IddCxMonitorCreateTableIndex as PFN_IDDCXMONITORCREATE ); iddcx_ddi!( /// Announce monitor arrival; `out.OsTargetId`/`out.OsAdapterLuid` come back. IddCxMonitorArrival( monitor: iddcx::IDDCX_MONITOR, out_args: *mut iddcx::IDARG_OUT_MONITORARRIVAL, ) @ IddCxMonitorArrivalTableIndex as PFN_IDDCXMONITORARRIVAL ); iddcx_ddi!( /// Depart a monitor (host disconnect / session teardown). IddCxMonitorDeparture(monitor: iddcx::IDDCX_MONITOR) @ IddCxMonitorDepartureTableIndex as PFN_IDDCXMONITORDEPARTURE ); iddcx_ddi!( /// Set the preferred render adapter (LUID) for the virtual adapter. IddCxAdapterSetRenderAdapter( adapter: iddcx::IDDCX_ADAPTER, in_args: *const iddcx::IDARG_IN_ADAPTERSETRENDERADAPTER, ) @ IddCxAdapterSetRenderAdapterTableIndex as PFN_IDDCXADAPTERSETRENDERADAPTER -> () ); iddcx_ddi!( /// Bind a D3D device to an assigned swap-chain. HRESULT-shaped (0x887A0026 → retry on monitor flap). IddCxSwapChainSetDevice( swap_chain: iddcx::IDDCX_SWAPCHAIN, in_args: *const iddcx::IDARG_IN_SWAPCHAINSETDEVICE, ) @ IddCxSwapChainSetDeviceTableIndex as PFN_IDDCXSWAPCHAINSETDEVICE ); iddcx_ddi!( /// Release the previous frame and acquire the next (HDR/FP16 variant). HRESULT-shaped; E_PENDING /// (0x8000000A) means wait on the surface-available event. IddCxSwapChainReleaseAndAcquireBuffer2( swap_chain: iddcx::IDDCX_SWAPCHAIN, in_args: *mut iddcx::IDARG_IN_RELEASEANDACQUIREBUFFER2, out_args: *mut iddcx::IDARG_OUT_RELEASEANDACQUIREBUFFER2, ) @ IddCxSwapChainReleaseAndAcquireBuffer2TableIndex as PFN_IDDCXSWAPCHAINRELEASEANDACQUIREBUFFER2 ); iddcx_ddi!( /// Signal that the acquired frame has been processed. HRESULT-shaped. IddCxSwapChainFinishedProcessingFrame(swap_chain: iddcx::IDDCX_SWAPCHAIN) @ IddCxSwapChainFinishedProcessingFrameTableIndex as PFN_IDDCXSWAPCHAINFINISHEDPROCESSINGFRAME );