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}