feat(windows): pf-vdisplay — all-Rust IddCx virtual display (replaces SudoVDA)
P1 done: a pure-Rust UMDF2 IddCx driver, drop-in compatible with the host's
existing vdisplay/sudovda.rs control plane (the {e5bcc234} interface + the
SudoVDA IOCTL ABI), so the host drives it unchanged. Validated streaming on
glass at 5120x1440@240 — steady 240 fps, ~2.4 ms encode, clean teardown, full
parity with SudoVDA.
- Vendored wdf-umdf-sys / wdf-umdf bindgen crates (MIT, from virtual-display-rs)
+ the SDK-version build.rs fix that resolves the IddCxStub lib path by the WDK
version actually containing um\x64\iddcx, not the max base SDK.
- pf-vdisplay crate: entry/callbacks/context/control/monitor/edid/
swap_chain_processor. Our OWN 128-byte EDID (manufacturer PNK, product
punktfunk — no SudoVDA bytes), a real swap-chain drain (faithful vdd port,
required so DWM keeps compositing), the SudoVDA-compatible IOCTL control plane
(ADD/REMOVE/PING/GET_WATCHDOG/GET_VERSION/SET_RENDER_ADAPTER) + a watchdog that
tears down orphaned monitors when the host stops pinging.
- deploy-dev.ps1: stage + sign + stampinf (date.time DriverVer) + Inf2Cat +
install, codifying the "bump DriverVer or pnputil keeps the old binary" gotcha.
- docs/windows-virtual-display-rust-port.md: investigation, the on-glass
validation, and the two traps that cost time (Session-0 measurement +
accumulated device-state needing a reboot).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "wdf-umdf-sys"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
paste = "1.0.15"
|
||||
bytemuck = "1.19.0"
|
||||
thiserror = "2.0.3"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.70.1"
|
||||
thiserror = "2.0.3"
|
||||
winreg = "0.52.0"
|
||||
@@ -0,0 +1,275 @@
|
||||
use std::env;
|
||||
use std::fmt::{self, Display};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use bindgen::Abi;
|
||||
use winreg::enums::HKEY_LOCAL_MACHINE;
|
||||
use winreg::RegKey;
|
||||
|
||||
const UMDF_V: &str = "2.31";
|
||||
const IDDCX_V: &str = "1.4";
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
enum Error {
|
||||
#[error(transparent)]
|
||||
IoError(#[from] std::io::Error),
|
||||
#[error("cannot find the directory")]
|
||||
DirectoryNotFound,
|
||||
}
|
||||
|
||||
/// Retrieves the path to the Windows Kits directory. The default should be
|
||||
/// `C:\Program Files (x86)\Windows Kits\10`.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns IO error if failed
|
||||
fn get_windows_kits_dir() -> Result<PathBuf, Error> {
|
||||
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
|
||||
let key = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots";
|
||||
let dir: String = hklm.open_subkey(key)?.get_value("KitsRoot10")?;
|
||||
|
||||
Ok(dir.into())
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum DirectoryType {
|
||||
Include,
|
||||
Library,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum Target {
|
||||
X86_64,
|
||||
ARM64,
|
||||
}
|
||||
|
||||
impl Default for Target {
|
||||
fn default() -> Self {
|
||||
let target = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||
match &*target {
|
||||
"x86_64" => Self::X86_64,
|
||||
"aarch64" => Self::ARM64,
|
||||
_ => unimplemented!("{target} arch is unsupported"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Target {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Target::X86_64 => f.write_str("x64"),
|
||||
Target::ARM64 => f.write_str("arm64"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_base_path<S: AsRef<Path>>(dir_type: DirectoryType, subs: &[S]) -> Result<PathBuf, Error> {
|
||||
let mut dir = get_windows_kits_dir()?.join(match dir_type {
|
||||
DirectoryType::Include => "Include",
|
||||
DirectoryType::Library => "Lib",
|
||||
});
|
||||
|
||||
dir.extend(subs);
|
||||
if !dir.is_dir() {
|
||||
return Err(Error::DirectoryNotFound);
|
||||
}
|
||||
|
||||
Ok(dir)
|
||||
}
|
||||
|
||||
fn get_sdk_path<S: AsRef<Path>>(dir_type: DirectoryType, subs: &[S]) -> Result<PathBuf, Error> {
|
||||
// We first append lib to the path and read the directory..
|
||||
let dir = get_windows_kits_dir()?
|
||||
.join(match dir_type {
|
||||
DirectoryType::Include => "Include",
|
||||
DirectoryType::Library => "Lib",
|
||||
})
|
||||
.read_dir()?;
|
||||
|
||||
// In the lib directory we may have one or more directories named after the version of Windows,
|
||||
// we will be looking for the highest version number.
|
||||
let mut dir = dir
|
||||
.filter_map(Result::ok)
|
||||
.map(|dir| dir.path())
|
||||
.filter(|dir| {
|
||||
let is_sdk = dir
|
||||
.components()
|
||||
.last()
|
||||
.and_then(|c| c.as_os_str().to_str())
|
||||
.map_or(false, |c| c.starts_with("10."));
|
||||
|
||||
let mut sub_dir = dir.clone();
|
||||
sub_dir.extend(subs);
|
||||
|
||||
is_sdk && sub_dir.is_dir()
|
||||
})
|
||||
.max()
|
||||
.ok_or_else(|| Error::DirectoryNotFound)?;
|
||||
|
||||
dir.extend(subs);
|
||||
if !dir.is_dir() {
|
||||
return Err(Error::DirectoryNotFound);
|
||||
}
|
||||
|
||||
// Finally append um to the path to get the path to the user mode libraries.
|
||||
Ok(dir)
|
||||
}
|
||||
|
||||
/// Retrieves the path to the user mode libraries. The path may look something like:
|
||||
/// `C:\Program Files (x86)\Windows Kits\10\lib\10.0.18362.0\um`.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns IO error if failed
|
||||
fn get_um_dir(dir_type: DirectoryType) -> Result<PathBuf, Error> {
|
||||
let target = Target::default().to_string();
|
||||
|
||||
let binding = &["um", &target];
|
||||
let subs: &[&str] = match dir_type {
|
||||
DirectoryType::Include => &["um"],
|
||||
DirectoryType::Library => binding,
|
||||
};
|
||||
|
||||
let dir = get_sdk_path(dir_type, subs)?;
|
||||
Ok(dir)
|
||||
}
|
||||
|
||||
/// # Errors
|
||||
/// Returns IO error if failed
|
||||
fn get_umdf_dir(dir_type: DirectoryType) -> Result<PathBuf, Error> {
|
||||
match dir_type {
|
||||
DirectoryType::Include => get_base_path(dir_type, &["wdf", "umdf", UMDF_V]),
|
||||
DirectoryType::Library => get_base_path(
|
||||
dir_type,
|
||||
&["wdf", "umdf", &Target::default().to_string(), UMDF_V],
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves the path to the shared headers. The path may look something like:
|
||||
/// `C:\Program Files (x86)\Windows Kits\10\lib\10.0.18362.0\shared`.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns IO error if failed
|
||||
fn get_shared_dir() -> Result<PathBuf, Error> {
|
||||
let dir = get_sdk_path(DirectoryType::Include, &["shared"])?;
|
||||
Ok(dir)
|
||||
}
|
||||
|
||||
fn build_dir() -> PathBuf {
|
||||
PathBuf::from(
|
||||
std::env::var_os("OUT_DIR").expect("the environment variable OUT_DIR is undefined"),
|
||||
)
|
||||
}
|
||||
|
||||
fn generate() {
|
||||
// Find the include directory containing the user headers.
|
||||
let include_um_dir = get_um_dir(DirectoryType::Include).unwrap();
|
||||
let lib_um_dir = get_um_dir(DirectoryType::Library).unwrap();
|
||||
let shared = get_shared_dir().unwrap();
|
||||
|
||||
println!("cargo:rustc-link-search={}", lib_um_dir.display());
|
||||
|
||||
// Tell Cargo to re-run this if src/wrapper.h gets changed.
|
||||
println!("cargo:rerun-if-changed=c/wrapper.h");
|
||||
|
||||
//
|
||||
// UMDF
|
||||
//
|
||||
|
||||
let umdf_lib_dir = get_umdf_dir(DirectoryType::Library).unwrap();
|
||||
|
||||
println!("cargo:rustc-link-search={}", umdf_lib_dir.display());
|
||||
|
||||
let wdf_include_dir = get_umdf_dir(DirectoryType::Include).unwrap();
|
||||
|
||||
// need to link to umdf lib
|
||||
println!("cargo:rustc-link-lib=static=WdfDriverStubUm");
|
||||
|
||||
//
|
||||
// IDDCX
|
||||
//
|
||||
|
||||
// The IddCx import lib lives only under the WDK's SDK version (e.g. 10.0.26100.0); a newer base
|
||||
// SDK installed alongside it (e.g. 10.0.28000.0) has um\x64 but no iddcx subdir, so picking the
|
||||
// max um\x64 version (lib_um_dir) misses it. Resolve by the version that actually contains
|
||||
// iddcx — the same way the IddCx.h header path is resolved below.
|
||||
let iddcx_lib_dir = get_sdk_path(
|
||||
DirectoryType::Library,
|
||||
&["um", &Target::default().to_string(), "iddcx", IDDCX_V],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
println!("cargo:rustc-link-search={}", iddcx_lib_dir.display());
|
||||
|
||||
// need to link to iddcx lib
|
||||
println!("cargo:rustc-link-lib=static=IddCxStub");
|
||||
|
||||
//
|
||||
// REST
|
||||
//
|
||||
|
||||
// Get the build directory.
|
||||
let out_path = build_dir();
|
||||
|
||||
// Generate the bindings
|
||||
let mut builder = bindgen::Builder::default()
|
||||
.derive_debug(false)
|
||||
.layout_tests(false)
|
||||
.default_enum_style(bindgen::EnumVariation::NewType {
|
||||
is_bitfield: false,
|
||||
is_global: false,
|
||||
})
|
||||
.merge_extern_blocks(true)
|
||||
.header("c/wrapper.h")
|
||||
.header(
|
||||
get_sdk_path(DirectoryType::Include, &["um", "iddcx", IDDCX_V])
|
||||
.unwrap()
|
||||
.join("IddCx.h")
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
)
|
||||
// general um includes
|
||||
.clang_arg(format!("-I{}", include_um_dir.display()))
|
||||
// umdf includes
|
||||
.clang_arg(format!("-I{}", wdf_include_dir.display()))
|
||||
.clang_arg(format!("-I{}", shared.display()))
|
||||
// because aarch64 needs to find excpt.h
|
||||
.clang_arg(format!(
|
||||
"-I{}",
|
||||
get_sdk_path(DirectoryType::Include, &["km", "crt"])
|
||||
.unwrap()
|
||||
.display()
|
||||
))
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
||||
.blocklist_type("_?P?IMAGE_TLS_DIRECTORY.*")
|
||||
// we will use our own custom type
|
||||
.blocklist_item("NTSTATUS")
|
||||
.blocklist_item("IddMinimumVersionRequired")
|
||||
.blocklist_item("WdfMinimumVersionRequired")
|
||||
.clang_arg("--language=c++")
|
||||
.clang_arg("-fms-compatibility")
|
||||
.clang_arg("-fms-extensions")
|
||||
.override_abi(Abi::CUnwind, ".*")
|
||||
.generate_cstr(true)
|
||||
.derive_default(true);
|
||||
|
||||
let defines = match Target::default() {
|
||||
Target::X86_64 => ["AMD64", "_AMD64_"],
|
||||
Target::ARM64 => ["ARM64", "_ARM64_"],
|
||||
};
|
||||
|
||||
for define in defines {
|
||||
builder = builder.clang_arg(format!("-D{define}"));
|
||||
}
|
||||
|
||||
// generate
|
||||
let umdf = builder.generate().unwrap();
|
||||
|
||||
// Write the bindings to the $OUT_DIR/bindings.rs file.
|
||||
umdf.write_to_file(out_path.join("umdf.rs")).unwrap();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
generate();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <Windows.h>
|
||||
|
||||
/**
|
||||
*
|
||||
* UMDF
|
||||
*
|
||||
*/
|
||||
|
||||
#define WDF_STUB
|
||||
|
||||
#include <wdf.h>
|
||||
|
||||
/**
|
||||
*
|
||||
* IDCXX
|
||||
*
|
||||
*/
|
||||
|
||||
#define IDD_STUB
|
||||
|
||||
// handled in build.rs
|
||||
// #include <iddcx\1.4\IddCx.h>
|
||||
@@ -0,0 +1,17 @@
|
||||
#![allow(unsafe_op_in_unsafe_fn)]
|
||||
#![allow(clippy::all)]
|
||||
#![allow(clippy::pedantic)]
|
||||
#![allow(clippy::restriction)]
|
||||
|
||||
// stand-in type replacing NTSTATUS in the bindings
|
||||
use crate::NTSTATUS;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/umdf.rs"));
|
||||
|
||||
// required for some macros
|
||||
unsafe impl Send for _WDF_OBJECT_CONTEXT_TYPE_INFO {}
|
||||
unsafe impl Sync for _WDF_OBJECT_CONTEXT_TYPE_INFO {}
|
||||
|
||||
// fails to build without this symbol
|
||||
#[no_mangle]
|
||||
pub static IddMinimumVersionRequired: ULONG = 4;
|
||||
@@ -0,0 +1,211 @@
|
||||
#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals, unused)]
|
||||
|
||||
mod bindings;
|
||||
mod ntstatus;
|
||||
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
pub use bindings::*;
|
||||
pub use ntstatus::*;
|
||||
pub use paste::paste;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! WdfIsFunctionAvailable {
|
||||
($name:ident) => {{
|
||||
// SAFETY: We only ever do read access
|
||||
let higher = unsafe { $crate::WdfClientVersionHigherThanFramework } != 0;
|
||||
// SAFETY: We only ever do read access
|
||||
let fn_count = unsafe { $crate::WdfFunctionCount };
|
||||
|
||||
// https://github.com/microsoft/Windows-Driver-Frameworks/blob/main/src/publicinc/wdf/umdf/2.33/wdffuncenum.h#L126
|
||||
$crate::paste! {
|
||||
// index is always positive, see
|
||||
// https://github.com/microsoft/Windows-Driver-Frameworks/blob/main/src/publicinc/wdf/umdf/2.33/wdffuncenum.h
|
||||
const FN_INDEX: u32 = $crate::WDFFUNCENUM::[<$name TableIndex>].0 as u32;
|
||||
|
||||
FN_INDEX < $crate::WDF_ALWAYS_AVAILABLE_FUNCTION_COUNT
|
||||
|| !higher || FN_INDEX < fn_count
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! WdfIsStructureAvailable {
|
||||
($name:ident) => {{
|
||||
// SAFETY: We only ever do read access
|
||||
let higher = unsafe { $crate::WdfClientVersionHigherThanFramework } != 0;
|
||||
// SAFETY: We only ever do read access
|
||||
let struct_count = unsafe { $crate::WdfStructureCount };
|
||||
|
||||
// https://github.com/microsoft/Windows-Driver-Frameworks/blob/main/src/publicinc/wdf/umdf/2.33/wdffuncenum.h#L141
|
||||
$crate::paste! {
|
||||
// index is always positive, see
|
||||
// https://github.com/microsoft/Windows-Driver-Frameworks/blob/main/src/publicinc/wdf/umdf/2.33/wdffuncenum.h
|
||||
const STRUCT_INDEX: u32 = $crate::WDFSTRUCTENUM::[<INDEX_ $name>].0 as u32;
|
||||
|
||||
!higher || STRUCT_INDEX < struct_count
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! IddCxIsFunctionAvailable {
|
||||
($name:ident) => {{
|
||||
// SAFETY: We only ever do read access
|
||||
let higher = unsafe { $crate::IddClientVersionHigherThanFramework } != 0;
|
||||
// SAFETY: We only ever do read access
|
||||
let fn_count = unsafe { $crate::IddFunctionCount };
|
||||
|
||||
$crate::paste! {
|
||||
const FN_INDEX: u32 = $crate::IDDFUNCENUM::[<$name TableIndex>].0 as u32;
|
||||
|
||||
FN_INDEX < $crate::IDD_ALWAYS_AVAILABLE_FUNCTION_COUNT
|
||||
|| !higher || FN_INDEX < fn_count
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! IddCxIsStructureAvailable {
|
||||
($name:ident) => {{
|
||||
// SAFETY: We only ever do read access
|
||||
let higher = unsafe { $crate::IddClientVersionHigherThanFramework } != 0;
|
||||
// SAFETY: We only ever do read access
|
||||
let struct_count = unsafe { $crate::IddStructureCount };
|
||||
|
||||
$crate::paste! {
|
||||
const STRUCT_INDEX: u32 = $crate::IDDSTRUCTENUM::[<INDEX_ $name>].0 as u32;
|
||||
|
||||
!higher || STRUCT_INDEX < struct_count
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! WDF_STRUCTURE_SIZE {
|
||||
($name:ty) => {
|
||||
u32::try_from(::core::mem::size_of::<$name>()).expect("size is correct")
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! WDF_NO_HANDLE {
|
||||
() => {
|
||||
::core::ptr::null_mut()
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! WDF_NO_OBJECT_ATTRIBUTES {
|
||||
() => {
|
||||
::core::ptr::null_mut()
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE {
|
||||
($attr:ident, $context_type:ident) => {
|
||||
$attr.ContextTypeInfo = $context_type;
|
||||
};
|
||||
}
|
||||
|
||||
impl WDF_OBJECT_ATTRIBUTES {
|
||||
/// Initializes the [`WDF_OBJECT_ATTRIBUTES`] structure
|
||||
/// <https://github.com/microsoft/Windows-Driver-Frameworks/blob/a94b8c30dad524352fab90872aefc83920b98e56/src/publicinc/wdf/umdf/2.33/wdfobject.h#L136/>
|
||||
///
|
||||
/// Sets
|
||||
/// - `ExecutionLevel` to [`WDF_SYNCHRONIZATION_SCOPE::WdfSynchronizationScopeInheritFromParent`]
|
||||
/// - `SynchronizationScope` to [`WDF_EXECUTION_LEVEL::WdfExecutionLevelInheritFromParent`]
|
||||
#[must_use]
|
||||
pub fn init() -> Self {
|
||||
// SAFETY: All fields are zero-able
|
||||
let mut attributes: Self = unsafe { ::core::mem::zeroed() };
|
||||
|
||||
attributes.Size = WDF_STRUCTURE_SIZE!(Self);
|
||||
attributes.SynchronizationScope =
|
||||
WDF_SYNCHRONIZATION_SCOPE::WdfSynchronizationScopeInheritFromParent;
|
||||
attributes.ExecutionLevel = WDF_EXECUTION_LEVEL::WdfExecutionLevelInheritFromParent;
|
||||
|
||||
attributes
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn init_context_type(context_type: &_WDF_OBJECT_CONTEXT_TYPE_INFO) -> Self {
|
||||
let mut attr = Self::init();
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE!(attr, context_type);
|
||||
|
||||
attr
|
||||
}
|
||||
}
|
||||
|
||||
impl WDF_DRIVER_CONFIG {
|
||||
/// Initializes the [`WDF_DRIVER_CONFIG`] structure
|
||||
/// <https://github.com/microsoft/Windows-Driver-Frameworks/blob/a94b8c30dad524352fab90872aefc83920b98e56/src/publicinc/wdf/umdf/2.33/wdfdriver.h#L134/>
|
||||
#[must_use]
|
||||
pub fn init(EvtDriverDeviceAdd: PFN_WDF_DRIVER_DEVICE_ADD) -> Self {
|
||||
// SAFETY: All fields are zero-able
|
||||
let mut config: Self = unsafe { core::mem::zeroed() };
|
||||
|
||||
config.Size = WDF_STRUCTURE_SIZE!(Self);
|
||||
|
||||
config.EvtDriverDeviceAdd = EvtDriverDeviceAdd;
|
||||
|
||||
config
|
||||
}
|
||||
}
|
||||
|
||||
impl WDF_PNPPOWER_EVENT_CALLBACKS {
|
||||
/// Initializes the [`WDF_PNPPOWER_EVENT_CALLBACKS`] structure
|
||||
/// <https://github.com/microsoft/Windows-Driver-Frameworks/blob/a94b8c30dad524352fab90872aefc83920b98e56/src/publicinc/wdf/umdf/2.33/wdfdevice.h#L1278/>
|
||||
#[must_use]
|
||||
pub fn init() -> Self {
|
||||
// SAFETY: All fields are zero-able
|
||||
let mut callbacks: Self = unsafe { core::mem::zeroed() };
|
||||
callbacks.Size = WDF_STRUCTURE_SIZE!(Self);
|
||||
|
||||
callbacks
|
||||
}
|
||||
}
|
||||
|
||||
/// If this returns None, the struct is NOT available to be used
|
||||
macro_rules! IDD_STRUCTURE_SIZE {
|
||||
($name:ty) => {{
|
||||
// SAFETY: We only ever do read access, copy is fine
|
||||
let higher = unsafe { IddClientVersionHigherThanFramework } != 0;
|
||||
// SAFETY: We only ever do read access, copy is fine
|
||||
let struct_count = unsafe { IddStructureCount };
|
||||
|
||||
if higher {
|
||||
// as u32 is fine, since there's no way there's > 4 billion structs
|
||||
const STRUCT_INDEX: u32 =
|
||||
$crate::paste! { IDDSTRUCTENUM::[<INDEX_ $name:upper>].0 as u32 };
|
||||
|
||||
// SAFETY: A pointer to a [size_t], copying the pointer is ok
|
||||
let ptr = unsafe { IddStructures };
|
||||
|
||||
if STRUCT_INDEX < struct_count {
|
||||
// SAFETY: we validated struct index is able to be accessed
|
||||
let ptr = unsafe { ptr.add(STRUCT_INDEX as usize) };
|
||||
// SAFETY: So it's ok to read
|
||||
u32::try_from(unsafe { ptr.read() }).ok()
|
||||
} else {
|
||||
// struct CANNOT be used
|
||||
None
|
||||
}
|
||||
} else {
|
||||
u32::try_from(::std::mem::size_of::<$name>()).ok()
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
impl IDD_CX_CLIENT_CONFIG {
|
||||
#[must_use]
|
||||
pub fn init() -> Option<Self> {
|
||||
// SAFETY: All fields are zero-able
|
||||
let mut config: Self = unsafe { core::mem::zeroed() };
|
||||
|
||||
config.Size = IDD_STRUCTURE_SIZE!(IDD_CX_CLIENT_CONFIG)?;
|
||||
|
||||
Some(config)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user