use std::{
marker::PhantomData,
sync::{
Arc, Mutex, Weak,
mpsc::{self, Receiver, RecvTimeoutError},
},
time::Duration,
};
#[cfg(feature = "ds4")]
use crate::controller::ds4::{Ds4Notification, Ds4OutputBuffer, Ds4Report, Ds4ReportEx};
#[cfg(feature = "x360")]
use crate::controller::x360::{X360Notification, X360Report};
use crate::{
client::{Client, ClientError, ClientInner},
internal::bus::{Bus, BusError},
};
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum TargetType {
Xbox360 = 0,
DualShock4 = 2,
}
impl TargetType {
#[inline]
pub fn get_identifiers(&self) -> (u16, u16) {
match self {
TargetType::Xbox360 => (0x045E, 0x028E),
TargetType::DualShock4 => (0x054C, 0x05C4),
}
}
}
#[derive(Debug)]
pub(crate) struct Target {
pub(crate) kind: TargetType,
pub(crate) serial_no: u32,
pub(crate) vendor_id: u16,
pub(crate) product_id: u16,
}
#[cfg(feature = "x360")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Xbox360;
#[cfg(feature = "ds4")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DualShock4;
struct TargetHandleInner<T> {
serial_no: u32,
bus: Bus,
client_inner: Weak<Mutex<ClientInner>>,
_marker: PhantomData<T>,
}
impl<T> Drop for TargetHandleInner<T> {
fn drop(&mut self) {
if let Some(inner_arc) = self.client_inner.upgrade()
&& let Ok(mut inner) = inner_arc.lock()
&& inner.targets.remove(&self.serial_no).is_some()
{
let _ = self.bus.unplug(self.serial_no);
}
}
}
#[derive(Clone)]
pub struct TargetHandle<T> {
inner: Arc<TargetHandleInner<T>>,
}
impl<T> TargetHandle<T> {
pub(crate) fn new(serial_no: u32, bus: Bus, client_inner: Weak<Mutex<ClientInner>>) -> Self {
Self {
inner: Arc::new(TargetHandleInner {
serial_no,
bus,
client_inner,
_marker: PhantomData,
}),
}
}
fn with_client<F, R>(&self, f: F) -> Result<R, ClientError>
where
F: FnOnce(&ClientInner) -> Result<R, ClientError>,
{
if let Some(inner_arc) = self.inner.client_inner.upgrade() {
let inner = inner_arc.lock().expect("Client mutex was poisoned");
if !inner.targets.contains_key(&self.inner.serial_no) {
return Err(ClientError::TargetDoesNotExist(self.inner.serial_no));
}
f(&inner)
} else {
Err(ClientError::ClientNoLongerExists)
}
}
pub fn is_attached(&self) -> Result<bool, ClientError> {
self.with_client(|inner| Ok(inner.targets.contains_key(&self.inner.serial_no)))
}
pub fn unplug(&self) -> Result<(), ClientError> {
if let Some(inner_arc) = self.inner.client_inner.upgrade() {
let mut inner = inner_arc.lock().expect("Client mutex was poisoned");
if inner.targets.remove(&self.inner.serial_no).is_some() {
let _ = self.inner.bus.unplug(self.inner.serial_no);
}
Ok(())
} else {
Err(ClientError::ClientNoLongerExists)
}
}
}
#[cfg(feature = "x360")]
impl TargetHandle<Xbox360> {
pub fn get_user_index(&self) -> Result<u32, ClientError> {
let index = self.inner.bus.get_x360_user_index(self.inner.serial_no)?;
Ok(index)
}
pub fn wait_for_ready(&self) -> Result<(), ClientError> {
let (sender, receiver) = mpsc::channel();
self.inner
.bus
.start_x360_notification_thread(self.inner.serial_no, sender)?;
wait_for_notifications_internal(receiver, self.inner.serial_no)
}
pub fn register_notification(
&self,
) -> Result<Receiver<Result<X360Notification, BusError>>, ClientError> {
let (sender, receiver) = mpsc::channel();
self.inner
.bus
.start_x360_notification_thread(self.inner.serial_no, sender)?;
Ok(receiver)
}
pub fn update(&self, report: &X360Report) -> Result<(), ClientError> {
self.inner.bus.update_x360(self.inner.serial_no, report)?;
Ok(())
}
}
#[cfg(feature = "ds4")]
impl TargetHandle<DualShock4> {
pub fn wait_for_ready(&self) -> Result<(), ClientError> {
let (sender, receiver) = mpsc::channel();
self.inner
.bus
.start_ds4_notification_thread(self.inner.serial_no, sender)?;
wait_for_notifications_internal(receiver, self.inner.serial_no)
}
pub fn register_notification(
&self,
) -> Result<Receiver<Result<Ds4Notification, BusError>>, ClientError> {
let (sender, receiver) = mpsc::channel();
self.inner
.bus
.start_ds4_notification_thread(self.inner.serial_no, sender)?;
Ok(receiver)
}
pub fn register_notification_raw_buffer(
&self,
) -> Result<Receiver<Result<Ds4OutputBuffer, BusError>>, ClientError> {
let (sender, receiver) = mpsc::channel();
self.inner
.bus
.start_ds4_output_thread(self.inner.serial_no, sender)?;
Ok(receiver)
}
pub fn update(&self, report: &Ds4Report) -> Result<(), ClientError> {
self.inner.bus.update_ds4(self.inner.serial_no, report)?;
Ok(())
}
pub fn update_ex(&self, report: &Ds4ReportEx) -> Result<(), ClientError> {
self.inner.bus.update_ds4_ex(self.inner.serial_no, report)?;
Ok(())
}
}
pub struct TargetBuilder<'a, T> {
client: &'a Client,
vid: Option<u16>,
pid: Option<u16>,
_marker: PhantomData<T>,
}
impl<'a, T> TargetBuilder<'a, T> {
#[inline]
pub(crate) fn new(client: &'a Client) -> Self {
Self {
client,
vid: None,
pid: None,
_marker: PhantomData,
}
}
#[inline]
pub fn with_vid(mut self, vid: u16) -> Self {
self.vid = Some(vid);
self
}
#[inline]
pub fn with_pid(mut self, pid: u16) -> Self {
self.pid = Some(pid);
self
}
}
#[cfg(feature = "x360")]
impl<'a> TargetBuilder<'a, Xbox360> {
pub fn plugin(self) -> Result<TargetHandle<Xbox360>, ClientError> {
let (default_vid, default_pid) = TargetType::Xbox360.get_identifiers();
let target = Target {
kind: TargetType::Xbox360,
serial_no: 0, vendor_id: self.vid.unwrap_or(default_vid),
product_id: self.pid.unwrap_or(default_pid),
};
self.client.plugin_internal(target)
}
}
#[cfg(feature = "ds4")]
impl<'a> TargetBuilder<'a, DualShock4> {
pub fn plugin(self) -> Result<TargetHandle<DualShock4>, ClientError> {
let (default_vid, default_pid) = TargetType::DualShock4.get_identifiers();
let target = Target {
kind: TargetType::DualShock4,
serial_no: 0,
vendor_id: self.vid.unwrap_or(default_vid),
product_id: self.pid.unwrap_or(default_pid),
};
self.client.plugin_internal(target)
}
}
pub(crate) fn wait_for_notifications_internal<N>(
receiver: Receiver<Result<N, BusError>>,
serial_no: u32,
) -> Result<(), ClientError> {
match receiver.recv_timeout(Duration::from_millis(500)) {
Ok(Ok(_)) => {
}
Ok(Err(bus_error)) => {
return Err(bus_error.into());
}
Err(RecvTimeoutError::Timeout) => return Ok(()),
Err(_) => {
return Err(ClientError::TargetDoesNotExist(serial_no));
}
}
loop {
match receiver.recv_timeout(Duration::from_millis(250)) {
Ok(Ok(_)) => {
continue;
}
Ok(Err(bus_error)) => {
return Err(bus_error.into());
}
Err(RecvTimeoutError::Timeout) => {
break;
}
Err(RecvTimeoutError::Disconnected) => {
return Err(ClientError::TargetDoesNotExist(serial_no));
}
}
}
Ok(())
}