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, FeatureEndpoint},
11    protocol::v20::Hidpp20Error,
12};
13
14/// Implements the `SmartShift` / `0x2110` feature.
15pub struct SmartShiftFeature {
16    /// The endpoint this feature talks to.
17    endpoint: FeatureEndpoint,
18}
19
20impl CreatableFeature for SmartShiftFeature {
21    const ID: u16 = 0x2110;
22    const STARTING_VERSION: u8 = 0;
23
24    fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
25        Self {
26            endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
27        }
28    }
29}
30
31impl Feature for SmartShiftFeature {}
32
33impl SmartShiftFeature {
34    /// Retrieves the current ratchet control mode.
35    ///
36    /// [`RatchetControlMode::wheel_mode`] will only reflect the value set
37    /// either by software or the wheel mode button. It will not provide
38    /// information about whether the wheel is in auto-disengaged mode.
39    pub async fn get_ratchet_control_mode(&self) -> Result<RatchetControlMode, Hidpp20Error> {
40        let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
41
42        Ok(RatchetControlMode {
43            wheel_mode: WheelMode::try_from(payload[0])
44                .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
45            auto_disengage: payload[1],
46            auto_disengage_default: payload[2],
47        })
48    }
49
50    /// Sets the ratchet control mode.
51    ///
52    /// For `auto_disengage` (and `auto_disengage_default` respectively), the
53    /// values `0x01..=0xfe` correspond to the amount of quarter-turns the wheel
54    /// has to make per second for the wheel to disengage.
55    /// `0xff` enables permanent ratchet mode.
56    ///
57    /// All values are optional and will stay as they are if provided with
58    /// [`None`].
59    ///
60    /// For `auto_disengage` and `auto_disengange_default`, `0` will have the
61    /// same effect as [`None`].
62    pub async fn set_ratchet_control_mode(
63        &self,
64        wheel_mode: Option<WheelMode>,
65        auto_disengage: Option<u8>,
66        auto_disengage_default: Option<u8>,
67    ) -> Result<(), Hidpp20Error> {
68        self.endpoint
69            .call(
70                1,
71                [
72                    wheel_mode.map_or(0, u8::from),
73                    auto_disengage.unwrap_or(0),
74                    auto_disengage_default.unwrap_or(0),
75                ],
76            )
77            .await?;
78
79        Ok(())
80    }
81}
82
83/// Represents the ratchet control mode of the mouse wheel.
84#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
85#[cfg_attr(feature = "serde", derive(serde::Serialize))]
86#[non_exhaustive]
87pub struct RatchetControlMode {
88    /// The mode the wheel is currently set to.
89    ///
90    /// This does not reflect the automatic disengage state.
91    pub wheel_mode: WheelMode,
92
93    /// The amount of quarter-turns per second it takes for the wheel to
94    /// automatically disengage.
95    ///
96    /// If this value is `0xff`, the wheel will not disengage automatically.
97    pub auto_disengage: u8,
98
99    /// The default value of [`Self::auto_disengage`].
100    pub auto_disengage_default: u8,
101}
102
103/// Represents the ratchet mode of the scroll wheel.
104#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
105#[cfg_attr(feature = "serde", derive(serde::Serialize))]
106#[non_exhaustive]
107#[repr(u8)]
108pub enum WheelMode {
109    Freespin = 1,
110    Ratchet = 2,
111}