vigem_client/
ds4.rs

1use std::{fmt, mem, ptr};
2use std::borrow::Borrow;
3use crate::*;
4
5/// DualShock4 HID Input report.
6#[cfg(feature = "unstable_ds4")]
7#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
8#[repr(C)]
9pub struct DS4Report {
10	pub thumb_lx: u8,
11	pub thumb_ly: u8,
12	pub thumb_rx: u8,
13	pub thumb_ry: u8,
14	pub buttons: u16,
15	pub special: u8,
16	pub trigger_l: u8,
17	pub trigger_r: u8,
18}
19#[cfg(feature = "unstable_ds4")]
20impl Default for DS4Report {
21	#[inline]
22	fn default() -> Self {
23		DS4Report {
24			thumb_lx: 0x80,
25			thumb_ly: 0x80,
26			thumb_rx: 0x80,
27			thumb_ry: 0x80,
28			buttons: 0x8,
29			special: 0,
30			trigger_l: 0,
31			trigger_r: 0,
32		}
33	}
34}
35
36// /// DualShock4 v1 complete HID Input report.
37// #[derive(Copy, Clone, Debug, Eq, PartialEq)]
38// #[repr(C)]
39// pub struct DS4ReportEx {
40// 	pub thumb_lx: u8,
41// 	pub thumb_ly: u8,
42// 	pub thumb_rx: u8,
43// 	pub thumb_ry: u8,
44// 	pub buttons: u16,
45// 	pub special: u8,
46// 	pub trigger_l: u8,
47// 	pub trigger_r: u8,
48// 	pub timestamp: u16,
49// 	pub battery_lvl: u8,
50// 	pub gyro_x: i16,
51// 	pub gyro_y: i16,
52// 	pub gyro_z: i16,
53// 	pub accel_x: i16,
54// 	pub accel_y: i16,
55// 	pub accel_z: i16,
56// 	pub _unknown1: [u8; 5],
57// 	pub battery_lvl_special: u8,
58// 	pub _unknown2: [u8; 2],
59// 	pub touch_packets_n: u8, // 0x00 to 0x03 (USB max)
60// 	pub current_touch: DS4Touch,
61// 	pub previous_touch: [DS4Touch; 2],
62// }
63
64/// A virtual Sony DualShock 4 (wired).
65pub struct DualShock4Wired<CL: Borrow<Client>> {
66	client: CL,
67	event: Event,
68	serial_no: u32,
69	id: TargetId,
70}
71
72impl<CL: Borrow<Client>> DualShock4Wired<CL> {
73	/// Creates a new instance.
74	#[inline]
75	pub fn new(client: CL, id: TargetId) -> DualShock4Wired<CL> {
76		let event = Event::new(false, false);
77		DualShock4Wired { client, event, serial_no: 0, id }
78	}
79
80	/// Returns if the controller is plugged in.
81	#[inline]
82	pub fn is_attached(&self) -> bool {
83		self.serial_no != 0
84	}
85
86	/// Returns the id the controller was constructed with.
87	#[inline]
88	pub fn id(&self) -> TargetId {
89		self.id
90	}
91
92	/// Returns the client.
93	#[inline]
94	pub fn client(&self) -> &CL {
95		&self.client
96	}
97
98	/// Unplugs and destroys the controller, returning the client.
99	#[inline]
100	pub fn drop(mut self) -> CL {
101		let _ = self.unplug();
102
103		unsafe {
104			let client = (&self.client as *const CL).read();
105			ptr::drop_in_place(&mut self.event);
106			mem::forget(self);
107			client
108		}
109	}
110
111	/// Plugs the controller in.
112	#[inline(never)]
113	pub fn plugin(&mut self) -> Result<(), Error> {
114		if self.is_attached() {
115			return Err(Error::AlreadyConnected);
116		}
117
118		self.serial_no = unsafe {
119			let mut plugin = bus::PluginTarget::ds4_wired(1, self.id.vendor, self.id.product);
120			let device = self.client.borrow().device;
121
122			// Yes this is how the driver is implemented
123			while plugin.ioctl(device, self.event.handle).is_err() {
124				plugin.SerialNo += 1;
125				if plugin.SerialNo >= u16::MAX as u32 {
126					return Err(Error::NoFreeSlot);
127				}
128			}
129
130			plugin.SerialNo
131		};
132
133		Ok(())
134	}
135
136	/// Unplugs the controller.
137	#[inline(never)]
138	pub fn unplug(&mut self) -> Result<(), Error> {
139		if !self.is_attached() {
140			return Err(Error::NotPluggedIn);
141		}
142
143		unsafe {
144			let mut unplug = bus::UnplugTarget::new(self.serial_no);
145			let device = self.client.borrow().device;
146			unplug.ioctl(device, self.event.handle)?;
147		}
148
149		self.serial_no = 0;
150		Ok(())
151	}
152
153	/// Waits until the virtual controller is ready.
154	///
155	/// Any updates submitted before the virtual controller is ready may return an error.
156	#[inline(never)]
157	pub fn wait_ready(&mut self) -> Result<(), Error> {
158		if !self.is_attached() {
159			return Err(Error::NotPluggedIn);
160		}
161
162		unsafe {
163			let mut wait = bus::WaitDeviceReady::new(self.serial_no);
164			let device = self.client.borrow().device;
165			wait.ioctl(device, self.event.handle)?;
166		}
167
168		Ok(())
169	}
170
171	/// Updates the virtual controller state.
172	#[cfg(feature = "unstable_ds4")]
173	#[inline(never)]
174	pub fn update(&mut self, report: &DS4Report) -> Result<(), Error> {
175		if !self.is_attached() {
176			return Err(Error::NotPluggedIn);
177		}
178
179		unsafe {
180			let mut dsr = bus::DS4SubmitReport::new(self.serial_no, *report);
181			let device = self.client.borrow().device;
182			dsr.ioctl(device, self.event.handle)?;
183		}
184
185		Ok(())
186	}
187
188	// #[inline(never)]
189	// pub fn update_ex(&mut self, report: &DS4ReportEx) -> Result<(), Error> {
190	// 	if !self.is_attached() {
191	// 		return Err(Error::NotPluggedIn);
192	// 	}
193	// 	unimplemented!()
194	// }
195}
196
197impl<CL: Borrow<Client>> fmt::Debug for DualShock4Wired<CL> {
198	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199		f.debug_struct("DualShock4Wired")
200			.field("serial_no", &self.serial_no)
201			.field("vendor_id", &self.id.vendor)
202			.field("product_id", &self.id.product)
203			.finish()
204	}
205}
206
207impl<CL: Borrow<Client>> Drop for DualShock4Wired<CL> {
208	#[inline]
209	fn drop(&mut self) {
210		let _ = self.unplug();
211	}
212}