Skip to main content

hidpp/feature/touch_mouse_raw/
mod.rs

1//! Implements the `TouchMouseRaw` feature (ID `0x6110`) that exposes a touch
2//! mouse's raw touch points: pad characteristics, the raw-data mode, and the
3//! raw-data / status events.
4
5pub mod event;
6
7#[cfg(test)]
8mod tests;
9
10use std::sync::Arc;
11
12use num_enum::{IntoPrimitive, TryFromPrimitive};
13
14pub use event::{TouchMousePoint, TouchMouseRawEvent, TouchMouseStatus};
15
16use crate::{
17    channel::{HidppChannel, MessageListenerGuard},
18    event::EventEmitter,
19    feature::{CreatableFeature, EmittingFeature, Feature, FeatureEndpoint, event_payload},
20    protocol::v20::Hidpp20Error,
21};
22
23/// The position of the touch surface's coordinate origin, viewed from above.
24#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize))]
26#[non_exhaustive]
27#[repr(u8)]
28pub enum Origin {
29    /// Lower-left corner.
30    LowerLeft = 1,
31    /// Lower-right corner.
32    LowerRight = 2,
33    /// Upper-left corner.
34    UpperLeft = 3,
35    /// Upper-right corner.
36    UpperRight = 4,
37}
38
39/// The raw-reporting mode of a touch mouse.
40#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
41#[cfg_attr(feature = "serde", derive(serde::Serialize))]
42#[non_exhaustive]
43#[repr(u8)]
44pub enum RawMode {
45    /// Native gestures only (out of the box).
46    NativeGestures = 0,
47    /// Filtered raw data.
48    RawFiltered = 1,
49    /// Unfiltered raw data plus native gestures.
50    RawUnfilteredAndGestures = 2,
51    /// Unfiltered raw data, sent even while lifted or with a button active.
52    RawUnfilteredAlways = 3,
53    /// Like [`RawUnfilteredAndGestures`](Self::RawUnfilteredAndGestures) but with
54    /// Z information in place of width.
55    RawUnfilteredWithZ = 4,
56}
57
58/// Touch-mouse characteristics from
59/// [`get_touchpad_info`](TouchMouseRawFeature::get_touchpad_info).
60#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
61#[cfg_attr(feature = "serde", derive(serde::Serialize))]
62#[non_exhaustive]
63pub struct TouchMouseInfo {
64    /// Maximum X count in dots.
65    pub x_max_count: u16,
66    /// Maximum Y count in dots.
67    pub y_max_count: u16,
68    /// Sensor resolution in DPI (assumed equal for X and Y).
69    pub resolution_dpi: u16,
70    /// Position of the coordinate origin.
71    pub origin: Origin,
72    /// Maximum number of reported fingers.
73    pub max_finger_count: u8,
74    /// Maximum value of the touch-point width/height data.
75    pub width_height_data_range: u8,
76}
77
78impl TouchMouseInfo {
79    fn from_payload(payload: &[u8; 16]) -> Result<Self, Hidpp20Error> {
80        Ok(Self {
81            x_max_count: u16::from_be_bytes([payload[0], payload[1]]),
82            y_max_count: u16::from_be_bytes([payload[2], payload[3]]),
83            resolution_dpi: u16::from_be_bytes([payload[4], payload[5]]),
84            origin: Origin::try_from(payload[6]).map_err(|_| Hidpp20Error::UnsupportedResponse)?,
85            max_finger_count: payload[7],
86            width_height_data_range: payload[8],
87        })
88    }
89}
90
91/// Implements the `TouchMouseRaw` / `0x6110` feature.
92pub struct TouchMouseRawFeature {
93    /// The endpoint this feature talks to.
94    endpoint: FeatureEndpoint,
95
96    /// The emitter used to publish decoded events.
97    emitter: Arc<EventEmitter<TouchMouseRawEvent>>,
98
99    /// Removes the message listener when the feature is dropped.
100    _msg_listener: MessageListenerGuard,
101}
102
103impl CreatableFeature for TouchMouseRawFeature {
104    const ID: u16 = 0x6110;
105    const STARTING_VERSION: u8 = 0;
106
107    fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
108        let emitter = Arc::new(EventEmitter::new());
109
110        let listener = chan.add_msg_listener_guarded({
111            let emitter = Arc::clone(&emitter);
112
113            move |raw, matched| {
114                let Some((func, payload)) =
115                    event_payload(raw, matched, device_index, feature_index)
116                else {
117                    return;
118                };
119                if let Some(event) = event::decode_event(func.to_lo(), &payload) {
120                    emitter.emit(event);
121                }
122            }
123        });
124
125        Self {
126            endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
127            emitter,
128            _msg_listener: listener,
129        }
130    }
131}
132
133impl Feature for TouchMouseRawFeature {}
134
135impl EmittingFeature<TouchMouseRawEvent> for TouchMouseRawFeature {
136    fn listen(&self) -> async_channel::Receiver<TouchMouseRawEvent> {
137        self.emitter.create_receiver()
138    }
139}
140
141impl TouchMouseRawFeature {
142    /// Retrieves the touch mouse's characteristics.
143    pub async fn get_touchpad_info(&self) -> Result<TouchMouseInfo, Hidpp20Error> {
144        let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
145        TouchMouseInfo::from_payload(&payload)
146    }
147
148    /// Retrieves the current raw-reporting mode.
149    pub async fn get_raw_mode(&self) -> Result<RawMode, Hidpp20Error> {
150        let payload = self.endpoint.call(1, [0; 3]).await?.extend_payload();
151        RawMode::try_from(payload[0]).map_err(|_| Hidpp20Error::UnsupportedResponse)
152    }
153
154    /// Sets the raw-reporting mode.
155    ///
156    /// A raw mode must be selected for [`TouchMouseRawEvent::RawData`] events to
157    /// be emitted.
158    pub async fn set_raw_mode(&self, mode: RawMode) -> Result<(), Hidpp20Error> {
159        self.endpoint.call(2, [mode.into(), 0, 0]).await?;
160        Ok(())
161    }
162}