Skip to main content

hidpp/feature/mode_status/
mod.rs

1//! Implements `ModeStatus` (feature `0x8090`).
2
3use std::sync::Arc;
4
5use crate::{
6    channel::HidppChannel,
7    feature::{CreatableFeature, Feature, FeatureEndpoint},
8    protocol::v20::Hidpp20Error,
9};
10
11bitflags::bitflags! {
12    /// The first mode-status byte.
13    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14    #[cfg_attr(feature = "serde", derive(serde::Serialize))]
15    pub struct ModeStatus0: u8 {
16        /// Performance mode. When unset, the device is in endurance mode.
17        const PERFORMANCE = 1 << 0;
18    }
19}
20
21bitflags::bitflags! {
22    /// Capabilities reported by `ModeStatus`.
23    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
24    #[cfg_attr(feature = "serde", derive(serde::Serialize))]
25    pub struct ModeStatusCapabilities: u16 {
26        /// A hardware switch can change the mode bit.
27        const HARDWARE_SWITCH = 1 << 0;
28        /// Software can change the mode bit.
29        const SOFTWARE_SWITCH = 1 << 1;
30    }
31}
32
33/// Current mode-status bytes.
34#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
35#[cfg_attr(feature = "serde", derive(serde::Serialize))]
36#[non_exhaustive]
37pub struct ModeStatus {
38    /// Primary status bits.
39    pub status0: ModeStatus0,
40    /// Secondary status byte, reserved by v1 but preserved for callers.
41    pub status1: u8,
42}
43
44/// A mode-status update request.
45#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize))]
47pub struct ModeStatusChange {
48    /// Desired primary status bits.
49    pub status0: ModeStatus0,
50    /// Desired secondary status byte.
51    pub status1: u8,
52    /// Primary changed-bit mask.
53    pub changed_mask0: ModeStatus0,
54    /// Secondary changed-bit mask.
55    pub changed_mask1: u8,
56}
57
58/// Implements the `ModeStatus` / `0x8090` feature.
59#[derive(Clone)]
60pub struct ModeStatusFeature {
61    /// The endpoint this feature talks to.
62    endpoint: FeatureEndpoint,
63}
64
65impl CreatableFeature for ModeStatusFeature {
66    const ID: u16 = 0x8090;
67    const STARTING_VERSION: u8 = 1;
68
69    fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
70        Self {
71            endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
72        }
73    }
74}
75
76impl Feature for ModeStatusFeature {}
77
78impl ModeStatusFeature {
79    /// Retrieves the current mode status.
80    pub async fn get_mode_status(&self) -> Result<ModeStatus, Hidpp20Error> {
81        let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
82        Ok(ModeStatus {
83            status0: ModeStatus0::from_bits_retain(payload[0]),
84            status1: payload[1],
85        })
86    }
87
88    /// Sets selected mode-status bits.
89    pub async fn set_mode_status(&self, change: ModeStatusChange) -> Result<(), Hidpp20Error> {
90        let mut args = [0; 16];
91        args[0] = change.status0.bits();
92        args[1] = change.status1;
93        args[2] = change.changed_mask0.bits();
94        args[3] = change.changed_mask1;
95
96        self.endpoint.call_long(1, args).await?;
97        Ok(())
98    }
99
100    /// Enables or disables performance mode.
101    pub async fn set_performance_mode(&self, enabled: bool) -> Result<(), Hidpp20Error> {
102        let status0 = if enabled {
103            ModeStatus0::PERFORMANCE
104        } else {
105            ModeStatus0::empty()
106        };
107        self.set_mode_status(ModeStatusChange {
108            status0,
109            status1: 0,
110            changed_mask0: ModeStatus0::PERFORMANCE,
111            changed_mask1: 0,
112        })
113        .await
114    }
115
116    /// Retrieves device capabilities for mode switching.
117    pub async fn get_device_config(&self) -> Result<ModeStatusCapabilities, Hidpp20Error> {
118        let payload = self.endpoint.call(2, [0; 3]).await?.extend_payload();
119        Ok(ModeStatusCapabilities::from_bits_retain(
120            u16::from_be_bytes([payload[0], payload[1]]),
121        ))
122    }
123}