Skip to main content

hidpp/feature/
vertical_scrolling.rs

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