use std::{collections::BTreeMap, fmt};
const MAX_MOD_SLOTS: usize = 32;
pub const LEFT_CTRL: u32 = 29;
pub const LEFT_SHIFT: u32 = 42;
pub const RIGHT_SHIFT: u32 = 54;
pub const RIGHT_CTRL: u32 = 97;
pub const ALT: u32 = 56;
pub const ALTGR: u32 = 100;
pub const LOGO: u32 = 125;
pub const CAPS_LOCK: u32 = 58;
pub const NUM_LOCK: u32 = 69;
pub const SCROLL_LOCK: u32 = 70;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum KeyDirection {
Up,
Down,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ModType {
None,
Level2,
Level3,
Level5,
Compose,
Caps,
Num,
Scroll,
}
#[derive(Debug, Clone)]
pub enum ModKind {
Pressed {
pressed: bool,
mod_type: ModType,
},
Lock {
pressed: bool,
locked: u8,
mod_type: ModType,
},
Latch {
pressed: bool,
latched: bool,
mod_type: ModType,
},
None,
}
impl ModKind {
pub fn update(&mut self, key_direction: KeyDirection) {
match self {
ModKind::Pressed {
ref mut pressed,
mod_type: _,
} => match key_direction {
KeyDirection::Down => *pressed = true,
KeyDirection::Up => *pressed = false,
},
ModKind::Lock {
ref mut pressed,
ref mut locked,
mod_type: _,
} => match key_direction {
KeyDirection::Down => {
*pressed = true;
if *locked == 0 {
*locked = 2;
}
}
KeyDirection::Up => {
*pressed = false;
if *locked != 0 {
*locked -= 1;
}
}
},
ModKind::Latch {
ref mut pressed,
ref mut latched,
mod_type: _,
} => match key_direction {
KeyDirection::Down => {
*pressed = true;
*latched = !*latched;
}
KeyDirection::Up => {
*pressed = false;
}
},
ModKind::None => {}
}
}
fn unlatch(&mut self) {
if let ModKind::Latch {
pressed: _,
latched,
mod_type: _,
} = self
{
*latched = false
}
}
pub fn locked(&self) -> bool {
match self {
ModKind::Pressed {
pressed: _,
mod_type: _,
} => false,
ModKind::Lock {
pressed: _,
locked,
mod_type: _,
} => locked > &0,
ModKind::Latch {
pressed: _,
latched: _,
mod_type: _,
} => false,
ModKind::None => false,
}
}
pub fn is_active(&self) -> bool {
match self {
ModKind::Pressed {
pressed,
mod_type: _,
} => *pressed,
ModKind::Lock {
pressed: _,
locked,
mod_type: _,
} => locked > &0,
ModKind::Latch {
pressed: _,
latched,
mod_type: _,
} => *latched,
ModKind::None => false,
}
}
pub(crate) fn get_modkind_from_modtype(&self, mod_type: ModType) -> Option<ModKind> {
match self {
ModKind::Pressed { mod_type: m_t, .. }
| ModKind::Lock { mod_type: m_t, .. }
| ModKind::Latch { mod_type: m_t, .. } => {
if *m_t == mod_type {
Some(self.clone())
} else {
None
}
}
ModKind::None => None,
}
}
}
#[derive(Debug, Clone)]
pub enum Modifier {
Single(ModKind),
Leveled(BTreeMap<u8, ModKind>),
}
#[derive(Debug, Clone)]
pub struct Modifiers {
entries: Vec<(u32, Modifier)>,
}
impl fmt::Display for Modifiers {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (code, modifier) in &self.entries {
write!(f, "code {}: ", code)?;
match modifier {
Modifier::Single(mod_kind) => {
write!(f, "{:?}", mod_kind)?;
}
Modifier::Leveled(map) => {
writeln!(f, "[")?;
for (index, mod_kind) in map {
writeln!(f, " index {}: {:?}, ", index, mod_kind)?;
}
write!(f, "]")?;
}
}
writeln!(f)?;
}
Ok(())
}
}
impl Default for Modifiers {
fn default() -> Self {
let entries = vec![
(
LEFT_CTRL,
Modifier::Single(ModKind::Pressed {
pressed: false,
mod_type: ModType::None,
}),
),
(
RIGHT_CTRL,
Modifier::Single(ModKind::Pressed {
pressed: false,
mod_type: ModType::None,
}),
),
(
LEFT_SHIFT,
Modifier::Single(ModKind::Pressed {
pressed: false,
mod_type: ModType::Level2,
}),
),
(
RIGHT_SHIFT,
Modifier::Single(ModKind::Pressed {
pressed: false,
mod_type: ModType::Level2,
}),
),
(
ALT,
Modifier::Single(ModKind::Pressed {
pressed: false,
mod_type: ModType::None,
}),
),
(
ALTGR,
Modifier::Single(ModKind::Pressed {
pressed: false,
mod_type: ModType::None,
}),
),
(
LOGO,
Modifier::Single(ModKind::Pressed {
pressed: false,
mod_type: ModType::None,
}),
),
(
CAPS_LOCK,
Modifier::Single(ModKind::Lock {
pressed: false,
locked: 0,
mod_type: ModType::Caps,
}),
),
(
NUM_LOCK,
Modifier::Single(ModKind::Lock {
pressed: false,
locked: 0,
mod_type: ModType::Num,
}),
),
(
SCROLL_LOCK,
Modifier::Single(ModKind::Lock {
pressed: false,
locked: 0,
mod_type: ModType::Scroll,
}),
),
];
Self { entries }
}
}
impl Modifiers {
pub fn new() -> Self {
Self {
entries: Vec::with_capacity(MAX_MOD_SLOTS),
}
}
#[inline]
pub fn get(&self, evdev_code: u32) -> Option<&Modifier> {
self.entries
.iter()
.find(|(c, _)| *c == evdev_code)
.map(|(_, m)| m)
}
#[inline]
pub fn get_mut(&mut self, evdev_code: u32) -> Option<&mut Modifier> {
self.entries
.iter_mut()
.find(|(c, _)| *c == evdev_code)
.map(|(_, m)| m)
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = (&u32, &Modifier)> {
self.entries.iter().map(|(c, m)| (c, m))
}
pub fn set_modifier(&mut self, evdev_code: u32, modifier: Modifier) {
if let Some((_, existing)) = self.entries.iter_mut().find(|(c, _)| *c == evdev_code) {
*existing = modifier;
} else {
self.entries.push((evdev_code, modifier));
}
}
pub fn active_mod_type(&self, mod_type: ModType) -> bool {
self.entries.iter().any(|(_, modifier)| match modifier {
Modifier::Single(mod_kind) => {
if let Some(mk) = mod_kind.get_modkind_from_modtype(mod_type) {
mk.is_active()
} else {
false
}
}
Modifier::Leveled(map) => map.values().any(|mod_kind| {
if let Some(mk) = mod_kind.get_modkind_from_modtype(mod_type) {
mk.is_active()
} else {
false
}
}),
})
}
#[inline]
pub fn active_none_and_levels(&self) -> (bool, bool, bool, bool) {
let mut none_active = false;
let mut l2 = false;
let mut l3 = false;
let mut l5 = false;
for (_, modifier) in &self.entries {
match modifier {
Modifier::Single(mk) => {
Self::check_mod_kind(mk, &mut none_active, &mut l2, &mut l3, &mut l5);
}
Modifier::Leveled(map) => {
for mk in map.values() {
Self::check_mod_kind(mk, &mut none_active, &mut l2, &mut l3, &mut l5);
}
}
}
}
(none_active, l2, l3, l5)
}
#[inline(always)]
fn check_mod_kind(
mk: &ModKind,
none_active: &mut bool,
l2: &mut bool,
l3: &mut bool,
l5: &mut bool,
) {
match mk {
ModKind::Pressed { pressed, mod_type } if *pressed => match mod_type {
ModType::None => *none_active = true,
ModType::Level2 => *l2 = true,
ModType::Level3 => *l3 = true,
ModType::Level5 => *l5 = true,
_ => {}
},
ModKind::Lock {
locked, mod_type, ..
} if *locked > 0 => match mod_type {
ModType::None => *none_active = true,
ModType::Level2 => *l2 = true,
ModType::Level3 => *l3 = true,
ModType::Level5 => *l5 = true,
_ => {}
},
ModKind::Latch {
latched, mod_type, ..
} if *latched => match mod_type {
ModType::None => *none_active = true,
ModType::Level2 => *l2 = true,
ModType::Level3 => *l3 = true,
ModType::Level5 => *l5 = true,
_ => {}
},
_ => {}
}
}
pub fn unlatch(&mut self) {
self.entries
.iter_mut()
.for_each(|(_, modifier)| match modifier {
Modifier::Single(mod_kind) => {
mod_kind.unlatch();
}
Modifier::Leveled(map) => {
map.values_mut().for_each(|mod_kind| {
mod_kind.unlatch();
});
}
});
}
pub fn locked(&self, evdev_code: u32) -> bool {
self.get(evdev_code).is_some_and(|modifier| match modifier {
Modifier::Single(mod_kind) => mod_kind.locked(),
Modifier::Leveled(map) => map.values().any(|mod_kind| mod_kind.locked()),
})
}
pub fn locked_with_type(&self, evdev_code: u32, mod_type: ModType) -> bool {
self.get(evdev_code).is_some_and(|modifier| match modifier {
Modifier::Single(mod_kind) => {
mod_kind.locked() && mod_kind.get_modkind_from_modtype(mod_type).is_some()
}
Modifier::Leveled(map) => map.values().any(|mod_kind| {
mod_kind.locked() && mod_kind.get_modkind_from_modtype(mod_type).is_some()
}),
})
}
#[inline]
pub fn set_state(&mut self, evdev_code: u32, key_direction: KeyDirection) -> bool {
let pos = match self.entries.iter().position(|(c, _)| *c == evdev_code) {
Some(p) => p,
None => return false,
};
let is_leveled = matches!(&self.entries[pos].1, Modifier::Leveled(_));
if is_leveled {
let (_, l2, l3, l5) = self.active_none_and_levels();
let level = level_index(l5, l3, l2) as u8;
if let Modifier::Leveled(map) = &mut self.entries[pos].1 {
if let Some(mod_kind) = map.get_mut(&level) {
mod_kind.update(key_direction);
} else if let Some(mod_kind) = map.get_mut(&0) {
mod_kind.update(key_direction);
} else {
return false;
}
}
} else if let Modifier::Single(mod_kind) = &mut self.entries[pos].1 {
mod_kind.update(key_direction);
}
true
}
}
#[inline(always)]
pub fn level_index(level5: bool, level3: bool, level2: bool) -> usize {
((level5 as usize) << 2) | ((level3 as usize) << 1) | (level2 as usize)
}