vigem_client/
x360.rs

1use std::{fmt, mem, ptr};
2#[cfg(feature = "unstable_xtarget_notification")]
3use std::{marker, pin, thread};
4use std::borrow::Borrow;
5use winapi::um::xinput::XINPUT_GAMEPAD;
6use winapi::shared::winerror;
7use crate::*;
8
9/// XInput compatible button flags.
10#[derive(Copy, Clone, Default, Eq, PartialEq, Hash)]
11#[repr(transparent)]
12pub struct XButtons {
13	pub raw: u16,
14}
15
16/// XInput compatible button flags.
17#[allow(non_snake_case)]
18#[inline]
19pub const fn XButtons(raw: u16) -> XButtons {
20	XButtons { raw }
21}
22
23/// XInput compatible button flags.
24///
25/// ```
26/// let buttons = vigem_client::XButtons!(UP|RIGHT|LB|A|X);
27/// assert_eq!(buttons, vigem_client::XButtons(0x5109));
28/// ```
29#[macro_export]
30macro_rules! XButtons {
31	(UP) => { $crate::XButtons { raw: $crate::XButtons::UP } };
32	(DOWN) => { $crate::XButtons { raw: $crate::XButtons::DOWN } };
33	(LEFT) => { $crate::XButtons { raw: $crate::XButtons::LEFT } };
34	(RIGHT) => { $crate::XButtons { raw: $crate::XButtons::RIGHT } };
35	(START) => { $crate::XButtons { raw: $crate::XButtons::START } };
36	(BACK) => { $crate::XButtons { raw: $crate::XButtons::BACK } };
37	(LTHUMB) => { $crate::XButtons { raw: $crate::XButtons::LTHUMB } };
38	(RTHUMB) => { $crate::XButtons { raw: $crate::XButtons::RTHUMB } };
39	(LB) => { $crate::XButtons { raw: $crate::XButtons::LB } };
40	(RB) => { $crate::XButtons { raw: $crate::XButtons::RB } };
41	(GUIDE) => { $crate::XButtons { raw: $crate::XButtons::GUIDE } };
42	(A) => { $crate::XButtons { raw: $crate::XButtons::A } };
43	(B) => { $crate::XButtons { raw: $crate::XButtons::B } };
44	(X) => { $crate::XButtons { raw: $crate::XButtons::X } };
45	(Y) => { $crate::XButtons { raw: $crate::XButtons::Y } };
46
47	($($face:ident)|*) => {
48		$crate::XButtons { raw: 0 $(| $crate::XButtons!($face).raw)* }
49	};
50}
51
52impl XButtons {
53	/// Dpad up button.
54	pub const UP: u16     = 0x0001;
55	/// Dpad down button.
56	pub const DOWN: u16   = 0x0002;
57	/// Dpad left button.
58	pub const LEFT: u16   = 0x0004;
59	/// Dpad right button.
60	pub const RIGHT: u16  = 0x0008;
61	/// Start button.
62	pub const START: u16  = 0x0010;
63	/// Back button.
64	pub const BACK: u16   = 0x0020;
65	/// Left thumb button.
66	pub const LTHUMB: u16 = 0x0040;
67	/// Right thumb button.
68	pub const RTHUMB: u16 = 0x0080;
69	/// Left shoulder button.
70	pub const LB: u16     = 0x0100;
71	/// Right shoulder button.
72	pub const RB: u16     = 0x0200;
73	/// Xbox guide button.
74	pub const GUIDE: u16  = 0x0400;
75	/// A button.
76	pub const A: u16      = 0x1000;
77	/// B button.
78	pub const B: u16      = 0x2000;
79	/// X button.
80	pub const X: u16      = 0x4000;
81	/// Y button.
82	pub const Y: u16      = 0x8000;
83}
84
85impl From<u16> for XButtons {
86	#[inline]
87	fn from(raw: u16) -> Self {
88		XButtons { raw }
89	}
90}
91impl From<XButtons> for u16 {
92	#[inline]
93	fn from(buttons: XButtons) -> Self {
94		buttons.raw
95	}
96}
97impl AsRef<u16> for XButtons {
98	#[inline]
99	fn as_ref(&self) -> &u16 {
100		&self.raw
101	}
102}
103impl AsMut<u16> for XButtons {
104	#[inline]
105	fn as_mut(&mut self) -> &mut u16 {
106		&mut self.raw
107	}
108}
109
110impl fmt::Debug for XButtons {
111	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112		if f.alternate() {
113			const NAMES: [&'static str; 16] = [
114				"UP", "DOWN", "LEFT", "RIGHT",
115				"START", "BACK", "LTHUMB", "RTHUMB",
116				"LB", "RB", "GUIDE", "?",
117				"A", "B", "X", "Y",
118			];
119			let mut comma = false;
120			for index in 0..16 {
121				if self.raw & (1 << index) != 0 {
122					if comma {
123						f.write_str("|")?;
124						comma = true;
125					}
126					f.write_str(NAMES[index])?;
127				}
128			}
129			Ok(())
130		}
131		else {
132			write!(f, "XButtons({:#x})", self.raw)
133		}
134	}
135}
136
137/// XInput compatible gamepad.
138///
139/// Represents an [`XINPUT_GAMEPAD`]-compatible report structure.
140///
141/// ![image](https://user-images.githubusercontent.com/2324759/124391245-f889b180-dcef-11eb-927c-4b76d2ca332d.png)
142#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
143#[repr(C)]
144pub struct XGamepad {
145	pub buttons: XButtons,
146	pub left_trigger: u8,
147	pub right_trigger: u8,
148	pub thumb_lx: i16,
149	pub thumb_ly: i16,
150	pub thumb_rx: i16,
151	pub thumb_ry: i16,
152}
153
154impl From<XINPUT_GAMEPAD> for XGamepad {
155	#[inline]
156	fn from(gamepad: XINPUT_GAMEPAD) -> Self {
157		unsafe { mem::transmute(gamepad) }
158	}
159}
160impl From<XGamepad> for XINPUT_GAMEPAD {
161	#[inline]
162	fn from(report: XGamepad) -> XINPUT_GAMEPAD {
163		unsafe { mem::transmute(report) }
164	}
165}
166impl AsRef<XINPUT_GAMEPAD> for XGamepad {
167	#[inline]
168	fn as_ref(&self) -> &XINPUT_GAMEPAD {
169		unsafe { mem::transmute(self) }
170	}
171}
172impl AsMut<XINPUT_GAMEPAD> for XGamepad {
173	#[inline]
174	fn as_mut(&mut self) -> &mut XINPUT_GAMEPAD {
175		unsafe { mem::transmute(self) }
176	}
177}
178
179/// XInput notification structure.
180#[cfg(feature = "unstable_xtarget_notification")]
181#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
182#[repr(C)]
183pub struct XNotification {
184	pub large_motor: u8,
185	pub small_motor: u8,
186	pub led_number: u8,
187}
188
189/// XInput notification request.
190#[cfg(feature = "unstable_xtarget_notification")]
191pub struct XRequestNotification {
192	client: Client,
193	xurn: bus::RequestNotification<bus::XUsbRequestNotification>,
194	_unpin: marker::PhantomPinned,
195}
196
197#[cfg(feature = "unstable_xtarget_notification")]
198impl XRequestNotification {
199	/// Returns if the underlying target is still attached.
200	#[inline]
201	pub fn is_attached(&self) -> bool {
202		self.xurn.buffer.SerialNo != 0
203	}
204
205	/// Spawns a thread to handle the notifications.
206	///
207	/// The callback `f` is invoked for every notification.
208	///
209	/// Returns a [`JoinHandle`](thread::JoinHandle) for the created thread.
210	/// It is recommended to join the thread after the target from which the notifications are requested is dropped.
211	#[inline]
212	pub fn spawn_thread<F: FnMut(&XRequestNotification, XNotification) + Send + 'static>(self, mut f: F) -> thread::JoinHandle<()> {
213		thread::spawn(move || {
214			// Safety: the request notification object is not accessible after it is pinned
215			let mut reqn = self;
216			let mut reqn = unsafe { pin::Pin::new_unchecked(&mut reqn) };
217			loop {
218				reqn.as_mut().request();
219				let result = reqn.as_mut().poll(true);
220				match result {
221					Ok(None) => {},
222					Ok(Some(data)) => f(&reqn, data),
223					// When the target is dropped the notification request is aborted
224					Err(_) => break,
225				}
226			}
227		})
228	}
229
230	/// Requests a notification.
231	#[inline(never)]
232	pub fn request(self: pin::Pin<&mut Self>) {
233		unsafe {
234			let device = self.client.device;
235			let xurn = &mut self.get_unchecked_mut().xurn;
236			if xurn.buffer.SerialNo != 0 {
237				xurn.ioctl(device);
238			}
239		}
240	}
241
242	/// Polls the request for notifications.
243	///
244	/// If `wait` is true this method will block until a notification is received.
245	/// Else returns immediately if no notification is received yet.
246	///
247	/// Returns:
248	///
249	/// * `Ok(None)`: When `wait` is false and there is no notification yet.
250	/// * `Ok(Some(_))`: The notification was successfully received.  
251	///   Another request should be made or any other calls to `poll` return the same result.
252	/// * `Err(OperationAborted)`: The underlying target was unplugged causing any pending notification requests to abort.
253	/// * `Err(_)`: An unexpected error occurred.
254	#[inline(never)]
255	pub fn poll(self: pin::Pin<&mut Self>, wait: bool) -> Result<Option<XNotification>, Error> {
256		unsafe {
257			let device = self.client.device;
258			let xurn = &mut self.get_unchecked_mut().xurn;
259			match xurn.poll(device, wait) {
260				Ok(()) => Ok(Some(XNotification {
261					large_motor: xurn.buffer.LargeMotor,
262					small_motor: xurn.buffer.SmallMotor,
263					led_number: xurn.buffer.LedNumber,
264				})),
265				Err(winerror::ERROR_IO_INCOMPLETE) => Ok(None),
266				Err(winerror::ERROR_OPERATION_ABORTED) => {
267					// Operation was aborted, fail all future calls
268					// The is aborted when the underlying target is unplugged
269					// This has the potential for a race condition:
270					//  What happens if a new target is plugged inbetween calls to poll and request...
271					xurn.buffer.SerialNo = 0;
272					Err(Error::OperationAborted)
273				},
274				Err(err) => Err(Error::WinError(err)),
275			}
276		}
277	}
278}
279
280#[cfg(feature = "unstable_xtarget_notification")]
281unsafe impl Sync for XRequestNotification {}
282#[cfg(feature = "unstable_xtarget_notification")]
283unsafe impl Send for XRequestNotification {}
284
285#[cfg(feature = "unstable_xtarget_notification")]
286impl fmt::Debug for XRequestNotification {
287	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288		f.debug_struct("XRequestNotification")
289			.field("client", &format_args!("{:?}", self.client))
290			.field("serial_no", &self.xurn.buffer.SerialNo)
291			.finish()
292	}
293}
294
295#[cfg(feature = "unstable_xtarget_notification")]
296impl Drop for XRequestNotification {
297	fn drop(&mut self) {
298		unsafe {
299			let this = pin::Pin::new_unchecked(self);
300			if this.xurn.buffer.SerialNo != 0 {
301				let device = this.client.device;
302				let xurn = &mut this.get_unchecked_mut().xurn;
303				let _ = xurn.cancel(device);
304			}
305		}
306	}
307}
308
309/// Virtual Microsoft Xbox 360 Controller (wired).
310pub type XTarget = Xbox360Wired<Client>;
311
312/// A virtual Microsoft Xbox 360 Controller (wired).
313pub struct Xbox360Wired<CL: Borrow<Client>> {
314	client: CL,
315	event: Event,
316	serial_no: u32,
317	id: TargetId,
318}
319
320impl<CL: Borrow<Client>> Xbox360Wired<CL> {
321	/// Creates a new instance.
322	#[inline]
323	pub fn new(client: CL, id: TargetId) -> Xbox360Wired<CL> {
324		let event = Event::new(false, false);
325		Xbox360Wired { client, event, serial_no: 0, id }
326	}
327
328	/// Returns if the controller is plugged in.
329	#[inline]
330	pub fn is_attached(&self) -> bool {
331		self.serial_no != 0
332	}
333
334	/// Returns the vendor and product ids.
335	#[inline]
336	pub fn id(&self) -> TargetId {
337		self.id
338	}
339
340	/// Returns the client.
341	#[inline]
342	pub fn client(&self) -> &CL {
343		&self.client
344	}
345
346	/// Unplugs and destroys the controller, returning the client.
347	#[inline]
348	pub fn drop(mut self) -> CL {
349		let _ = self.unplug();
350
351		unsafe {
352			let client = (&self.client as *const CL).read();
353			ptr::drop_in_place(&mut self.event);
354			mem::forget(self);
355			client
356		}
357	}
358
359	/// Plugs the controller in.
360	#[inline(never)]
361	pub fn plugin(&mut self) -> Result<(), Error> {
362		if self.is_attached() {
363			return Err(Error::AlreadyConnected);
364		}
365
366		let mut plugin = bus::PluginTarget::x360_wired(1, self.id.vendor, self.id.product);
367		let device = self.client.borrow().device;
368
369		// Yes this is how the driver is implemented
370		while unsafe { plugin.ioctl(device, self.event.handle) }.is_err() {
371			plugin.SerialNo += 1;
372			if plugin.SerialNo >= u16::MAX as u32 {
373				return Err(Error::NoFreeSlot);
374			}
375		}
376
377		self.serial_no = plugin.SerialNo;
378		Ok(())
379	}
380
381	/// Unplugs the controller.
382	#[inline(never)]
383	pub fn unplug(&mut self) -> Result<(), Error> {
384		if !self.is_attached() {
385			return Err(Error::NotPluggedIn);
386		}
387
388		unsafe {
389			let mut unplug = bus::UnplugTarget::new(self.serial_no);
390			let device = self.client.borrow().device;
391			unplug.ioctl(device, self.event.handle)?;
392		}
393
394		self.serial_no = 0;
395		Ok(())
396	}
397
398	/// Waits until the virtual controller is ready.
399	///
400	/// Any updates submitted before the virtual controller is ready may return an error.
401	#[inline(never)]
402	pub fn wait_ready(&mut self) -> Result<(), Error> {
403		if !self.is_attached() {
404			return Err(Error::NotPluggedIn);
405		}
406
407		unsafe {
408			let mut wait = bus::WaitDeviceReady::new(self.serial_no);
409			let device = self.client.borrow().device;
410			wait.ioctl(device, self.event.handle)?;
411		}
412
413		Ok(())
414	}
415
416	/// Gets the user index of the device in XInput.
417	#[inline(never)]
418	pub fn get_user_index(&mut self) -> Result<u32, Error> {
419		if !self.is_attached() {
420			return Err(Error::NotPluggedIn);
421		}
422
423		let user_index = unsafe {
424			let mut gui = bus::XUsbGetUserIndex::new(self.serial_no);
425			let device = self.client.borrow().device;
426			match gui.ioctl(device, self.event.handle) {
427				Ok(()) => (),
428				// Err(winerror::ERROR_ACCESS_DENIED) => return Err(Error::InvalidTarget),
429				Err(winerror::ERROR_INVALID_DEVICE_OBJECT_PARAMETER) => return Err(Error::UserIndexOutOfRange),
430				Err(err) => return Err(Error::WinError(err)),
431			}
432
433			gui.UserIndex
434		};
435
436		Ok(user_index)
437	}
438
439	/// Updates the virtual controller state.
440	#[inline(never)]
441	pub fn update(&mut self, gamepad: &XGamepad) -> Result<(), Error> {
442		if !self.is_attached() {
443			return Err(Error::NotPluggedIn);
444		}
445
446		unsafe {
447			let mut xsr = bus::XUsbSubmitReport::new(self.serial_no, *gamepad);
448			let device = self.client.borrow().device;
449			match xsr.ioctl(device, self.event.handle) {
450				Ok(()) => Ok(()),
451				Err(winerror::ERROR_DEV_NOT_EXIST) => Err(Error::TargetNotReady),
452				Err(err) => Err(Error::WinError(err)),
453			}
454		}
455	}
456
457	/// Request notification.
458	///
459	/// See examples/notification.rs for a complete example how to use this interface.
460	///
461	/// Do not create more than one request notification per target.
462	/// Notifications may get lost or received by one or more listeners.
463	#[cfg(feature = "unstable_xtarget_notification")]
464	#[inline(never)]
465	pub fn request_notification(&mut self) -> Result<XRequestNotification, Error> {
466		if !self.is_attached() {
467			return Err(Error::NotPluggedIn);
468		}
469
470		let client = self.client.borrow().try_clone()?;
471		let xurn = bus::RequestNotification::new(
472			bus::XUsbRequestNotification::new(self.serial_no));
473
474		Ok(XRequestNotification { client, xurn, _unpin: marker::PhantomPinned })
475	}
476}
477
478impl<CL: Borrow<Client>> fmt::Debug for Xbox360Wired<CL> {
479	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
480		f.debug_struct("Xbox360Wired")
481			.field("client", &format_args!("{:?}", self.client.borrow()))
482			.field("event", &format_args!("{:?}", self.event))
483			.field("serial_no", &self.serial_no)
484			.field("vendor_id", &self.id.vendor)
485			.field("product_id", &self.id.product)
486			.finish()
487	}
488}
489
490impl<CL: Borrow<Client>> Drop for Xbox360Wired<CL> {
491	#[inline]
492	fn drop(&mut self) {
493		let _ = self.unplug();
494	}
495}