use hidpp::protocol::v20;
use super::{ControlId, ReprogControlsEvent, decode_full_event};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RawControlEvent {
DivertedButtons([u16; 4]),
RawXy {
dx: i16,
dy: i16,
},
}
impl RawControlEvent {
#[must_use]
pub fn is_pressed(&self, cid: u16) -> bool {
matches!(self, Self::DivertedButtons(cids) if cids.contains(&cid))
}
}
impl TryFrom<ReprogControlsEvent> for RawControlEvent {
type Error = ReprogControlsEvent;
fn try_from(event: ReprogControlsEvent) -> Result<Self, Self::Error> {
match event {
ReprogControlsEvent::DivertedButtons(cids) => {
Ok(Self::DivertedButtons(cids.map(ControlId::into)))
}
ReprogControlsEvent::DivertedRawMouseXy { dx, dy } => Ok(Self::RawXy { dx, dy }),
ReprogControlsEvent::AnalyticsKeyEvents(_)
| ReprogControlsEvent::DivertedRawWheel { .. } => Err(event),
}
}
}
#[must_use]
pub fn decode_event(
msg: &v20::Message,
device_index: u8,
feature_index: u8,
) -> Option<RawControlEvent> {
decode_full_event(msg, device_index, feature_index)?
.try_into()
.ok()
}
#[cfg(test)]
mod tests {
use hidpp::{nibble::U4, protocol::v20};
use super::*;
use crate::reprog_controls::GESTURE_BUTTON_CID;
fn event(function_id: u8, software_id: u8, payload: [u8; 16]) -> v20::Message {
v20::Message::Long(
v20::MessageHeader {
device_index: 2,
feature_index: 7,
function_id: U4::from_lo(function_id),
software_id: U4::from_lo(software_id),
},
payload,
)
}
#[test]
fn decodes_diverted_buttons() {
let mut p = [0u8; 16];
p[0..2].copy_from_slice(&GESTURE_BUTTON_CID.to_be_bytes());
let decoded = decode_event(&event(0, 0, p), 2, 7);
assert_eq!(
decoded,
Some(RawControlEvent::DivertedButtons([
GESTURE_BUTTON_CID,
0,
0,
0,
]))
);
assert!(decoded.is_some_and(|e| e.is_pressed(GESTURE_BUTTON_CID)));
}
#[test]
fn decodes_signed_raw_xy() {
let mut p = [0u8; 16];
p[0..2].copy_from_slice(&(-5i16).to_be_bytes());
p[2..4].copy_from_slice(&12i16.to_be_bytes());
assert_eq!(
decode_event(&event(1, 0, p), 2, 7),
Some(RawControlEvent::RawXy { dx: -5, dy: 12 })
);
}
#[test]
fn ignores_responses_and_foreign_messages() {
let p = [0u8; 16];
assert_eq!(decode_event(&event(0, 5, p), 2, 7), None);
assert_eq!(decode_event(&event(2, 0, p), 2, 7), None);
assert_eq!(decode_event(&event(0, 0, p), 2, 9), None);
}
}