vigem_rust/target.rs
1use std::{
2 marker::PhantomData,
3 sync::{
4 Arc, Mutex, Weak,
5 mpsc::{self, Receiver, RecvTimeoutError},
6 },
7 time::Duration,
8};
9
10#[cfg(feature = "ds4")]
11use crate::controller::ds4::{Ds4Notification, Ds4OutputBuffer, Ds4Report, Ds4ReportEx};
12#[cfg(feature = "x360")]
13use crate::controller::x360::{X360Notification, X360Report};
14
15use crate::{
16 client::{Client, ClientError, ClientInner},
17 internal::bus::{Bus, BusError},
18};
19
20#[repr(u32)]
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub(crate) enum TargetType {
23 Xbox360 = 0,
24 DualShock4 = 2,
25}
26
27impl TargetType {
28 #[inline]
29 // (vendor_id, product_id)
30 pub fn get_identifiers(&self) -> (u16, u16) {
31 match self {
32 TargetType::Xbox360 => (0x045E, 0x028E),
33 TargetType::DualShock4 => (0x054C, 0x05C4),
34 }
35 }
36}
37
38#[derive(Debug)]
39pub(crate) struct Target {
40 pub(crate) kind: TargetType,
41 pub(crate) serial_no: u32,
42 pub(crate) vendor_id: u16,
43 pub(crate) product_id: u16,
44}
45
46#[cfg(feature = "x360")]
47/// A marker type representing a virtual Xbox 360 controller.
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub struct Xbox360;
50
51#[cfg(feature = "ds4")]
52/// A marker type representing a virtual DualShock 4 controller.
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub struct DualShock4;
55
56struct TargetHandleInner<T> {
57 serial_no: u32,
58 bus: Bus,
59 client_inner: Weak<Mutex<ClientInner>>,
60 _marker: PhantomData<T>,
61}
62
63impl<T> Drop for TargetHandleInner<T> {
64 fn drop(&mut self) {
65 if let Some(inner_arc) = self.client_inner.upgrade()
66 && let Ok(mut inner) = inner_arc.lock()
67 && inner.targets.remove(&self.serial_no).is_some()
68 {
69 let _ = self.bus.unplug(self.serial_no);
70 }
71 }
72}
73
74/// An opaque handle to a plugged-in virtual controller.
75///
76/// This handle is returned when a new target is successfully plugged into the bus.
77/// It is used to identify the target for subsequent operations like updating its state,
78/// listening for notifications, or removing it.
79///
80/// This handle uses reference counting (`Arc`). Cloning it is cheap and creates another
81/// handle to the same virtual controller. The controller is only unplugged from the bus
82/// when the **last** handle is dropped.
83#[derive(Clone)]
84pub struct TargetHandle<T> {
85 inner: Arc<TargetHandleInner<T>>,
86}
87
88impl<T> TargetHandle<T> {
89 pub(crate) fn new(serial_no: u32, bus: Bus, client_inner: Weak<Mutex<ClientInner>>) -> Self {
90 Self {
91 inner: Arc::new(TargetHandleInner {
92 serial_no,
93 bus,
94 client_inner,
95 _marker: PhantomData,
96 }),
97 }
98 }
99
100 fn with_client<F, R>(&self, f: F) -> Result<R, ClientError>
101 where
102 F: FnOnce(&ClientInner) -> Result<R, ClientError>,
103 {
104 if let Some(inner_arc) = self.inner.client_inner.upgrade() {
105 let inner = inner_arc.lock().expect("Client mutex was poisoned");
106 if !inner.targets.contains_key(&self.inner.serial_no) {
107 return Err(ClientError::TargetDoesNotExist(self.inner.serial_no));
108 }
109 f(&inner)
110 } else {
111 Err(ClientError::ClientNoLongerExists)
112 }
113 }
114
115 /// Checks if the virtual controller is still attached to the bus.
116 ///
117 /// This can return `false` if the controller was manually unplugged
118 /// or if the client was dropped.
119 pub fn is_attached(&self) -> Result<bool, ClientError> {
120 self.with_client(|inner| Ok(inner.targets.contains_key(&self.inner.serial_no)))
121 }
122
123 /// Explicitly unplugs the virtual controller from the bus.
124 ///
125 /// After calling this, any further operations on this [`TargetHandle`] (and any
126 /// of its clones) will fail. The controller is also automatically unplugged when
127 /// the last [`TargetHandle`] is dropped.
128 pub fn unplug(&self) -> Result<(), ClientError> {
129 if let Some(inner_arc) = self.inner.client_inner.upgrade() {
130 let mut inner = inner_arc.lock().expect("Client mutex was poisoned");
131 if inner.targets.remove(&self.inner.serial_no).is_some() {
132 let _ = self.inner.bus.unplug(self.inner.serial_no);
133 }
134 Ok(())
135 } else {
136 Err(ClientError::ClientNoLongerExists)
137 }
138 }
139}
140
141#[cfg(feature = "x360")]
142impl TargetHandle<Xbox360> {
143 /// Gets the user index of a virtual Xbox 360 controller.
144 ///
145 /// It doesn't seem like this method is reliable for getting the dynamic player index assigned by a game.
146 /// It often returns `0` even after an index has been assigned.
147 ///
148 /// **To reliably get the player index, use `TargetHandle<X360>::register_notification` and
149 /// check the `led_number` field of the received `X360Notification`.**
150 pub fn get_user_index(&self) -> Result<u32, ClientError> {
151 let index = self.inner.bus.get_x360_user_index(self.inner.serial_no)?;
152 Ok(index)
153 }
154
155 /// Blocks until the virtual controller is fully enumerated and ready to receive updates.
156 ///
157 /// It is recommended to call this after plugging in a new controller if
158 /// you want to immediately send a report to the controller.
159 ///
160 /// # Example
161 /// ```no_run
162 /// use vigem_rust::{Client, X360Report};
163 /// let client = Client::connect().unwrap();
164 /// let x360 = client.new_x360_target().plugin().unwrap();
165 ///
166 /// // Wait for the controller to be ready
167 /// x360.wait_for_ready().unwrap();
168 ///
169 /// // Now it's safe to send updates
170 /// x360.update(&X360Report::default()).unwrap();
171 /// ```
172 pub fn wait_for_ready(&self) -> Result<(), ClientError> {
173 let (sender, receiver) = mpsc::channel();
174 self.inner
175 .bus
176 .start_x360_notification_thread(self.inner.serial_no, sender)?;
177 wait_for_notifications_internal(receiver, self.inner.serial_no)
178 }
179
180 /// Registers to receive notifications for this Xbox 360 target.
181 ///
182 /// This returns a `Receiver` that will yield [`X360Notification`]s from the bus,
183 /// which contain information like rumble data and the controller's player LED index.
184 ///
185 /// # Important
186 /// Calling this function spawns a dedicated background thread that lives as long as
187 /// the `Receiver` does.
188 ///
189 /// # Example
190 /// ```no_run
191 /// # use vigem_rust::{Client, X360Notification};
192 /// # let client = Client::connect().unwrap();
193 /// # let x360 = client.new_x360_target().plugin().unwrap();
194 /// let receiver = x360.register_notification().unwrap();
195 ///
196 /// // In a loop or another thread:
197 /// if let Ok(Ok(notification)) = receiver.try_recv() {
198 /// println!("Player LED is now {}", notification.led_number);
199 /// }
200 /// ```
201 pub fn register_notification(
202 &self,
203 ) -> Result<Receiver<Result<X360Notification, BusError>>, ClientError> {
204 let (sender, receiver) = mpsc::channel();
205 self.inner
206 .bus
207 .start_x360_notification_thread(self.inner.serial_no, sender)?;
208 Ok(receiver)
209 }
210
211 /// Submits an input state report for this Xbox 360 target.
212 ///
213 /// This is the primary method for sending controller inputs to the system.
214 /// The provided [`X360Report`] contains the state of all buttons, triggers,
215 /// and thumbsticks.
216 ///
217 /// # Warning
218 /// Calling this method immediately after plugging in the target will likely fail,
219 /// as the system needs time to enumerate the device.
220 ///
221 /// To reliably send updates right after creation, you must first call [`wait_for_ready()`].
222 ///
223 /// # Example
224 /// ```no_run
225 /// # use vigem_rust::{Client, X360Report, X360Button};
226 /// # use std::error::Error;
227 /// # fn main() -> Result<(), Box<dyn Error>> {
228 /// # let client = Client::connect()?;
229 /// # let x360 = client.new_x360_target().plugin()?;
230 /// # x360.wait_for_ready()?;
231 /// let mut report = X360Report::default();
232 /// report.buttons = X360Button::A | X360Button::START;
233 /// report.thumb_lx = 16384; // Move left stick right
234 ///
235 /// x360.update(&report)?;
236 /// # Ok(())
237 /// # }
238 /// ```
239 pub fn update(&self, report: &X360Report) -> Result<(), ClientError> {
240 self.inner.bus.update_x360(self.inner.serial_no, report)?;
241 Ok(())
242 }
243}
244
245#[cfg(feature = "ds4")]
246impl TargetHandle<DualShock4> {
247 /// Blocks until the virtual controller is fully enumerated and ready to receive updates.
248 ///
249 /// It is recommended to call this after plugging in a new controller if
250 /// you want to immediately send a report to the controller.
251 ///
252 /// # Example
253 /// ```no_run
254 /// use vigem_rust::{Client, Ds4Report};
255 /// let client = Client::connect().unwrap();
256 /// let ds4 = client.new_ds4_target().plugin().unwrap();
257 ///
258 /// // Wait for the controller to be ready
259 /// ds4.wait_for_ready().unwrap();
260 ///
261 /// // Now it's safe to send updates
262 /// ds4.update(&Ds4Report::default()).unwrap();
263 /// ```
264 pub fn wait_for_ready(&self) -> Result<(), ClientError> {
265 let (sender, receiver) = mpsc::channel();
266 self.inner
267 .bus
268 .start_ds4_notification_thread(self.inner.serial_no, sender)?;
269 wait_for_notifications_internal(receiver, self.inner.serial_no)
270 }
271
272 /// Registers to receive notifications for this DualShock 4 target.
273 ///
274 /// This returns a `Receiver` that will yield [`Ds4Notification`]s from the bus,
275 /// which contain information like rumble data and lightbar color commands.
276 ///
277 /// # Important
278 /// Calling this function spawns a dedicated background thread that lives as long as
279 /// the `Receiver` does.
280 ///
281 /// # Example
282 /// ```no_run
283 /// # use vigem_rust::{Client, Ds4Notification};
284 /// # let client = Client::connect().unwrap();
285 /// # let ds4 = client.new_ds4_target().plugin().unwrap();
286 /// let receiver = ds4.register_notification().unwrap();
287 ///
288 /// // In a loop or another thread:
289 /// if let Ok(Ok(notification)) = receiver.try_recv() {
290 /// println!("Lightbar color changed to: {:?}", notification.lightbar);
291 /// }
292 /// ```
293 pub fn register_notification(
294 &self,
295 ) -> Result<Receiver<Result<Ds4Notification, BusError>>, ClientError> {
296 let (sender, receiver) = mpsc::channel();
297 self.inner
298 .bus
299 .start_ds4_notification_thread(self.inner.serial_no, sender)?;
300 Ok(receiver)
301 }
302
303 /// Subscribes to raw 64-byte output buffers for a DualShock 4 target.
304 ///
305 /// # Warning
306 /// Unlike the `register_notification` method, this one gets the raw
307 /// output buffer of all connected Dualshock 4 virtual controllers
308 /// via a single thread (centralized system.)
309 ///
310 /// # Important
311 /// Calling this function spawns a dedicated background thread that lives as long as
312 /// the `Receiver` does.
313 ///
314 /// This is an advanced function for applications that need to parse the raw output
315 /// report from the bus, which may contain more detailed information than the standard
316 /// [`Ds4Notification`].
317 pub fn register_notification_raw_buffer(
318 &self,
319 ) -> Result<Receiver<Result<Ds4OutputBuffer, BusError>>, ClientError> {
320 let (sender, receiver) = mpsc::channel();
321 self.inner
322 .bus
323 .start_ds4_output_thread(self.inner.serial_no, sender)?;
324 Ok(receiver)
325 }
326
327 /// Submits a standard input state report for this DualShock 4 target.
328 ///
329 /// This method sends a [`Ds4Report`], which covers the state of all buttons,
330 /// D-Pad, triggers, and thumbsticks. For advanced features like touchpad or
331 /// motion sensor data, use [`update_ex`](Self::update_ex) instead.
332 ///
333 /// # Warning
334 /// Calling this method immediately after plugging in the target will likely fail,
335 /// as the system needs time to enumerate the device.
336 ///
337 /// To reliably send updates right after creation, you must first call [`wait_for_ready()`].
338 ///
339 /// # Example
340 /// ```no_run
341 /// # use vigem_rust::{Client, Ds4Report, Ds4Button, Ds4Dpad};
342 /// # use std::error::Error;
343 /// # fn main() -> Result<(), Box<dyn Error>> {
344 /// # let client = Client::connect()?;
345 /// # let ds4 = client.new_ds4_target().plugin()?;
346 /// # ds4.wait_for_ready()?;
347 /// let mut report = Ds4Report::default();
348 /// report.buttons = Ds4Button::CROSS.bits();
349 /// report.set_dpad(Ds4Dpad::South);
350 /// report.trigger_r = 255;
351 ///
352 /// ds4.update(&report)?;
353 /// # Ok(())
354 /// # }
355 /// ```
356 pub fn update(&self, report: &Ds4Report) -> Result<(), ClientError> {
357 self.inner.bus.update_ds4(self.inner.serial_no, report)?;
358 Ok(())
359 }
360
361 /// Submits an extended input state report for this DualShock 4 target.
362 ///
363 /// This method is used for advanced scenarios that require simulating motion
364 /// controls (gyroscope/accelerometer) and detailed touchpad activity. It sends
365 /// a [`Ds4ReportEx`], which is a superset of the standard [`Ds4Report`].
366 ///
367 /// # Warning
368 /// Calling this method immediately after plugging in the target will likely fail,
369 /// as the system needs time to enumerate the device.
370 ///
371 /// To reliably send updates right after creation, you must first call [`wait_for_ready()`].
372 ///
373 /// # Example
374 /// ```no_run
375 /// # use vigem_rust::{Client, controller::ds4::Ds4ReportEx};
376 /// # use std::error::Error;
377 /// # fn main() -> Result<(), Box<dyn Error>> {
378 /// # let client = Client::connect()?;
379 /// # let ds4 = client.new_ds4_target().plugin()?;
380 /// # ds4.wait_for_ready()?;
381 /// let mut report_ex = Ds4ReportEx::default();
382 ///
383 ///
384 /// // Set gyro data
385 /// report_ex.gyro_x = 12345;
386 ///
387 /// // The standard report fields are also available
388 /// report_ex.thumb_lx = 200;
389 ///
390 /// ds4.update_ex(&report_ex)?;
391 /// # Ok(())
392 /// # }
393 /// ```
394 pub fn update_ex(&self, report: &Ds4ReportEx) -> Result<(), ClientError> {
395 self.inner.bus.update_ds4_ex(self.inner.serial_no, report)?;
396 Ok(())
397 }
398}
399
400/// A builder for creating and plugging in a new virtual target.
401///
402/// Obtain a [`TargetBuilder`] from [`Client::new_x360_target()`] or [`Client::new_ds4_target()`].
403pub struct TargetBuilder<'a, T> {
404 client: &'a Client,
405 vid: Option<u16>,
406 pid: Option<u16>,
407 _marker: PhantomData<T>,
408}
409
410impl<'a, T> TargetBuilder<'a, T> {
411 #[inline]
412 pub(crate) fn new(client: &'a Client) -> Self {
413 Self {
414 client,
415 vid: None,
416 pid: None,
417 _marker: PhantomData,
418 }
419 }
420
421 #[inline]
422 /// Sets a custom Vendor ID (VID) for this virtual device.
423 ///
424 /// If not set, the default VID for the controller type will be used.
425 pub fn with_vid(mut self, vid: u16) -> Self {
426 self.vid = Some(vid);
427 self
428 }
429
430 #[inline]
431 /// Sets a custom Product ID (PID) for this virtual device.
432 ///
433 /// If not set, the default PID for the controller type will be used.
434 pub fn with_pid(mut self, pid: u16) -> Self {
435 self.pid = Some(pid);
436 self
437 }
438}
439
440#[cfg(feature = "x360")]
441impl<'a> TargetBuilder<'a, Xbox360> {
442 /// Plugs the configured target into the ViGEm bus.
443 ///
444 /// **WARNING:** The virtual controller may not be immediately ready for input updates
445 /// (e.g., `update()` calls) upon the return of this function. Windows and the ViGEm
446 /// driver require time to fully enumerate the device.
447 ///
448 /// For reliable operation, especially when sending updates immediately after plugging
449 /// in, it is highly recommended to call [`TargetHandle<T>::wait_for_ready`] and wait for
450 /// it to return successfully before sending the first report.
451 ///
452 /// On success, this consumes the builder and returns a [`TargetHandle`] which can
453 /// be used to control the virtual device.
454 pub fn plugin(self) -> Result<TargetHandle<Xbox360>, ClientError> {
455 let (default_vid, default_pid) = TargetType::Xbox360.get_identifiers();
456 let target = Target {
457 kind: TargetType::Xbox360,
458 serial_no: 0, // Will be filled in by the client
459 vendor_id: self.vid.unwrap_or(default_vid),
460 product_id: self.pid.unwrap_or(default_pid),
461 };
462 self.client.plugin_internal(target)
463 }
464}
465
466#[cfg(feature = "ds4")]
467impl<'a> TargetBuilder<'a, DualShock4> {
468 /// Plugs the configured target into the ViGEm bus.
469 ///
470 /// **WARNING:** The virtual controller may not be immediately ready for input updates
471 /// (e.g., `update()` calls) upon the return of this function. Windows and the ViGEm
472 /// driver require time to fully enumerate the device.
473 ///
474 /// For reliable operation, especially when sending updates immediately after plugging
475 /// in, it is highly recommended to call [`TargetHandle<T>::wait_for_ready`] and wait for
476 /// it to return successfully before sending the first report.
477 ///
478 /// On success, this consumes the builder and returns a [`TargetHandle`] which can
479 /// be used to control the virtual device.
480 pub fn plugin(self) -> Result<TargetHandle<DualShock4>, ClientError> {
481 // Same here, using the concrete type.
482 let (default_vid, default_pid) = TargetType::DualShock4.get_identifiers();
483 let target = Target {
484 kind: TargetType::DualShock4,
485 serial_no: 0,
486 vendor_id: self.vid.unwrap_or(default_vid),
487 product_id: self.pid.unwrap_or(default_pid),
488 };
489 self.client.plugin_internal(target)
490 }
491}
492
493// HELPER
494
495/// Blocks until the controller is ready.
496///
497/// The readiness logic is as follows:
498/// 1. Block until the first notification arrives from the host with a 500ms timeout.
499/// 2. After the first notification, enter a state with a 250ms timeout.
500/// 3. Each subsequent notification resets this timeout.
501/// 4. If 250ms pass without any new notifications, the controller is considered "stable"
502/// and ready, and the method returns.
503///
504/// This approach is used because the underlying `IOCTL_VIGEM_WAIT_DEVICE_READY` signal
505/// from the driver doesn't seem to working properly. Waiting for a brief period of notification
506/// silence after initial activity is a more robust heuristic for device readiness.
507pub(crate) fn wait_for_notifications_internal<N>(
508 receiver: Receiver<Result<N, BusError>>,
509 serial_no: u32,
510) -> Result<(), ClientError> {
511 // We wait for the first notification. If it doesnt come within 500ms,
512 // then chances are the device is ready for receiving updates.
513 match receiver.recv_timeout(Duration::from_millis(500)) {
514 Ok(Ok(_)) => {
515 // First notification received. Now, wait for notifications to stabilize.
516 }
517 Ok(Err(bus_error)) => {
518 return Err(bus_error.into());
519 }
520 Err(RecvTimeoutError::Timeout) => return Ok(()),
521 Err(_) => {
522 return Err(ClientError::TargetDoesNotExist(serial_no));
523 }
524 }
525
526 loop {
527 match receiver.recv_timeout(Duration::from_millis(250)) {
528 Ok(Ok(_)) => {
529 // Another notification arrived. Reset the timer by looping again.
530 continue;
531 }
532 Ok(Err(bus_error)) => {
533 return Err(bus_error.into());
534 }
535 Err(RecvTimeoutError::Timeout) => {
536 // Timeout reached. No notifications for set timeout period. Device is (hopefully) ready!
537 break;
538 }
539 Err(RecvTimeoutError::Disconnected) => {
540 return Err(ClientError::TargetDoesNotExist(serial_no));
541 }
542 }
543 }
544
545 Ok(())
546}