Skip to main content

hidpp/feature/smartshift/
mod.rs

1//! Implements the `SmartShift` feature (ID `0x2110`) that allows controlling a
2//! smart shift enhanced scroll wheel.
3
4use std::{hash::Hash, sync::Arc};
5
6use num_enum::{IntoPrimitive, TryFromPrimitive};
7
8use crate::{
9    channel::HidppChannel,
10    feature::{CreatableFeature, Feature},
11    nibble::U4,
12    protocol::v20::{self, Hidpp20Error},
13};
14
15/// Implements the `SmartShift` / `0x2110` feature.
16pub struct SmartShiftFeature {
17    /// The underlying HID++ channel.
18    chan: Arc<HidppChannel>,
19
20    /// The index of the device to implement the feature for.
21    device_index: u8,
22
23    /// The index of the feature in the feature table.
24    feature_index: u8,
25}
26
27impl CreatableFeature for SmartShiftFeature {
28    const ID: u16 = 0x2110;
29    const STARTING_VERSION: u8 = 0;
30
31    fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
32        Self {
33            chan,
34            device_index,
35            feature_index,
36        }
37    }
38}
39
40impl Feature for SmartShiftFeature {}
41
42impl SmartShiftFeature {
43    /// Retrieves the current ratchet control mode.
44    ///
45    /// [`RatchetControlMode::wheel_mode`] will only reflect the value set
46    /// either by software or the wheel mode button. It will not provide
47    /// information about whether the wheel is in auto-disengaged mode.
48    pub async fn get_ratchet_control_mode(&self) -> Result<RatchetControlMode, Hidpp20Error> {
49        let response = self
50            .chan
51            .send_v20(v20::Message::Short(
52                v20::MessageHeader {
53                    device_index: self.device_index,
54                    feature_index: self.feature_index,
55                    function_id: U4::from_lo(0),
56                    software_id: self.chan.get_sw_id(),
57                },
58                [0x00, 0x00, 0x00],
59            ))
60            .await?;
61
62        let payload = response.extend_payload();
63
64        Ok(RatchetControlMode {
65            wheel_mode: WheelMode::try_from(payload[0])
66                .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
67            auto_disengage: payload[1],
68            auto_disengage_default: payload[2],
69        })
70    }
71
72    /// Sets the ratchet control mode.
73    ///
74    /// For `auto_disengage` (and `auto_disengage_default` respectively), the
75    /// values `0x01..=0xfe` correspond to the amount of quarter-turns the wheel
76    /// has to make per second for the wheel to disengage.
77    /// `0xff` enables permanent ratchet mode.
78    ///
79    /// All values are optional and will stay as they are if provided with
80    /// [`None`].
81    ///
82    /// For `auto_disengage` and `auto_disengange_default`, `0` will have the
83    /// same effect as [`None`].
84    pub async fn set_ratchet_control_mode(
85        &self,
86        wheel_mode: Option<WheelMode>,
87        auto_disengage: Option<u8>,
88        auto_disengage_default: Option<u8>,
89    ) -> Result<(), Hidpp20Error> {
90        self.chan
91            .send_v20(v20::Message::Short(
92                v20::MessageHeader {
93                    device_index: self.device_index,
94                    feature_index: self.feature_index,
95                    function_id: U4::from_lo(1),
96                    software_id: self.chan.get_sw_id(),
97                },
98                [
99                    wheel_mode.map_or(0, u8::from),
100                    auto_disengage.unwrap_or(0),
101                    auto_disengage_default.unwrap_or(0),
102                ],
103            ))
104            .await?;
105
106        Ok(())
107    }
108}
109
110/// Represents the ratchet control mode of the mouse wheel.
111#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
112#[cfg_attr(feature = "serde", derive(serde::Serialize))]
113#[non_exhaustive]
114pub struct RatchetControlMode {
115    /// The mode the wheel is currently set to.
116    ///
117    /// This does not reflect the automatic disengage state.
118    pub wheel_mode: WheelMode,
119
120    /// The amount of quarter-turns per second it takes for the wheel to
121    /// automatically disengage.
122    ///
123    /// If this value is `0xff`, the wheel will not disengage automatically.
124    pub auto_disengage: u8,
125
126    /// The default value of [`Self::auto_disengage`].
127    pub auto_disengage_default: u8,
128}
129
130/// Represents the ratchet mode of the scroll wheel.
131#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
132#[cfg_attr(feature = "serde", derive(serde::Serialize))]
133#[non_exhaustive]
134#[repr(u8)]
135pub enum WheelMode {
136    Freespin = 1,
137    Ratchet = 2,
138}