diff --git a/.vscode/settings.json b/.vscode/settings.json index 371afb2..8f170fb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "rust-analyzer.check.command": "clippy" + "rust-analyzer.check.command": "clippy", } diff --git a/README.md b/README.md index b777a6a..09f7112 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,8 @@ Please see the [wiki](https://github.com/MolotovCherry/virtual-display-rs/wiki/V 3. Install [`cargo-make`](https://github.com/sagiegurari/cargo-make) if you don't have it 4. Install [`cargo-target-dir`](https://github.com/MolotovCherry/cargo-target-dir) +Make sure you generate your own windows code signing cert and install it according to "Installing the certificate" section. + You can build it with `cargo make build` (debug) or `cargo make -p prod build` (release), and check the `target/output` directory for all the files To build the installer, do a `cargo make build-installer` (dev) or `cargo make -p prod build-installer` (release). In order to build the installer, you need [wix toolset](https://github.com/wixtoolset/wix3/releases) installed and on `Path` diff --git a/rust/virtual-display-driver/src/context.rs b/rust/virtual-display-driver/src/context.rs index 24d2351..02c31e2 100644 --- a/rust/virtual-display-driver/src/context.rs +++ b/rust/virtual-display-driver/src/context.rs @@ -1,24 +1,29 @@ use std::{ + ffi::c_void, mem::{self, size_of}, num::{ParseIntError, TryFromIntError}, ptr::{addr_of_mut, NonNull}, }; use anyhow::anyhow; -use log::error; +use log::{error, warn}; use wdf_umdf::{ - IddCxAdapterInitAsync, IddCxError, IddCxMonitorArrival, IddCxMonitorCreate, WdfError, - WdfObjectDelete, WDF_DECLARE_CONTEXT_TYPE, + IddCxAdapterInitAsync, IddCxError, IddCxMonitorArrival, IddCxMonitorCreate, + IddCxMonitorSetupHardwareCursor, WdfError, WdfObjectDelete, WDF_DECLARE_CONTEXT_TYPE, }; use wdf_umdf_sys::{ DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY, HANDLE, IDARG_IN_ADAPTER_INIT, IDARG_IN_MONITORCREATE, - IDARG_OUT_ADAPTER_INIT, IDARG_OUT_MONITORARRIVAL, IDARG_OUT_MONITORCREATE, IDDCX_ADAPTER, - IDDCX_ADAPTER_CAPS, IDDCX_ENDPOINT_DIAGNOSTIC_INFO, IDDCX_ENDPOINT_VERSION, - IDDCX_FEATURE_IMPLEMENTATION, IDDCX_MONITOR, IDDCX_MONITOR_DESCRIPTION, - IDDCX_MONITOR_DESCRIPTION_TYPE, IDDCX_MONITOR_INFO, IDDCX_SWAPCHAIN, IDDCX_TRANSMISSION_TYPE, - LUID, NTSTATUS, WDFDEVICE, WDFOBJECT, WDF_OBJECT_ATTRIBUTES, + IDARG_IN_SETUP_HWCURSOR, IDARG_OUT_ADAPTER_INIT, IDARG_OUT_MONITORARRIVAL, + IDARG_OUT_MONITORCREATE, IDDCX_ADAPTER, IDDCX_ADAPTER_CAPS, IDDCX_CURSOR_CAPS, + IDDCX_ENDPOINT_DIAGNOSTIC_INFO, IDDCX_ENDPOINT_VERSION, IDDCX_FEATURE_IMPLEMENTATION, + IDDCX_MONITOR, IDDCX_MONITOR_DESCRIPTION, IDDCX_MONITOR_DESCRIPTION_TYPE, IDDCX_MONITOR_INFO, + IDDCX_SWAPCHAIN, IDDCX_TRANSMISSION_TYPE, IDDCX_XOR_CURSOR_SUPPORT, LUID, NTSTATUS, WDFDEVICE, + WDFOBJECT, WDF_OBJECT_ATTRIBUTES, +}; +use windows::{ + core::{s, w, GUID}, + Win32::{Foundation::TRUE, System::Threading::CreateEventA}, }; -use windows::core::{w, GUID}; use crate::{ direct_3d_device::Direct3DDevice, @@ -250,6 +255,8 @@ impl MonitorContext { processor.run(swap_chain, device, new_frame_event); self.swap_chain_processor = Some(processor); + + self.setup_hw_cursor(); } else { // It's important to delete the swap-chain if D3D initialization fails, so that the OS knows to generate a new // swap-chain and try again. @@ -263,4 +270,40 @@ impl MonitorContext { pub fn unassign_swap_chain(&mut self) { self.swap_chain_processor.take(); } + + pub fn setup_hw_cursor(&mut self) { + let mouse_event = unsafe { CreateEventA(None, false, false, s!("vdd_mouse_event")) }; + let Ok(mouse_event) = mouse_event else { + error!("CreateEventA failed: {mouse_event:?}"); + return; + }; + + // setup hardware cursor + let cursor_info = IDDCX_CURSOR_CAPS { + #[allow(clippy::cast_possible_truncation)] + Size: std::mem::size_of::() as u32, + AlphaCursorSupport: TRUE.0, + MaxX: 512, + MaxY: 512, + ColorXorCursorSupport: IDDCX_XOR_CURSOR_SUPPORT::IDDCX_XOR_CURSOR_SUPPORT_NONE, + }; + + let hw_cursor = IDARG_IN_SETUP_HWCURSOR { + CursorInfo: cursor_info, + hNewCursorDataAvailable: mouse_event.0 as *mut c_void, + }; + + let res = unsafe { IddCxMonitorSetupHardwareCursor(self.device, &hw_cursor) }; + let Ok(res) = res else { + error!("IddCxMonitorSetupHardwareCursor() failed: {res:?}"); + return; + }; + + if res.is_warning() { + warn!("IddCxMonitorSetupHardwareCursor() warn: {res:?}"); + } + if res.is_error() { + error!("IddCxMonitorSetupHardwareCursor() failed: {res:?}"); + } + } } diff --git a/rust/wdf-umdf/src/iddcx.rs b/rust/wdf-umdf/src/iddcx.rs index 66e6a95..260a231 100644 --- a/rust/wdf-umdf/src/iddcx.rs +++ b/rust/wdf-umdf/src/iddcx.rs @@ -2,8 +2,9 @@ #![allow(clippy::missing_errors_doc)] use wdf_umdf_sys::{ - IDARG_IN_ADAPTER_INIT, IDARG_IN_MONITORCREATE, IDARG_IN_SWAPCHAINSETDEVICE, - IDARG_OUT_ADAPTER_INIT, IDARG_OUT_MONITORARRIVAL, IDARG_OUT_MONITORCREATE, + IDARG_IN_ADAPTER_INIT, IDARG_IN_MONITORCREATE, IDARG_IN_QUERY_HWCURSOR, + IDARG_IN_SETUP_HWCURSOR, IDARG_IN_SWAPCHAINSETDEVICE, IDARG_OUT_ADAPTER_INIT, + IDARG_OUT_MONITORARRIVAL, IDARG_OUT_MONITORCREATE, IDARG_OUT_QUERY_HWCURSOR, IDARG_OUT_RELEASEANDACQUIREBUFFER, IDDCX_ADAPTER, IDDCX_MONITOR, IDDCX_SWAPCHAIN, IDD_CX_CLIENT_CONFIG, NTSTATUS, WDFDEVICE, WDFDEVICE_INIT, }; @@ -135,9 +136,9 @@ pub unsafe fn IddCxDeviceInitialize( /// None. User is responsible for safety. pub unsafe fn IddCxAdapterInitAsync( // in - pInArgs: *const IDARG_IN_ADAPTER_INIT, + pInArgs: &IDARG_IN_ADAPTER_INIT, // out - pOutArgs: *mut IDARG_OUT_ADAPTER_INIT, + pOutArgs: &mut IDARG_OUT_ADAPTER_INIT, ) -> Result { IddCxCall! { IddCxAdapterInitAsync( @@ -155,9 +156,9 @@ pub unsafe fn IddCxMonitorCreate( // in AdapterObject: IDDCX_ADAPTER, // in - pInArgs: *const IDARG_IN_MONITORCREATE, + pInArgs: &IDARG_IN_MONITORCREATE, // out - pOutArgs: *mut IDARG_OUT_MONITORCREATE, + pOutArgs: &mut IDARG_OUT_MONITORCREATE, ) -> Result { IddCxCall!( IddCxMonitorCreate( @@ -176,7 +177,7 @@ pub unsafe fn IddCxMonitorArrival( // in MonitorObject: IDDCX_MONITOR, // out - pOutArgs: *mut IDARG_OUT_MONITORARRIVAL, + pOutArgs: &mut IDARG_OUT_MONITORARRIVAL, ) -> Result { IddCxCall!( IddCxMonitorArrival( @@ -254,3 +255,42 @@ pub unsafe fn IddCxMonitorDeparture( ) ) } + +/// # Safety +/// +/// None. User is responsible for safety. +#[rustfmt::skip] +pub unsafe fn IddCxMonitorSetupHardwareCursor( + // in + MonitorObject: IDDCX_MONITOR, + // in + pInArgs: &IDARG_IN_SETUP_HWCURSOR +) -> Result { + IddCxCall!( + IddCxMonitorSetupHardwareCursor( + MonitorObject, + pInArgs + ) + ) +} + +/// # Safety +/// +/// None. User is responsible for safety. +#[rustfmt::skip] +pub unsafe fn IddCxMonitorQueryHardwareCursor( + // in + MonitorObject: IDDCX_MONITOR, + // in + pInArgs: &IDARG_IN_QUERY_HWCURSOR, + // out + pOutArgs: &mut IDARG_OUT_QUERY_HWCURSOR +) -> Result { + IddCxCall!( + IddCxMonitorQueryHardwareCursor( + MonitorObject, + pInArgs, + pOutArgs + ) + ) +}