use std::sync::Arc;
use num_enum::TryFromPrimitive;
use crate::{
channel::HidppChannel,
feature::{CreatableFeature, Feature, FeatureEndpoint},
protocol::v20::Hidpp20Error,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
#[repr(u8)]
pub enum RollerType {
Standard = 0x01,
ThreeG = 0x03,
MicroRatchet = 0x04,
Touchpad = 0x05,
TouchpadNaturalDefault = 0x06,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum ScrollLines {
SystemDefault,
Lines(u8),
Page,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct RollerInfo {
pub roller_type: RollerType,
pub ratchets_per_turn: u8,
pub scroll_lines: ScrollLines,
}
#[derive(Clone)]
pub struct VerticalScrollingFeature {
endpoint: FeatureEndpoint,
}
impl CreatableFeature for VerticalScrollingFeature {
const ID: u16 = 0x2100;
const STARTING_VERSION: u8 = 0;
fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
Self {
endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
}
}
}
impl Feature for VerticalScrollingFeature {}
impl VerticalScrollingFeature {
pub async fn get_roller_info(&self) -> Result<RollerInfo, Hidpp20Error> {
let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
RollerInfo::from_payload(payload)
}
}
impl RollerInfo {
fn from_payload(payload: [u8; 16]) -> Result<Self, Hidpp20Error> {
Ok(Self {
roller_type: RollerType::try_from(payload[0])
.map_err(|_| Hidpp20Error::UnsupportedResponse)?,
ratchets_per_turn: payload[1],
scroll_lines: ScrollLines::from(payload[2]),
})
}
}
impl From<u8> for ScrollLines {
fn from(value: u8) -> Self {
match value {
0x00 => Self::SystemDefault,
0xff => Self::Page,
lines => Self::Lines(lines),
}
}
}
#[cfg(test)]
mod tests {
use super::{RollerInfo, RollerType, ScrollLines};
#[test]
fn parses_roller_info() {
let mut payload = [0; 16];
payload[0] = 0x04;
payload[1] = 24;
payload[2] = 0xff;
let info = RollerInfo::from_payload(payload).unwrap();
assert_eq!(info.roller_type, RollerType::MicroRatchet);
assert_eq!(info.ratchets_per_turn, 24);
assert_eq!(info.scroll_lines, ScrollLines::Page);
}
}