use hidapi::{HidApi, HidDevice};
use std::{
collections::HashMap,
thread::{self, sleep, JoinHandle},
time::Duration,
};
use crate::property_helpers::{Property, ValueType};
const VENDOR_ID: u16 = 1356;
const PRODUCT_ID: u16 = 3302;
const PACKET_SIZE: usize = 64;
pub struct DualSense {
device: HidDevice,
callbacks: HashMap<Property, Vec<Box<dyn Fn(ValueType) + Send>>>,
cache: HashMap<Property, ValueType>,
}
impl DualSense {
pub fn new() -> Self {
let api = HidApi::new().unwrap();
let device = api.open(VENDOR_ID, PRODUCT_ID).unwrap();
Self {
device,
callbacks: HashMap::new(),
cache: HashMap::new(),
}
}
pub fn run(mut self) -> JoinHandle<()> {
thread::spawn(move || loop {
let mut buf = [0u8; PACKET_SIZE];
let bytes_read = self.device.read(&mut buf);
match bytes_read {
Ok(PACKET_SIZE) => {}
Ok(actual_size) => {
eprintln!("Packet size mismatch, ignoring values ({actual_size})");
continue;
}
Err(e) => {
eprintln!("Error on read, ignoring values {e}");
continue;
}
}
self.packet_received(&buf);
sleep(Duration::from_millis(100));
})
}
pub fn on_left_pad_x_changed<F>(&mut self, cb: &'static F)
where
F: Fn(u8) + Send + Sync,
{
self.register_u8(Property::LeftPadX, cb);
}
pub fn on_left_pad_y_changed<F>(&mut self, cb: &'static F)
where
F: Fn(u8) + Send + Sync,
{
self.register_u8(Property::LeftPadY, cb);
}
pub fn on_right_pad_x_changed<F>(&mut self, cb: &'static F)
where
F: Fn(u8) + Send + Sync,
{
self.register_u8(Property::RightPadX, cb);
}
pub fn on_right_pad_y_changed<F>(&mut self, cb: &'static F)
where
F: Fn(u8) + Send + Sync,
{
self.register_u8(Property::RightPadY, cb);
}
pub fn on_l2_changed<F>(&mut self, cb: &'static F)
where
F: Fn(u8) + Send + Sync,
{
self.register_u8(Property::L2, cb);
}
pub fn on_r2_changed<F>(&mut self, cb: &'static F)
where
F: Fn(u8) + Send + Sync,
{
self.register_u8(Property::R2, cb);
}
fn register_u8<F>(&mut self, prop: Property, cb: &'static F)
where
F: Fn(u8) + Send + Sync,
{
self.callbacks
.entry(prop)
.or_insert_with(|| vec![])
.push(Box::new(move |x| cb(x.to_u8())));
}
fn packet_received(&mut self, data: &[u8; 64]) {
self.callbacks.iter().for_each(|(prop, cbs)| {
let new_val = prop.convert(&data.as_slice()[prop.offset().byte]);
let mut update = false;
match self.cache.get_mut(prop) {
Some(old_val) if old_val != &new_val => {
update = true;
}
None => {
update = true;
}
_ => {}
}
if update {
self.cache.insert(*prop, new_val);
cbs.iter().for_each(|cb| cb(new_val));
}
})
}
}