mirror of
https://github.com/MolotovCherry/virtual-display-rs.git
synced 2025-09-07 01:06:22 +00:00
Implement hardware cursor #81
This commit is contained in:
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"rust-analyzer.check.command": "clippy"
|
"rust-analyzer.check.command": "clippy",
|
||||||
}
|
}
|
||||||
|
@@ -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
|
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)
|
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
|
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`
|
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`
|
||||||
|
@@ -1,24 +1,29 @@
|
|||||||
use std::{
|
use std::{
|
||||||
|
ffi::c_void,
|
||||||
mem::{self, size_of},
|
mem::{self, size_of},
|
||||||
num::{ParseIntError, TryFromIntError},
|
num::{ParseIntError, TryFromIntError},
|
||||||
ptr::{addr_of_mut, NonNull},
|
ptr::{addr_of_mut, NonNull},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use log::error;
|
use log::{error, warn};
|
||||||
use wdf_umdf::{
|
use wdf_umdf::{
|
||||||
IddCxAdapterInitAsync, IddCxError, IddCxMonitorArrival, IddCxMonitorCreate, WdfError,
|
IddCxAdapterInitAsync, IddCxError, IddCxMonitorArrival, IddCxMonitorCreate,
|
||||||
WdfObjectDelete, WDF_DECLARE_CONTEXT_TYPE,
|
IddCxMonitorSetupHardwareCursor, WdfError, WdfObjectDelete, WDF_DECLARE_CONTEXT_TYPE,
|
||||||
};
|
};
|
||||||
use wdf_umdf_sys::{
|
use wdf_umdf_sys::{
|
||||||
DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY, HANDLE, IDARG_IN_ADAPTER_INIT, IDARG_IN_MONITORCREATE,
|
DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY, HANDLE, IDARG_IN_ADAPTER_INIT, IDARG_IN_MONITORCREATE,
|
||||||
IDARG_OUT_ADAPTER_INIT, IDARG_OUT_MONITORARRIVAL, IDARG_OUT_MONITORCREATE, IDDCX_ADAPTER,
|
IDARG_IN_SETUP_HWCURSOR, IDARG_OUT_ADAPTER_INIT, IDARG_OUT_MONITORARRIVAL,
|
||||||
IDDCX_ADAPTER_CAPS, IDDCX_ENDPOINT_DIAGNOSTIC_INFO, IDDCX_ENDPOINT_VERSION,
|
IDARG_OUT_MONITORCREATE, IDDCX_ADAPTER, IDDCX_ADAPTER_CAPS, IDDCX_CURSOR_CAPS,
|
||||||
IDDCX_FEATURE_IMPLEMENTATION, IDDCX_MONITOR, IDDCX_MONITOR_DESCRIPTION,
|
IDDCX_ENDPOINT_DIAGNOSTIC_INFO, IDDCX_ENDPOINT_VERSION, IDDCX_FEATURE_IMPLEMENTATION,
|
||||||
IDDCX_MONITOR_DESCRIPTION_TYPE, IDDCX_MONITOR_INFO, IDDCX_SWAPCHAIN, IDDCX_TRANSMISSION_TYPE,
|
IDDCX_MONITOR, IDDCX_MONITOR_DESCRIPTION, IDDCX_MONITOR_DESCRIPTION_TYPE, IDDCX_MONITOR_INFO,
|
||||||
LUID, NTSTATUS, WDFDEVICE, WDFOBJECT, WDF_OBJECT_ATTRIBUTES,
|
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::{
|
use crate::{
|
||||||
direct_3d_device::Direct3DDevice,
|
direct_3d_device::Direct3DDevice,
|
||||||
@@ -250,6 +255,8 @@ impl MonitorContext {
|
|||||||
processor.run(swap_chain, device, new_frame_event);
|
processor.run(swap_chain, device, new_frame_event);
|
||||||
|
|
||||||
self.swap_chain_processor = Some(processor);
|
self.swap_chain_processor = Some(processor);
|
||||||
|
|
||||||
|
self.setup_hw_cursor();
|
||||||
} else {
|
} else {
|
||||||
// It's important to delete the swap-chain if D3D initialization fails, so that the OS knows to generate a new
|
// 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.
|
// swap-chain and try again.
|
||||||
@@ -263,4 +270,40 @@ impl MonitorContext {
|
|||||||
pub fn unassign_swap_chain(&mut self) {
|
pub fn unassign_swap_chain(&mut self) {
|
||||||
self.swap_chain_processor.take();
|
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::<IDDCX_CURSOR_CAPS>() 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:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,8 +2,9 @@
|
|||||||
#![allow(clippy::missing_errors_doc)]
|
#![allow(clippy::missing_errors_doc)]
|
||||||
|
|
||||||
use wdf_umdf_sys::{
|
use wdf_umdf_sys::{
|
||||||
IDARG_IN_ADAPTER_INIT, IDARG_IN_MONITORCREATE, IDARG_IN_SWAPCHAINSETDEVICE,
|
IDARG_IN_ADAPTER_INIT, IDARG_IN_MONITORCREATE, IDARG_IN_QUERY_HWCURSOR,
|
||||||
IDARG_OUT_ADAPTER_INIT, IDARG_OUT_MONITORARRIVAL, IDARG_OUT_MONITORCREATE,
|
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,
|
IDARG_OUT_RELEASEANDACQUIREBUFFER, IDDCX_ADAPTER, IDDCX_MONITOR, IDDCX_SWAPCHAIN,
|
||||||
IDD_CX_CLIENT_CONFIG, NTSTATUS, WDFDEVICE, WDFDEVICE_INIT,
|
IDD_CX_CLIENT_CONFIG, NTSTATUS, WDFDEVICE, WDFDEVICE_INIT,
|
||||||
};
|
};
|
||||||
@@ -135,9 +136,9 @@ pub unsafe fn IddCxDeviceInitialize(
|
|||||||
/// None. User is responsible for safety.
|
/// None. User is responsible for safety.
|
||||||
pub unsafe fn IddCxAdapterInitAsync(
|
pub unsafe fn IddCxAdapterInitAsync(
|
||||||
// in
|
// in
|
||||||
pInArgs: *const IDARG_IN_ADAPTER_INIT,
|
pInArgs: &IDARG_IN_ADAPTER_INIT,
|
||||||
// out
|
// out
|
||||||
pOutArgs: *mut IDARG_OUT_ADAPTER_INIT,
|
pOutArgs: &mut IDARG_OUT_ADAPTER_INIT,
|
||||||
) -> Result<NTSTATUS, IddCxError> {
|
) -> Result<NTSTATUS, IddCxError> {
|
||||||
IddCxCall! {
|
IddCxCall! {
|
||||||
IddCxAdapterInitAsync(
|
IddCxAdapterInitAsync(
|
||||||
@@ -155,9 +156,9 @@ pub unsafe fn IddCxMonitorCreate(
|
|||||||
// in
|
// in
|
||||||
AdapterObject: IDDCX_ADAPTER,
|
AdapterObject: IDDCX_ADAPTER,
|
||||||
// in
|
// in
|
||||||
pInArgs: *const IDARG_IN_MONITORCREATE,
|
pInArgs: &IDARG_IN_MONITORCREATE,
|
||||||
// out
|
// out
|
||||||
pOutArgs: *mut IDARG_OUT_MONITORCREATE,
|
pOutArgs: &mut IDARG_OUT_MONITORCREATE,
|
||||||
) -> Result<NTSTATUS, IddCxError> {
|
) -> Result<NTSTATUS, IddCxError> {
|
||||||
IddCxCall!(
|
IddCxCall!(
|
||||||
IddCxMonitorCreate(
|
IddCxMonitorCreate(
|
||||||
@@ -176,7 +177,7 @@ pub unsafe fn IddCxMonitorArrival(
|
|||||||
// in
|
// in
|
||||||
MonitorObject: IDDCX_MONITOR,
|
MonitorObject: IDDCX_MONITOR,
|
||||||
// out
|
// out
|
||||||
pOutArgs: *mut IDARG_OUT_MONITORARRIVAL,
|
pOutArgs: &mut IDARG_OUT_MONITORARRIVAL,
|
||||||
) -> Result<NTSTATUS, IddCxError> {
|
) -> Result<NTSTATUS, IddCxError> {
|
||||||
IddCxCall!(
|
IddCxCall!(
|
||||||
IddCxMonitorArrival(
|
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<NTSTATUS, IddCxError> {
|
||||||
|
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<NTSTATUS, IddCxError> {
|
||||||
|
IddCxCall!(
|
||||||
|
IddCxMonitorQueryHardwareCursor(
|
||||||
|
MonitorObject,
|
||||||
|
pInArgs,
|
||||||
|
pOutArgs
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user