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}