use hidapi::{HidApi, HidDevice};
use crate::{
colour::Rgb, consts::STEELSERIES_APS, error::{DeviceError, MouseError, Result}, mouse::{packet::{ColourPacket, MousePacket, PersistPacket, PollingPacket}, polling_rate::PollingRate}
};
pub mod device_type;
pub(crate) mod consts;
pub(crate) mod packet;
pub mod polling_rate;
pub use device_type::MouseDevice;
pub struct Mouse {
device: HidDevice,
api: HidApi,
device_type: MouseDevice,
polling_rate: PollingRate,
colours: ColourPacket,
}
impl std::fmt::Debug for Mouse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Mouse")
.field("device_type", &self.device_type)
.field("polling_rate", &self.polling_rate)
.field("colours", &self.colours)
.finish_non_exhaustive()
}
}
impl Mouse {
pub fn new(device_type: MouseDevice) -> Result<Self> {
let api = HidApi::new()?;
let info = api
.device_list()
.find(|d| {
d.vendor_id() == STEELSERIES_APS
&& d.product_id() == device_type.product_id()
&& d.interface_number() == device_type.interface()
})
.ok_or(DeviceError::NotFound)?;
#[cfg(feature = "default")]
tracing::info!(
"found {} (VID=0x{:04X}, PID=0x{:04X}, interface={})",
info.product_string().unwrap_or("Unknown"),
info.vendor_id(),
info.product_id(),
info.interface_number(),
);
let device = info.open_device(&api)?;
Ok(Self {
api,
device_type,
device,
polling_rate: 1000.into(),
colours: ColourPacket::new(device_type)
})
}
pub fn get_type(&self) -> MouseDevice {
self.device_type
}
pub fn persist(&self) -> Result<usize> {
self.send_packet(&PersistPacket)
}
pub fn polling_rate(&mut self, rate: PollingRate) -> Result<usize> {
self.polling_rate = rate;
self.send_packet(&PollingPacket(rate))
}
pub fn set_zone(&mut self, zone: usize, colour: Rgb) -> Result<usize> {
self.colours.change_zone(zone, colour)?;
self.send_packet(&self.colours)
}
pub fn set_colours(&mut self, colours: &[Rgb]) -> Result<usize> {
if colours.len() > self.device_type.get_zones() {
return Err(MouseError::ZoneOutOfRange.into());
}
for (i, zone) in colours.iter().enumerate() {
self.colours.change_zone(i, *zone)?;
}
self.send_packet(&self.colours)
}
pub fn get_polling_rate(&self) -> u16 {
self.polling_rate.0
}
pub fn get_colours(&self) -> Vec<Rgb> {
self.colours.get_colours()
}
pub fn get_zone(&self, zone: usize) -> Result<Rgb> {
self.colours.get_zone(zone)
}
fn send_packet<T: MousePacket>(&self, data: &T) -> Result<usize> {
Ok(self.device.write(&data.serialize()?)?)
}
}