vigem_rust/
client.rs

1use std::collections::HashMap;
2use std::sync::{Arc, Mutex};
3use thiserror::Error;
4
5use crate::internal::bus::{Bus, BusError};
6#[cfg(feature = "ds4")]
7use crate::target::DualShock4;
8#[cfg(feature = "x360")]
9use crate::target::Xbox360;
10
11use crate::target::{Target, TargetBuilder, TargetHandle};
12
13/// Errors that can occur when interacting with the ViGEm client.
14#[derive(Error, Debug)]
15pub enum ClientError {
16    #[error("Windows API Error: {0}")]
17    WindowsAPIError(#[from] windows::core::Error),
18
19    #[error("Bus error: {0}")]
20    BusError(#[from] BusError),
21
22    #[error("No more free slots available, consider increasing slots via the Client builder")]
23    NoFreeSlot,
24
25    #[error("Target with serial ID {0} is no longer connected or has been unplugged")]
26    TargetDoesNotExist(u32),
27
28    #[error("Client has been dropped, therefore any target operations can't be done.")]
29    ClientNoLongerExists,
30}
31
32const DEFAULT_VIGEM_TARGETS_MAX: u32 = 16;
33
34pub(crate) struct ClientInner {
35    pub(crate) bus: Bus,
36    pub(crate) targets: HashMap<u32, Target>,
37    max_targets: u32,
38}
39
40/// The main entry point for interacting with the ViGEm bus driver.
41///
42/// A `Client` manages the connection to the driver and keeps track of all
43/// virtual controllers created by it. When the `Client` is dropped, it will
44/// automatically unplug all of its connected virtual controllers.
45///
46/// Use `Client::builder()` or `Client::connect()` to create a new client.
47pub struct Client {
48    inner: Arc<Mutex<ClientInner>>,
49}
50
51/// A builder for creating a `Client`.
52pub struct ClientBuilder {
53    max_targets: Option<u32>,
54}
55
56impl ClientBuilder {
57    #[inline]
58    /// Creates a new `ClientBuilder` with default settings.
59    fn new() -> Self {
60        Self { max_targets: None }
61    }
62
63    #[inline]
64    /// Sets the maximum number of virtual targets this client can manage.
65    ///
66    /// The default is 16.
67    pub fn max_targets(mut self, count: u32) -> Self {
68        self.max_targets = Some(count);
69        self
70    }
71
72    /// Connects to the ViGEm bus and creates a `Client`.
73    pub fn connect(self) -> Result<Client, ClientError> {
74        let max_targets = self.max_targets.unwrap_or(DEFAULT_VIGEM_TARGETS_MAX);
75        let bus = Bus::connect()?;
76        let inner = ClientInner {
77            bus,
78            targets: HashMap::new(),
79            max_targets,
80        };
81
82        Ok(Client {
83            inner: Arc::new(Mutex::new(inner)),
84        })
85    }
86}
87
88impl Client {
89    #[inline]
90    /// Create a builder to configure and connect a new client.
91    ///
92    /// This allows setting options like the maximum number of targets.
93    ///
94    /// # Example
95    /// ```no_run
96    /// use vigem_rust::client::Client;
97    /// let client = Client::builder()
98    ///     .max_targets(32) // Allow up to 32 controllers
99    ///     .connect()
100    ///     .unwrap();
101    /// ```
102    pub fn builder() -> ClientBuilder {
103        ClientBuilder::new()
104    }
105
106    #[inline]
107    /// Creates a new client with default options and connects to the ViGEm bus.
108    ///
109    /// This is a convenient shortcut for `Client::builder().connect()`.
110    ///
111    /// # Example
112    /// ```no_run
113    /// use vigem_rust::Client;
114    ///
115    /// let client = Client::connect().unwrap();
116    /// ```
117    pub fn connect() -> Result<Self, ClientError> {
118        Self::builder().connect()
119    }
120
121    #[inline]
122    #[cfg(feature = "x360")]
123    /// Creates a builder for a new virtual Xbox 360 controller.
124    ///
125    /// The returned builder can be used to set properties like vendor and product IDs
126    /// before plugging the controller into the bus.
127    ///
128    /// # Example
129    /// ```no_run
130    /// # use vigem_rust::Client;
131    /// # let mut client = Client::connect().unwrap();
132    /// let x360 = client.new_x360_target()
133    ///     .with_vid(0xAAAA)
134    ///     .with_pid(0xBBBB)
135    ///     .plugin()
136    ///     .unwrap();
137    /// ```
138    pub fn new_x360_target(&self) -> TargetBuilder<'_, Xbox360> {
139        TargetBuilder::new(self)
140    }
141
142    #[inline]
143    #[cfg(feature = "ds4")]
144    /// Creates a builder for a new virtual DualShock 4 controller.
145    ///
146    /// The returned builder can be used to set properties like vendor and product IDs
147    /// before plugging the controller into the bus.
148    ///
149    /// # Example
150    /// ```no_run
151    /// # use vigem_rust::Client;
152    /// # let mut client = Client::connect().unwrap();
153    /// let ds4 = client.new_ds4_target()
154    ///     .with_vid(0xAAAA)
155    ///     .with_pid(0xBBBB)
156    ///     .plugin()
157    ///     .unwrap();
158    /// ```
159    pub fn new_ds4_target(&self) -> TargetBuilder<'_, DualShock4> {
160        TargetBuilder::new(self)
161    }
162
163    pub(crate) fn plugin_internal<T>(
164        &self,
165        target: Target,
166    ) -> Result<TargetHandle<T>, ClientError> {
167        let mut inner = self.inner.lock().expect("Client mutex was poisoned");
168        let mut target = target;
169
170        for serial_no in 1..=inner.max_targets {
171            if !inner.targets.contains_key(&serial_no) && inner.bus.plug(&target, serial_no).is_ok()
172            {
173                target.serial_no = serial_no;
174                inner.targets.insert(serial_no, target);
175
176                return Ok(TargetHandle::new(
177                    serial_no,
178                    inner.bus.clone(),
179                    Arc::downgrade(&self.inner),
180                ));
181            }
182        }
183
184        Err(ClientError::NoFreeSlot)
185    }
186}
187
188impl Drop for ClientInner {
189    fn drop(&mut self) {
190        for target in self.targets.values() {
191            let _ = self.bus.unplug(target.serial_no);
192        }
193        self.targets.clear();
194    }
195}