use hidapi::{HidApi, HidDevice};
use crate::{
colour::Rgb, consts::STEELSERIES_APS, error::{DeviceError, MouseError, Result}, keyboard::{packet::{ColourPacket, KeyboardPacket}}
};
pub mod device_type;
pub(crate) mod consts;
pub(crate) mod packet;
pub use device_type::KeyboardDevice;
pub struct Keyboard {
device: HidDevice,
api: HidApi,
device_type: KeyboardDevice,
colours: ColourPacket,
}
impl std::fmt::Debug for Keyboard {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Keyboard")
.field("device_type", &self.device_type)
.field("colours", &self.colours)
.finish_non_exhaustive()
}
}
impl Keyboard {
pub fn new(device_type: KeyboardDevice) -> 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,
colours: ColourPacket::new(device_type)
})
}
pub fn get_type(&self) -> KeyboardDevice {
self.device_type
}
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_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: KeyboardPacket>(&self, data: &T) -> Result<usize> {
Ok(self.device.write(&data.serialize()?)?)
}
}