// SPDX-License-Identifier: GPL-3.0-only /* * Copyright (c) 2024, Richard Acayan. All rights reserved. */ use core::Display; use core::Graphics; use core::Layout; use core::layout::Key; use core::layout::MODIFIERS_MAX; use core::xkeysym::Keysym; use std::collections::VecDeque; use std::convert::TryInto; use std::iter; use std::ops::Add; use std::sync::Arc; use std::sync::Mutex; use std::time::Duration; use std::time::Instant; /* * The available states for the modifier keys. * * The state goes Released -> Pressed when the modifier key is pressed. * * When pressed, the modifier can be released, going Pressed -> Latched so that * the modifier will apply to the next key press. * It could be held down for long enough that it goes Pressed -> Locked, and the * modifier would apply to any other key press until it is released, pressed and * released again. * Finally, it could be held down while another key is being pressed, going * Pressed -> HeldPressed (or Locked -> HeldLockedPressed) to apply until it is * released. * * When latched, it could go Latched -> Released when any key * (including itself) is released. * * When held, it goes Held -> Released (or HeldLocked -> Released) when the * modifier key is released. * * When locked, the modifier key goes Locked -> HeldLocked when it is pressed * again. */ #[derive(Clone, Copy, Debug, PartialEq)] pub enum ModState { Released, Pressed, HeldPressed, Latched, Held, Locked, HeldLocked, HeldLockedPressed, } #[derive(PartialEq)] enum DrawOperation { Key, Modifiers([bool; MODIFIERS_MAX]), Labels, } impl DrawOperation { fn from_modifier(modifier: usize) -> DrawOperation { let mut modifiers = [false; MODIFIERS_MAX]; modifiers[modifier - 1] = true; DrawOperation::Modifiers(modifiers) } fn from_modifier_edge(modifier: usize) -> DrawOperation { if Layout::is_label_modifier(modifier) { DrawOperation::Labels } else { DrawOperation::from_modifier(modifier) } } } impl Add for DrawOperation { type Output = Self; fn add(self, other: Self) -> Self { match (&self, &other) { (&DrawOperation::Labels, _) => self, (_, &DrawOperation::Labels) => other, (&DrawOperation::Key, &DrawOperation::Modifiers(_)) => other, (&DrawOperation::Modifiers(_), &DrawOperation::Key) => self, (&DrawOperation::Modifiers(a), &DrawOperation::Modifiers(b)) => { let mods = iter::zip(&a, &b).map(|(a, b)| *a || *b); DrawOperation::Modifiers(mods.collect::>().try_into().unwrap()) }, (&DrawOperation::Key, &DrawOperation::Key) => self, } } } struct Press { x1: f64, y1: f64, part: usize, timer: Instant, } const PRESSES_MAX: usize = 64; pub trait Keyboard { fn press(&mut self, sym: Keysym); fn release(&mut self, sym: Keysym); fn change_layout(&mut self, layout: &Layout); } pub struct Button { layout: Layout, kbd: K, gfx: Arc>>, presses: [Option; PRESSES_MAX], timers: VecDeque, modifiers: [ModState; MODIFIERS_MAX], longpress: Duration, repeat: Duration, r_square: f64, } const PART_TL: usize = 1; const PART_TR: usize = 2; const PART_BL: usize = 3; const PART_BR: usize = 4; const PART_LEFT: usize = 5; const PART_RIGHT: usize = 6; const PART_TOP: usize = 7; const PART_BOTTOM: usize = 8; const ANGLE_PARTS: [[usize; 4]; 16] = [ [PART_LEFT, PART_TL, PART_BL, PART_TOP], [PART_TL, PART_LEFT, PART_TOP, PART_BL], [PART_TL, PART_TOP, PART_LEFT, PART_TR], [PART_TOP, PART_TL, PART_TR, PART_LEFT], [PART_TOP, PART_TR, PART_TL, PART_RIGHT], [PART_TR, PART_TOP, PART_RIGHT, PART_TL], [PART_TR, PART_RIGHT, PART_TOP, PART_BR], [PART_RIGHT, PART_TR, PART_BR, PART_TOP], [PART_RIGHT, PART_BR, PART_TR, PART_BOTTOM], [PART_BR, PART_RIGHT, PART_BOTTOM, PART_TR], [PART_BR, PART_BOTTOM, PART_RIGHT, PART_BL], [PART_BOTTOM, PART_BR, PART_BL, PART_RIGHT], [PART_BOTTOM, PART_BL, PART_BR, PART_LEFT], [PART_BL, PART_BOTTOM, PART_LEFT, PART_BR], [PART_BL, PART_LEFT, PART_BOTTOM, PART_TL], [PART_LEFT, PART_BL, PART_TL, PART_BOTTOM], ]; // The None option cannot be copied, but it works if it is a constant. Rust is insane. const NO_PRESS: Option = None; impl Button { pub fn new(layout: Layout, mut kbd: K, gfx: Arc>>) -> Button { kbd.change_layout(&layout); Button { layout, kbd, gfx, presses: [NO_PRESS; PRESSES_MAX], modifiers: [ModState::Released; MODIFIERS_MAX], timers: VecDeque::with_capacity(PRESSES_MAX), longpress: Duration::from_millis(600), repeat: Duration::from_millis(25), r_square: 0.25, } } #[inline(always)] pub fn layout(&self) -> &Layout { &self.layout } pub fn mod_state(&self) -> &[ModState; MODIFIERS_MAX] { &self.modifiers } pub fn next_time(&self) -> Option { let id = *self.timers.front()?; if id == PRESSES_MAX { return None; } // The next timer must always be held by an active key press. Some(self.presses[id].as_ref().unwrap().timer) } fn update_modifier(&mut self, modifier: usize, old: ModState, new: ModState) -> bool { if self.modifiers[modifier - 1] == old { self.modifiers[modifier - 1] = new; if new == ModState::Released { self.kbd.release(Layout::modifier_keysym(modifier)); } if old == ModState::Released { self.kbd.press(Layout::modifier_keysym(modifier)); } if old == ModState::Released || new == ModState::Released { self.layout.update_modifiers(&self.modifiers); if Layout::is_keysym_modifier(modifier) { self.kbd.change_layout(&self.layout); } } true } else { false } } fn try_press_mod(&mut self, modifier: usize) -> DrawOperation { let mut draw = DrawOperation::Key; if modifier != 0 { let changed = self.update_modifier(modifier, ModState::Released, ModState::Pressed); if changed { draw = draw + DrawOperation::from_modifier_edge(modifier); } self.update_modifier(modifier, ModState::Latched, ModState::Held); self.update_modifier(modifier, ModState::Locked, ModState::HeldLocked); } else { for modifier in 1..=MODIFIERS_MAX { self.update_modifier(modifier, ModState::Pressed, ModState::HeldPressed); self.update_modifier(modifier, ModState::Locked, ModState::HeldLockedPressed); } } draw } fn draw(op: DrawOperation, gfx: &Arc>>, layout: &Layout, key: Option<&Key>, mod_state: &[ModState]) { let mut gfx = gfx.lock().unwrap(); match op { DrawOperation::Key => { if let Some(key) = key { gfx.draw_single(key, mod_state); } }, DrawOperation::Modifiers(m) => { gfx.draw_modifiers(layout, mod_state, &m); if let Some(key) = key { gfx.draw_single(key, mod_state); } }, DrawOperation::Labels => { gfx.change_layout(layout, mod_state); } } } pub fn press(&mut self, id: usize, x: f64, y: f64) { if self.presses[id].is_some() { return; } let key = match self.layout.locate_key_mut(x, y) { Some(k) => k, // Ignore the press if it is not for a key. None => return, }; key.parts[0].press(); let modifier = key.parts[0].modifier_id(); let draw = self.try_press_mod(modifier); let timer = Instant::now() + self.longpress; self.presses[id] = Some(Press { x1: x, y1: y, part: 0, timer, }); self.timers.push_back(id); let key = self.layout.locate_key(x, y).unwrap(); Self::draw(draw, &self.gfx, &self.layout, Some(key), &self.modifiers); } fn update_pressed_part(press: &mut Press, key: &Key, angle: usize) { if key.parts[ANGLE_PARTS[angle][0]].sym() != Keysym::NoSymbol { press.part = ANGLE_PARTS[angle][0]; } else if key.parts[ANGLE_PARTS[angle][1]].sym() != Keysym::NoSymbol { press.part = ANGLE_PARTS[angle][1]; } else if key.parts[ANGLE_PARTS[angle][2]].sym() != Keysym::NoSymbol { press.part = ANGLE_PARTS[angle][2]; } else if key.parts[ANGLE_PARTS[angle][3]].sym() != Keysym::NoSymbol { press.part = ANGLE_PARTS[angle][3]; } } fn remove_timer(&mut self, id: usize) { for evt in self.timers.iter_mut() { if *evt == id { *evt = PRESSES_MAX; break; } } while self.timers.front() == Some(&PRESSES_MAX) { self.timers.pop_front(); } } fn try_cancel_mod(&mut self, modifier: usize) -> DrawOperation { let mut draw = DrawOperation::Key; if modifier != 0 { let changed = self.update_modifier(modifier, ModState::Pressed, ModState::Released); if changed && Layout::is_label_modifier(modifier) { draw = draw + DrawOperation::Labels; } let changed = self.update_modifier(modifier, ModState::Locked, ModState::Released); if changed && Layout::is_label_modifier(modifier) { draw = draw + DrawOperation::Labels; } let changed = self.update_modifier(modifier, ModState::HeldPressed, ModState::Released); if changed && Layout::is_label_modifier(modifier) { draw = draw + DrawOperation::Labels; } let changed = self.update_modifier(modifier, ModState::HeldLockedPressed, ModState::Released); if changed && Layout::is_label_modifier(modifier) { draw = draw + DrawOperation::Labels; } self.update_modifier(modifier, ModState::Held, ModState::Latched); self.update_modifier(modifier, ModState::HeldLocked, ModState::Locked); } draw } pub fn pos(&mut self, id: usize, x: f64, y: f64) { let press = match &mut self.presses[id] { Some(p) => p, None => return, }; let (dx, dy) = (x - press.x1, y - press.y1); if dx * dx + dy * dy < self.r_square { // We only need to make changes when the key is being dragged. return; } let angle = dy.atan2(dx) * 8.0 / std::f64::consts::PI + 8.0; let angle = angle.clamp(0.0, 15.0) as usize; let key = self.layout.locate_key_mut(press.x1, press.y1).unwrap(); let old_part = press.part; Self::update_pressed_part(press, key, angle); let new_part = press.part; if new_part == old_part { return; } key.parts[old_part].release(); key.parts[new_part].press(); let modifier1 = key.parts[old_part].modifier_id(); let modifier2 = key.parts[new_part].modifier_id(); press.timer = Instant::now() + self.longpress; self.remove_timer(id); self.timers.push_back(id); let mut draw = self.try_cancel_mod(modifier1); draw = draw + self.try_press_mod(modifier2); let press = self.presses[id].as_ref().unwrap(); let key = self.layout.locate_key(press.x1, press.y1).unwrap(); Self::draw(draw, &self.gfx, &self.layout, Some(key), &self.modifiers); } /* * Release the given modifier key. If the key being pressed is not a * modifier key (i.e. modifier = 0), release all latched modifier keys. */ fn release_mod(&mut self, modifier: usize) -> DrawOperation { let mut draw = DrawOperation::Key; if modifier != 0 { self.update_modifier(modifier, ModState::Pressed, ModState::Latched); let changed = self.update_modifier(modifier, ModState::Held, ModState::Released); if changed && Layout::is_label_modifier(modifier) { draw = draw + DrawOperation::Labels; } let changed = self.update_modifier(modifier, ModState::HeldLocked, ModState::Released); if changed && Layout::is_label_modifier(modifier) { draw = draw + DrawOperation::Labels; } let changed = self.update_modifier(modifier, ModState::HeldPressed, ModState::Released); if changed && Layout::is_label_modifier(modifier) { draw = draw + DrawOperation::Labels; } let changed = self.update_modifier(modifier, ModState::HeldLockedPressed, ModState::Released); if changed && Layout::is_label_modifier(modifier) { draw = draw + DrawOperation::Labels; } } else { for modifier in 1..=MODIFIERS_MAX { let changed = self.update_modifier(modifier, ModState::Latched, ModState::Released); if changed { draw = draw + DrawOperation::from_modifier_edge(modifier); } } } draw } pub fn release(&mut self, id: usize) { let press = match &mut self.presses[id] { Some(p) => p, None => return, }; let key = self.layout.locate_key_mut(press.x1, press.y1).unwrap(); key.parts[press.part].release(); let modifier = key.parts[press.part].modifier_id(); if modifier == 0 { self.kbd.press(key.parts[press.part].sym()); self.kbd.release(key.parts[press.part].sym()); } let draw = self.release_mod(modifier); let press = self.presses[id].as_ref().unwrap(); let key = self.layout.locate_key(press.x1, press.y1).unwrap(); Self::draw(draw, &self.gfx, &self.layout, Some(key), &self.modifiers); self.remove_timer(id); self.presses[id] = None; } fn lock_mod(&mut self, modifier: usize) -> DrawOperation { let changed = self.update_modifier(modifier, ModState::Pressed, ModState::Locked); if changed { DrawOperation::from_modifier(modifier) } else { DrawOperation::Key } } pub fn dispatch_timers(&mut self) { let mut draw = DrawOperation::Key; while !self.timers.is_empty() { let id = *self.timers.front().unwrap(); if id == PRESSES_MAX { self.timers.pop_front(); continue; } let press = self.presses[id].as_mut().unwrap(); if Instant::now() < press.timer { break; } self.timers.pop_front(); let key = self.layout.locate_key(press.x1, press.y1).unwrap(); let modifier = key.parts[press.part].modifier_id(); if modifier != 0 { draw = draw + self.lock_mod(modifier); } else { self.kbd.press(key.parts[press.part].sym()); self.kbd.release(key.parts[press.part].sym()); press.timer += self.repeat; self.timers.push_back(id); } } Self::draw(draw, &self.gfx, &self.layout, None, &self.modifiers); } }