use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use thiserror::Error;
use crate::internal::bus::{Bus, BusError};
#[cfg(feature = "ds4")]
use crate::target::DualShock4;
#[cfg(feature = "x360")]
use crate::target::Xbox360;
use crate::target::{Target, TargetBuilder, TargetHandle};
#[derive(Error, Debug)]
pub enum ClientError {
#[error("Windows API Error: {0}")]
WindowsAPIError(#[from] windows::core::Error),
#[error("Bus error: {0}")]
BusError(#[from] BusError),
#[error("No more free slots available, consider increasing slots via the Client builder")]
NoFreeSlot,
#[error("Target with serial ID {0} is no longer connected or has been unplugged")]
TargetDoesNotExist(u32),
#[error("Client has been dropped, therefore any target operations can't be done.")]
ClientNoLongerExists,
}
const DEFAULT_VIGEM_TARGETS_MAX: u32 = 16;
pub(crate) struct ClientInner {
pub(crate) bus: Bus,
pub(crate) targets: HashMap<u32, Target>,
max_targets: u32,
}
pub struct Client {
inner: Arc<Mutex<ClientInner>>,
}
pub struct ClientBuilder {
max_targets: Option<u32>,
}
impl ClientBuilder {
#[inline]
fn new() -> Self {
Self { max_targets: None }
}
#[inline]
pub fn max_targets(mut self, count: u32) -> Self {
self.max_targets = Some(count);
self
}
pub fn connect(self) -> Result<Client, ClientError> {
let max_targets = self.max_targets.unwrap_or(DEFAULT_VIGEM_TARGETS_MAX);
let bus = Bus::connect()?;
let inner = ClientInner {
bus,
targets: HashMap::new(),
max_targets,
};
Ok(Client {
inner: Arc::new(Mutex::new(inner)),
})
}
}
impl Client {
#[inline]
pub fn builder() -> ClientBuilder {
ClientBuilder::new()
}
#[inline]
pub fn connect() -> Result<Self, ClientError> {
Self::builder().connect()
}
#[inline]
#[cfg(feature = "x360")]
pub fn new_x360_target(&self) -> TargetBuilder<'_, Xbox360> {
TargetBuilder::new(self)
}
#[inline]
#[cfg(feature = "ds4")]
pub fn new_ds4_target(&self) -> TargetBuilder<'_, DualShock4> {
TargetBuilder::new(self)
}
pub(crate) fn plugin_internal<T>(
&self,
target: Target,
) -> Result<TargetHandle<T>, ClientError> {
let mut inner = self.inner.lock().expect("Client mutex was poisoned");
let mut target = target;
for serial_no in 1..=inner.max_targets {
if !inner.targets.contains_key(&serial_no) && inner.bus.plug(&target, serial_no).is_ok()
{
target.serial_no = serial_no;
inner.targets.insert(serial_no, target);
return Ok(TargetHandle::new(
serial_no,
inner.bus.clone(),
Arc::downgrade(&self.inner),
));
}
}
Err(ClientError::NoFreeSlot)
}
}
impl Drop for ClientInner {
fn drop(&mut self) {
for target in self.targets.values() {
let _ = self.bus.unplug(target.serial_no);
}
self.targets.clear();
}
}