pub mod event;
#[cfg(test)]
mod tests;
use std::sync::Arc;
use num_enum::{IntoPrimitive, TryFromPrimitive};
pub use event::{DualXyData, TouchPoint, TouchpadRawEvent};
use crate::{
channel::{HidppChannel, MessageListenerGuard},
event::EventEmitter,
feature::{CreatableFeature, EmittingFeature, Feature, FeatureEndpoint, event_payload},
protocol::v20::Hidpp20Error,
};
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct RawReportFlags: u8 {
const RAW = 1 << 0;
const FORCE_ADD = 1 << 1;
const ENHANCED = 1 << 2;
const WIDTH_HEIGHT = 1 << 3;
const NATIVE_GESTURE = 1 << 4;
const MAJOR_MINOR = 1 << 5;
const WIDTH_HEIGHT_8BIT = 1 << 6;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
#[repr(u8)]
pub enum Origin {
LowerLeft = 1,
LowerRight = 2,
UpperLeft = 3,
UpperRight = 4,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct TouchpadInfo {
pub x_size: u16,
pub y_size: u16,
pub z_data_range: u8,
pub area_data_range: u8,
pub timestamp_units: u8,
pub max_finger_count: u8,
pub origin: Origin,
pub pen_support: bool,
pub raw_report_mapping_version: u8,
pub dpi: u16,
}
impl TouchpadInfo {
fn from_payload(payload: &[u8; 16]) -> Result<Self, Hidpp20Error> {
Ok(Self {
x_size: u16::from_be_bytes([payload[0], payload[1]]),
y_size: u16::from_be_bytes([payload[2], payload[3]]),
z_data_range: payload[4],
area_data_range: payload[5],
timestamp_units: payload[6],
max_finger_count: payload[7],
origin: Origin::try_from(payload[8]).map_err(|_| Hidpp20Error::UnsupportedResponse)?,
pen_support: payload[9] != 0,
raw_report_mapping_version: payload[12],
dpi: u16::from_be_bytes([payload[13], payload[14]]),
})
}
}
pub struct TouchpadRawXyFeature {
endpoint: FeatureEndpoint,
emitter: Arc<EventEmitter<TouchpadRawEvent>>,
_msg_listener: MessageListenerGuard,
}
impl CreatableFeature for TouchpadRawXyFeature {
const ID: u16 = 0x6100;
const STARTING_VERSION: u8 = 0;
fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
let emitter = Arc::new(EventEmitter::new());
let listener = chan.add_msg_listener_guarded({
let emitter = Arc::clone(&emitter);
move |raw, matched| {
let Some((func, payload)) =
event_payload(raw, matched, device_index, feature_index)
else {
return;
};
if let Some(event) = event::decode_event(func.to_lo(), &payload) {
emitter.emit(event);
}
}
});
Self {
endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
emitter,
_msg_listener: listener,
}
}
}
impl Feature for TouchpadRawXyFeature {}
impl EmittingFeature<TouchpadRawEvent> for TouchpadRawXyFeature {
fn listen(&self) -> async_channel::Receiver<TouchpadRawEvent> {
self.emitter.create_receiver()
}
}
impl TouchpadRawXyFeature {
pub async fn get_touchpad_info(&self) -> Result<TouchpadInfo, Hidpp20Error> {
let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
TouchpadInfo::from_payload(&payload)
}
pub async fn get_raw_report_state(&self) -> Result<RawReportFlags, Hidpp20Error> {
let payload = self.endpoint.call(1, [0; 3]).await?.extend_payload();
Ok(RawReportFlags::from_bits_retain(payload[0]))
}
pub async fn set_raw_report_state(&self, flags: RawReportFlags) -> Result<(), Hidpp20Error> {
self.endpoint.call(2, [flags.bits(), 0, 0]).await?;
Ok(())
}
}