vigem_client/
client.rs

1use std::{mem, ptr};
2use std::os::windows::io as win_io;
3use winapi::um::handleapi::*;
4use winapi::um::setupapi::*;
5use winapi::um::fileapi::*;
6use winapi::um::winnt::*;
7use winapi::um::winbase::*;
8use winapi::um::errhandlingapi::*;
9use winapi::shared::ntdef::HANDLE;
10use crate::*;
11
12/// The ViGEmBus service connection.
13#[derive(Debug)]
14pub struct Client {
15	pub(crate) device: HANDLE,
16}
17
18impl Client {
19	/// Connects to the ViGEmBus service.
20	pub fn connect() -> Result<Client, Error> {
21		unsafe {
22			let mut error = Error::BusNotFound;
23
24			let mut member_index = 0;
25			let mut device_interface_data: SP_DEVICE_INTERFACE_DATA = mem::zeroed();
26			device_interface_data.cbSize = mem::size_of_val(&device_interface_data) as u32;
27
28			let mut detail_data_buffer = mem::MaybeUninit::<[u32; 0x300]>::uninit();
29
30			let device_info_set = SetupDiGetClassDevsW(
31				&bus::GUID_DEVINTERFACE,
32				ptr::null(),
33				ptr::null_mut(),
34				DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
35
36			if device_info_set == INVALID_HANDLE_VALUE {
37				return Err(Error::WinError(GetLastError()));
38			}
39
40			// Enumerate device instances
41			while SetupDiEnumDeviceInterfaces(
42				device_info_set,
43				ptr::null_mut(),
44				&bus::GUID_DEVINTERFACE,
45				member_index,
46				&mut device_interface_data) != 0
47			{
48				member_index += 1;
49
50				// Allocate target buffer
51				// This is a fixed size stack buffer which should be big enough for everyone
52				let detail_data_ptr = detail_data_buffer.as_mut_ptr() as PSP_DEVICE_INTERFACE_DETAIL_DATA_W;
53				*ptr::addr_of_mut!((*detail_data_ptr).cbSize) = mem::size_of::<SP_DEVICE_INTERFACE_DETAIL_DATA_W>() as u32;
54
55				// Get detail buffer
56				let mut required_size = 0;
57				if SetupDiGetDeviceInterfaceDetailW(
58					device_info_set,
59					&mut device_interface_data,
60					detail_data_ptr,
61					mem::size_of_val(&detail_data_buffer) as u32,
62					&mut required_size,
63					ptr::null_mut()) == 0
64				{
65					error = Error::WinError(GetLastError());
66					continue;
67				}
68
69				// bus found, open it
70				let device_path = ptr::addr_of!((*detail_data_ptr).DevicePath) as *const u16;
71				let device = CreateFileW(
72					device_path,
73					GENERIC_READ | GENERIC_WRITE,
74					FILE_SHARE_READ | FILE_SHARE_WRITE,
75					ptr::null_mut(),
76					OPEN_EXISTING,
77					FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED,
78					ptr::null_mut());
79
80				if device == INVALID_HANDLE_VALUE {
81					error = Error::BusAccessFailed(GetLastError());
82					continue;
83				}
84
85				let mut check_version = bus::CheckVersion::common();
86				if check_version.ioctl(device) {
87					SetupDiDestroyDeviceInfoList(device_info_set);
88					return Ok(Client { device })
89				}
90
91				// version mismatch, look for another instance
92				CloseHandle(device);
93				error = Error::BusVersionMismatch;
94			}
95
96			SetupDiDestroyDeviceInfoList(device_info_set);
97			Err(error)
98		}
99	}
100
101	/// Duplicates the ViGEmBus service handle.
102	#[inline]
103	pub fn try_clone(&self) -> Result<Client, Error> {
104		unsafe {
105			let process_handle = (!0) as *mut _;
106			let mut target_handle = mem::MaybeUninit::uninit();
107			let success = DuplicateHandle(
108				process_handle, self.device,
109				process_handle, target_handle.as_mut_ptr(),
110				GENERIC_READ | GENERIC_WRITE, 0, DUPLICATE_SAME_ACCESS);
111			if success == 0 {
112				let err = GetLastError();
113				return Err(Error::WinError(err));
114			}
115			Ok(Client { device: target_handle.assume_init() })
116		}
117	}
118}
119
120unsafe impl Sync for Client {}
121unsafe impl Send for Client {}
122
123impl win_io::AsRawHandle for Client {
124	#[inline]
125	fn as_raw_handle(&self) -> HANDLE {
126		self.device
127	}
128}
129impl win_io::IntoRawHandle for Client {
130	#[inline]
131	fn into_raw_handle(self) -> HANDLE {
132		self.device
133	}
134}
135impl win_io::FromRawHandle for Client {
136	#[inline]
137	unsafe fn from_raw_handle(device: HANDLE) -> Client {
138		Client { device }
139	}
140}
141
142impl Drop for Client {
143	#[inline]
144	fn drop(&mut self) {
145		unsafe {
146			CloseHandle(self.device);
147		}
148	}
149}