Skip to main content

hidpp/feature/
smartshift.rs

1//! Implements the `SmartShift` feature (ID `0x2110`) that allows controlling a
2//! smart shift enhanced scroll wheel.
3
4use std::hash::Hash;
5use std::num::NonZeroU8;
6
7use num_enum::{IntoPrimitive, TryFromPrimitive};
8use openlogi_hidpp_derive::Feature;
9
10use crate::{feature::FeatureEndpoint, protocol::v20::Hidpp20Error};
11
12/// Implements the `SmartShift` / `0x2110` feature.
13#[derive(Feature)]
14#[creatable(id = 0x2110, version = 0)]
15pub struct SmartShiftFeature {
16    /// The endpoint this feature talks to.
17    endpoint: FeatureEndpoint,
18}
19
20impl SmartShiftFeature {
21    /// Retrieves the current ratchet control mode.
22    ///
23    /// [`RatchetControlMode::wheel_mode`] will only reflect the value set
24    /// either by software or the wheel mode button. It will not provide
25    /// information about whether the wheel is in auto-disengaged mode.
26    pub async fn get_ratchet_control_mode(&self) -> Result<RatchetControlMode, Hidpp20Error> {
27        let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
28
29        Ok(RatchetControlMode {
30            wheel_mode: WheelMode::try_from(payload[0])
31                .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
32            auto_disengage: payload[1],
33            auto_disengage_default: payload[2],
34        })
35    }
36
37    /// Sets the ratchet control mode.
38    ///
39    /// For `auto_disengage` (and `auto_disengage_default` respectively), the
40    /// values `0x01..=0xfe` correspond to the amount of quarter-turns the wheel
41    /// has to make per second for the wheel to disengage.
42    /// `0xff` enables permanent ratchet mode.
43    ///
44    /// All values are optional and will stay as they are if provided with
45    /// [`None`] — encoded as the wire's `0` sentinel, so a written value is
46    /// non-zero by construction rather than by a doc warning.
47    pub async fn set_ratchet_control_mode(
48        &self,
49        wheel_mode: Option<WheelMode>,
50        auto_disengage: Option<NonZeroU8>,
51        auto_disengage_default: Option<NonZeroU8>,
52    ) -> Result<(), Hidpp20Error> {
53        self.endpoint
54            .call(
55                1,
56                [
57                    wheel_mode.map_or(0, u8::from),
58                    auto_disengage.map_or(0, NonZeroU8::get),
59                    auto_disengage_default.map_or(0, NonZeroU8::get),
60                ],
61            )
62            .await?;
63
64        Ok(())
65    }
66}
67
68/// Represents the ratchet control mode of the mouse wheel.
69#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
70#[cfg_attr(feature = "serde", derive(serde::Serialize))]
71#[non_exhaustive]
72pub struct RatchetControlMode {
73    /// The mode the wheel is currently set to.
74    ///
75    /// This does not reflect the automatic disengage state.
76    pub wheel_mode: WheelMode,
77
78    /// The amount of quarter-turns per second it takes for the wheel to
79    /// automatically disengage.
80    ///
81    /// If this value is `0xff`, the wheel will not disengage automatically.
82    pub auto_disengage: u8,
83
84    /// The default value of [`Self::auto_disengage`].
85    pub auto_disengage_default: u8,
86}
87
88/// Represents the ratchet mode of the scroll wheel.
89#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
90#[cfg_attr(feature = "serde", derive(serde::Serialize))]
91#[non_exhaustive]
92#[repr(u8)]
93pub enum WheelMode {
94    /// Free-spin wheel mode.
95    Freespin = 1,
96    /// Ratchet wheel mode.
97    Ratchet = 2,
98}