use std::sync::Arc;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use crate::{
channel::HidppChannel,
feature::{CreatableFeature, Feature, FeatureEndpoint},
protocol::v20::Hidpp20Error,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
#[repr(u8)]
pub enum PointerAcceleration {
None = 0,
Low = 1,
Medium = 2,
High = 3,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct MousePointerInfo {
pub sensor_resolution: u16,
pub pointer_acceleration: PointerAcceleration,
pub suggest_os_ballistics: bool,
pub suggest_vertical_tuning: bool,
}
#[derive(Clone)]
pub struct MousePointerFeature {
endpoint: FeatureEndpoint,
}
impl CreatableFeature for MousePointerFeature {
const ID: u16 = 0x2200;
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 MousePointerFeature {}
impl MousePointerFeature {
pub async fn get_mouse_pointer_info(&self) -> Result<MousePointerInfo, Hidpp20Error> {
let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
MousePointerInfo::from_payload(payload)
}
}
impl MousePointerInfo {
fn from_payload(payload: [u8; 16]) -> Result<Self, Hidpp20Error> {
let flags = payload[2];
Ok(Self {
sensor_resolution: u16::from_be_bytes([payload[0], payload[1]]),
pointer_acceleration: PointerAcceleration::try_from(flags & 0b11)
.map_err(|_| Hidpp20Error::UnsupportedResponse)?,
suggest_os_ballistics: flags & (1 << 2) != 0,
suggest_vertical_tuning: flags & (1 << 3) != 0,
})
}
}
#[cfg(test)]
mod tests {
use super::{MousePointerInfo, PointerAcceleration};
#[test]
fn decodes_resolution_and_flags() {
let mut payload = [0; 16];
payload[0..2].copy_from_slice(&1600u16.to_be_bytes());
payload[2] = 0b0000_0111;
let info = MousePointerInfo::from_payload(payload).unwrap();
assert_eq!(info.sensor_resolution, 1600);
assert_eq!(info.pointer_acceleration, PointerAcceleration::High);
assert!(info.suggest_os_ballistics);
assert!(!info.suggest_vertical_tuning);
}
#[test]
fn decodes_trackball_vertical_tuning() {
let mut payload = [0; 16];
payload[0..2].copy_from_slice(&400u16.to_be_bytes());
payload[2] = 0b0000_1000;
let info = MousePointerInfo::from_payload(payload).unwrap();
assert_eq!(info.pointer_acceleration, PointerAcceleration::None);
assert!(!info.suggest_os_ballistics);
assert!(info.suggest_vertical_tuning);
}
}