rewrite in rust
This commit is contained in:
parent
d43f61ec76
commit
4a6b261be0
51 changed files with 3104 additions and 9319 deletions
528
src/core/button.rs
Normal file
528
src/core/button.rs
Normal file
|
|
@ -0,0 +1,528 @@
|
|||
// 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 -> Held (or Locked -> HeldLocked) 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,
|
||||
Latched,
|
||||
Held,
|
||||
Locked,
|
||||
HeldLocked,
|
||||
}
|
||||
|
||||
#[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::<Vec<_>>().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<D: Display, K: Keyboard> {
|
||||
layout: Layout,
|
||||
kbd: K,
|
||||
gfx: Arc<Mutex<Graphics<D>>>,
|
||||
|
||||
presses: [Option<Press>; PRESSES_MAX],
|
||||
timers: VecDeque<usize>,
|
||||
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<Press> = None;
|
||||
|
||||
impl<D: Display, K: Keyboard> Button<D, K> {
|
||||
pub fn new(layout: Layout, mut kbd: K,
|
||||
gfx: Arc<Mutex<Graphics<D>>>) -> Button<D, K>
|
||||
{
|
||||
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<Instant>
|
||||
{
|
||||
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 || new == ModState::Released {
|
||||
self.layout.update_modifiers(&self.modifiers);
|
||||
if Layout::is_keysym_modifier(modifier) {
|
||||
self.kbd.change_layout(&self.layout);
|
||||
}
|
||||
}
|
||||
|
||||
if old == ModState::Released {
|
||||
self.kbd.press(Layout::modifier_keysym(modifier));
|
||||
}
|
||||
|
||||
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::Held);
|
||||
self.update_modifier(modifier,
|
||||
ModState::Locked,
|
||||
ModState::HeldLocked);
|
||||
}
|
||||
}
|
||||
|
||||
draw
|
||||
}
|
||||
|
||||
fn draw(op: DrawOperation,
|
||||
gfx: &Arc<Mutex<Graphics<D>>>,
|
||||
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() {
|
||||
eprintln!("warn: pressed same ID twice before releasing");
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue