use crate::protocol::opcode::{TF_F_FULL, TF_OP_COUNT, TF_REMAP, TF_SWAP, TRANSFORM_MAX_ENTRIES};
use crate::types::{Class, LockTarget};
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TransformOp {
Remap = TF_REMAP,
Swap = TF_SWAP,
}
const _: () = assert!(TF_SWAP + 1 == TF_OP_COUNT);
impl TransformOp {
pub const fn as_u8(self) -> u8 {
self as u8
}
pub fn from_u8(v: u8) -> Option<TransformOp> {
Some(match v {
TF_REMAP => TransformOp::Remap,
TF_SWAP => TransformOp::Swap,
_ => return None,
})
}
pub fn admits(self, source: LockTarget, dest: LockTarget) -> bool {
use LockTarget::{Axis, Usage};
if source == dest {
return false;
}
match self {
TransformOp::Swap => matches!((source, dest), (Axis(_), Axis(_))),
TransformOp::Remap => match (source, dest) {
(Axis(_), Axis(_)) => true,
(Usage(s), Usage(d)) => {
s.class == Class::Button
&& matches!(d.class, Class::Button | Class::Key | Class::Media)
}
_ => false,
},
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TransformKey {
pub source: LockTarget,
pub dest: LockTarget,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Transform {
pub op: TransformOp,
pub source: LockTarget,
pub dest: LockTarget,
}
impl Transform {
pub fn new(
op: TransformOp,
source: impl Into<LockTarget>,
dest: impl Into<LockTarget>,
) -> Transform {
Transform {
op,
source: source.into(),
dest: dest.into(),
}
}
pub fn swap(a: crate::types::Axis, b: crate::types::Axis) -> Transform {
Transform {
op: TransformOp::Swap,
source: LockTarget::Axis(a),
dest: LockTarget::Axis(b),
}
}
pub fn remap(source: impl Into<LockTarget>, dest: impl Into<LockTarget>) -> Transform {
Transform {
op: TransformOp::Remap,
source: source.into(),
dest: dest.into(),
}
}
pub fn key(&self) -> TransformKey {
TransformKey {
source: self.source,
dest: self.dest,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Transforms {
pub table_full: bool,
pub entries: Vec<Transform>,
}
impl Transforms {
pub const CAPACITY: usize = TRANSFORM_MAX_ENTRIES;
pub(crate) fn from_payload(p: &[u8]) -> Option<Transforms> {
if p.len() < 3 {
return None;
}
let table_full = p[1] & TF_F_FULL != 0;
let n = p[2] as usize;
let mut entries = Vec::with_capacity(n);
for i in 0..n {
let o = 3 + 7 * i;
let row = p.get(o..o + 7)?;
let (Some(op), Some(source), Some(dest)) = (
TransformOp::from_u8(row[0]),
LockTarget::from_class_id(row[1], u16::from_le_bytes([row[2], row[3]])),
LockTarget::from_class_id(row[4], u16::from_le_bytes([row[5], row[6]])),
) else {
continue;
};
entries.push(Transform { op, source, dest });
}
Some(Transforms {
table_full,
entries,
})
}
}