use crate::protocol::opcode::{
LOCK_AXIS_WHEEL, LOCK_AXIS_X, LOCK_AXIS_Y, LOCK_CLS_AXIS, LOCK_DIR_BOTH, LOCK_DIR_NEG,
LOCK_DIR_POS, LOCK_DIRBIT_NEG, LOCK_DIRBIT_POS,
};
use crate::types::{Axis, Class, Usage};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Blanket {
Aim,
Wheel,
Buttons,
Keys,
Media,
}
impl Blanket {
pub const ALL: &'static [Blanket] = &[
Blanket::Aim,
Blanket::Wheel,
Blanket::Buttons,
Blanket::Keys,
Blanket::Media,
];
pub(crate) fn clip_lock_bit(self) -> u8 {
use crate::protocol::opcode::*;
match self {
Blanket::Aim => CLIP_LOCK_AIM,
Blanket::Wheel => CLIP_LOCK_WHEEL,
Blanket::Buttons => CLIP_LOCK_BUTTONS,
Blanket::Keys => CLIP_LOCK_KEYS,
Blanket::Media => CLIP_LOCK_MEDIA,
}
}
}
pub(crate) fn blanket_scope(scope: &[Blanket]) -> u8 {
scope.iter().fold(0, |m, b| m | b.clip_lock_bit())
}
pub(crate) fn blanket_from_scope(scope: u8) -> Vec<Blanket> {
Blanket::ALL
.iter()
.copied()
.filter(|b| scope & b.clip_lock_bit() != 0)
.collect()
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LockDirection {
Both = LOCK_DIR_BOTH,
Positive = LOCK_DIR_POS,
Negative = LOCK_DIR_NEG,
}
impl LockDirection {
pub fn as_u8(self) -> u8 {
self as u8
}
pub fn from_u8(v: u8) -> Option<Self> {
Some(match v {
LOCK_DIR_BOTH => LockDirection::Both,
LOCK_DIR_POS => LockDirection::Positive,
LOCK_DIR_NEG => LockDirection::Negative,
_ => return None,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LockTarget {
Axis(Axis),
Usage(Usage),
}
impl From<Axis> for LockTarget {
fn from(a: Axis) -> LockTarget {
LockTarget::Axis(a)
}
}
impl<T: Into<Usage>> From<T> for LockTarget {
fn from(u: T) -> LockTarget {
LockTarget::Usage(u.into())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LockScope {
Target(LockTarget),
Blanket(Class),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct LockEntry {
pub scope: LockScope,
pub positive: bool,
pub negative: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Locks {
entries: Vec<LockEntry>,
}
impl Locks {
pub(crate) fn from_payload(p: &[u8]) -> Option<Locks> {
let n = *p.get(1)? as usize;
let mut entries = Vec::with_capacity(n);
for i in 0..n {
let off = 2 + 4 * i;
let cls = *p.get(off)?;
let id = u16::from_le_bytes([*p.get(off + 1)?, *p.get(off + 2)?]);
let db = *p.get(off + 3)?;
let Some(scope) = decode_scope(cls, id) else {
continue;
};
entries.push(LockEntry {
scope,
positive: db & LOCK_DIRBIT_POS != 0,
negative: db & LOCK_DIRBIT_NEG != 0,
});
}
Some(Locks { entries })
}
pub fn from_entries(entries: Vec<LockEntry>) -> Locks {
Locks { entries }
}
pub fn entries(&self) -> &[LockEntry] {
&self.entries
}
pub fn is_locked(&self, target: impl Into<LockTarget>, dir: LockDirection) -> bool {
let target = target.into();
self.entries.iter().any(|e| {
let covers = match e.scope {
LockScope::Target(t) => t == target,
LockScope::Blanket(class) => {
matches!(target, LockTarget::Usage(u) if u.class == class)
}
};
covers
&& match dir {
LockDirection::Both => e.positive && e.negative,
LockDirection::Positive => e.positive,
LockDirection::Negative => e.negative,
}
})
}
}
fn decode_scope(cls: u8, id: u16) -> Option<LockScope> {
if cls == LOCK_CLS_AXIS {
let axis = match id {
LOCK_AXIS_X => Axis::X,
LOCK_AXIS_Y => Axis::Y,
LOCK_AXIS_WHEEL => Axis::Wheel,
_ => return None,
};
Some(LockScope::Target(LockTarget::Axis(axis)))
} else if id == crate::protocol::opcode::LOCK_ID_ALL {
Some(LockScope::Blanket(Class::from_u8(cls)?))
} else {
let class = Class::from_u8(cls)?;
Some(LockScope::Target(LockTarget::Usage(Usage::new(class, id))))
}
}