Skip to main content

hidpp/feature/vertical_scrolling/
mod.rs

1//! Implements `VerticalScrolling` (feature `0x2100`).
2
3use std::sync::Arc;
4
5use num_enum::TryFromPrimitive;
6
7use crate::{
8    channel::HidppChannel,
9    feature::{CreatableFeature, Feature, FeatureEndpoint},
10    protocol::v20::Hidpp20Error,
11};
12
13/// Roller type reported by `VerticalScrolling`.
14#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TryFromPrimitive)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize))]
16#[non_exhaustive]
17#[repr(u8)]
18pub enum RollerType {
19    /// Standard one- or two-dimensional roller.
20    Standard = 0x01,
21    /// 3G roller.
22    ThreeG = 0x03,
23    /// Micro-ratchet roller.
24    MicroRatchet = 0x04,
25    /// Touchpad scrolling.
26    Touchpad = 0x05,
27    /// Touchpad with natural scrolling enabled by default.
28    TouchpadNaturalDefault = 0x06,
29}
30
31/// Number of lines scrolled for a wheel movement.
32#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize))]
34#[non_exhaustive]
35pub enum ScrollLines {
36    /// Do not change the host system setting.
37    SystemDefault,
38    /// Scroll this many lines per movement.
39    Lines(u8),
40    /// Scroll a full page or screen per movement.
41    Page,
42}
43
44/// Vertical scrolling roller information.
45#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize))]
47#[non_exhaustive]
48pub struct RollerInfo {
49    /// Roller type.
50    pub roller_type: RollerType,
51    /// Number of ratchets per wheel turn.
52    pub ratchets_per_turn: u8,
53    /// Scroll-line behavior.
54    pub scroll_lines: ScrollLines,
55}
56
57/// Implements the `VerticalScrolling` / `0x2100` feature.
58#[derive(Clone)]
59pub struct VerticalScrollingFeature {
60    /// The endpoint this feature talks to.
61    endpoint: FeatureEndpoint,
62}
63
64impl CreatableFeature for VerticalScrollingFeature {
65    const ID: u16 = 0x2100;
66    const STARTING_VERSION: u8 = 0;
67
68    fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
69        Self {
70            endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
71        }
72    }
73}
74
75impl Feature for VerticalScrollingFeature {}
76
77impl VerticalScrollingFeature {
78    /// Retrieves roller information.
79    pub async fn get_roller_info(&self) -> Result<RollerInfo, Hidpp20Error> {
80        let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
81        RollerInfo::from_payload(payload)
82    }
83}
84
85impl RollerInfo {
86    fn from_payload(payload: [u8; 16]) -> Result<Self, Hidpp20Error> {
87        Ok(Self {
88            roller_type: RollerType::try_from(payload[0])
89                .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
90            ratchets_per_turn: payload[1],
91            scroll_lines: ScrollLines::from(payload[2]),
92        })
93    }
94}
95
96impl From<u8> for ScrollLines {
97    fn from(value: u8) -> Self {
98        match value {
99            0x00 => Self::SystemDefault,
100            0xff => Self::Page,
101            lines => Self::Lines(lines),
102        }
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::{RollerInfo, RollerType, ScrollLines};
109
110    #[test]
111    fn parses_roller_info() {
112        let mut payload = [0; 16];
113        payload[0] = 0x04;
114        payload[1] = 24;
115        payload[2] = 0xff;
116
117        let info = RollerInfo::from_payload(payload).unwrap();
118
119        assert_eq!(info.roller_type, RollerType::MicroRatchet);
120        assert_eq!(info.ratchets_per_turn, 24);
121        assert_eq!(info.scroll_lines, ScrollLines::Page);
122    }
123}