Ths gets rid of Rc<RefCell<>> sharing of state, which can be hard to keep track of. In addition, there's no longer any duplication of button state.
340 lines
8.8 KiB
Rust
340 lines
8.8 KiB
Rust
/*! State of the emulated keyboard and keys.
|
|
* Regards the keyboard as if it was composed of switches. */
|
|
|
|
use crate::action::Action;
|
|
use crate::layout;
|
|
use crate::util;
|
|
use std::collections::HashMap;
|
|
use std::fmt;
|
|
use std::io;
|
|
use std::mem;
|
|
use std::ptr;
|
|
use std::string::FromUtf8Error;
|
|
|
|
// Traits
|
|
use std::io::Write;
|
|
use std::iter::{ FromIterator, IntoIterator };
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
pub enum PressType {
|
|
Released = 0,
|
|
Pressed = 1,
|
|
}
|
|
|
|
/// The extended, unambiguous layout-keycode
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct KeyCode {
|
|
pub code: u32,
|
|
pub keymap_idx: usize,
|
|
}
|
|
|
|
bitflags!{
|
|
/// Map to `virtual_keyboard.modifiers` modifiers values
|
|
/// From https://www.x.org/releases/current/doc/kbproto/xkbproto.html#Keyboard_State
|
|
pub struct Modifiers: u8 {
|
|
const SHIFT = 0x1;
|
|
const LOCK = 0x2;
|
|
const CONTROL = 0x4;
|
|
/// Alt
|
|
const MOD1 = 0x8;
|
|
const MOD2 = 0x10;
|
|
const MOD3 = 0x20;
|
|
/// Meta
|
|
const MOD4 = 0x40;
|
|
/// AltGr
|
|
const MOD5 = 0x80;
|
|
}
|
|
}
|
|
|
|
/// When the submitted actions of keys need to be tracked,
|
|
/// they need a stable, comparable ID.
|
|
/// With layout::ButtonPosition, the IDs are unique within layouts.
|
|
#[derive(Clone, PartialEq)]
|
|
pub struct KeyStateId(layout::ButtonPosition);
|
|
|
|
impl From<&layout::ButtonPosition> for KeyStateId {
|
|
fn from(v: &layout::ButtonPosition) -> Self {
|
|
Self(v.clone())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct Key {
|
|
/// A cache of raw keycodes derived from Action::Submit given a keymap
|
|
pub keycodes: Vec<KeyCode>,
|
|
/// Static description of what the key does when pressed or released
|
|
pub action: Action,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct KeyState {
|
|
pub pressed: PressType,
|
|
}
|
|
|
|
impl KeyState {
|
|
#[must_use]
|
|
pub fn into_released(self) -> KeyState {
|
|
KeyState {
|
|
pressed: PressType::Released,
|
|
..self
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn into_pressed(self) -> KeyState {
|
|
KeyState {
|
|
pressed: PressType::Pressed,
|
|
..self
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Sorts an iterator by converting it to a Vector and back
|
|
fn sorted<'a, I: Iterator<Item=String>>(
|
|
iter: I
|
|
) -> impl Iterator<Item=String> {
|
|
let mut v: Vec<String> = iter.collect();
|
|
v.sort();
|
|
v.into_iter()
|
|
}
|
|
|
|
/// Generates a mapping where each key gets a keycode, starting from ~~8~~
|
|
/// HACK: starting from 9, because 8 results in keycode 0,
|
|
/// which the compositor likes to discard
|
|
pub fn generate_keycodes<'a, C: IntoIterator<Item=String>>(
|
|
key_names: C,
|
|
) -> HashMap<String, KeyCode> {
|
|
HashMap::from_iter(
|
|
// Sort to remove a source of indeterminism in keycode assignment.
|
|
sorted(key_names.into_iter())
|
|
.zip(util::cycle_count(9..255))
|
|
.map(|(name, (code, keymap_idx))| (
|
|
String::from(name),
|
|
KeyCode { code, keymap_idx },
|
|
))
|
|
)
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum FormattingError {
|
|
Utf(FromUtf8Error),
|
|
Format(io::Error),
|
|
}
|
|
|
|
impl fmt::Display for FormattingError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
FormattingError::Utf(e) => write!(f, "UTF: {}", e),
|
|
FormattingError::Format(e) => write!(f, "Format: {}", e),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<io::Error> for FormattingError {
|
|
fn from(e: io::Error) -> Self {
|
|
FormattingError::Format(e)
|
|
}
|
|
}
|
|
|
|
/// Index is the key code, String is the occupant.
|
|
/// Starts all empty.
|
|
/// https://gitlab.freedesktop.org/xorg/xserver/-/issues/260
|
|
type SingleKeyMap = [Option<String>; 256];
|
|
|
|
fn single_key_map_new() -> SingleKeyMap {
|
|
// Why can't we just initialize arrays without tricks -_- ?
|
|
unsafe {
|
|
// Inspired by
|
|
// https://www.reddit.com/r/rust/comments/5n7bh1/how_to_create_an_array_of_a_type_with_clone_but/
|
|
#[cfg(feature = "rustc_less_1_36")]
|
|
let mut array: SingleKeyMap = mem::uninitialized();
|
|
#[cfg(not(feature = "rustc_less_1_36"))]
|
|
let mut array: SingleKeyMap = mem::MaybeUninit::uninit().assume_init();
|
|
|
|
for element in array.iter_mut() {
|
|
ptr::write(element, None);
|
|
}
|
|
array
|
|
}
|
|
}
|
|
|
|
pub fn generate_keymaps(symbolmap: HashMap::<String, KeyCode>)
|
|
-> Result<Vec<String>, FormattingError>
|
|
{
|
|
let mut bins: Vec<SingleKeyMap> = Vec::new();
|
|
|
|
for (name, KeyCode { code, keymap_idx }) in symbolmap.into_iter() {
|
|
if keymap_idx >= bins.len() {
|
|
bins.resize_with(
|
|
keymap_idx + 1,
|
|
|| single_key_map_new(),
|
|
);
|
|
}
|
|
bins[keymap_idx][code as usize] = Some(name);
|
|
}
|
|
|
|
let mut out = Vec::new();
|
|
for bin in bins {
|
|
out.push(generate_keymap(&bin)?);
|
|
}
|
|
Ok(out)
|
|
}
|
|
|
|
/// Generates a de-facto single level keymap.
|
|
/// Key codes must not repeat and must remain between 9 and 255.
|
|
fn generate_keymap(
|
|
symbolmap: &SingleKeyMap,
|
|
) -> Result<String, FormattingError> {
|
|
let mut buf: Vec<u8> = Vec::new();
|
|
writeln!(
|
|
buf,
|
|
"xkb_keymap {{
|
|
|
|
xkb_keycodes \"squeekboard\" {{
|
|
minimum = 8;
|
|
maximum = 255;"
|
|
)?;
|
|
|
|
let pairs: Vec<(&String, usize)> = symbolmap.iter()
|
|
// Attach a key code to each cell.
|
|
.enumerate()
|
|
// Get rid of empty keycodes.
|
|
.filter_map(|(code, name)| name.as_ref().map(|n| (n, code)))
|
|
.collect();
|
|
|
|
// Xorg can only consume up to 255 keys, so this may not work in Xwayland.
|
|
// Two possible solutions:
|
|
// - use levels to cram multiple characters into one key
|
|
// - swap layouts on key presses
|
|
for (_name, keycode) in &pairs {
|
|
write!(
|
|
buf,
|
|
"
|
|
<I{}> = {0};",
|
|
keycode,
|
|
)?;
|
|
}
|
|
|
|
writeln!(
|
|
buf,
|
|
"
|
|
indicator 1 = \"Caps Lock\"; // Xwayland won't accept without it.
|
|
}};
|
|
|
|
xkb_symbols \"squeekboard\" {{
|
|
"
|
|
)?;
|
|
|
|
for (name, keycode) in pairs {
|
|
write!(
|
|
buf,
|
|
"
|
|
key <I{}> {{ [ {} ] }};",
|
|
keycode,
|
|
name,
|
|
)?;
|
|
}
|
|
|
|
writeln!(
|
|
buf,
|
|
"
|
|
}};
|
|
|
|
xkb_types \"squeekboard\" {{
|
|
virtual_modifiers Squeekboard; // No modifiers! Needed for Xorg for some reason.
|
|
|
|
// Those names are needed for Xwayland.
|
|
type \"ONE_LEVEL\" {{
|
|
modifiers= none;
|
|
level_name[Level1]= \"Any\";
|
|
}};
|
|
type \"TWO_LEVEL\" {{
|
|
level_name[Level1]= \"Base\";
|
|
}};
|
|
type \"ALPHABETIC\" {{
|
|
level_name[Level1]= \"Base\";
|
|
}};
|
|
type \"KEYPAD\" {{
|
|
level_name[Level1]= \"Base\";
|
|
}};
|
|
type \"SHIFT+ALT\" {{
|
|
level_name[Level1]= \"Base\";
|
|
}};
|
|
|
|
}};
|
|
|
|
xkb_compatibility \"squeekboard\" {{
|
|
// Needed for Xwayland again.
|
|
interpret Any+AnyOf(all) {{
|
|
action= SetMods(modifiers=modMapMods,clearLocks);
|
|
}};
|
|
}};
|
|
}};"
|
|
)?;
|
|
|
|
//println!("{}", String::from_utf8(buf.clone()).unwrap());
|
|
String::from_utf8(buf).map_err(FormattingError::Utf)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
use xkbcommon::xkb;
|
|
|
|
#[test]
|
|
fn test_keymap_single_resolve() {
|
|
let mut key_map = single_key_map_new();
|
|
key_map[9] = Some("a".into());
|
|
key_map[10] = Some("c".into());
|
|
|
|
let keymap_str = generate_keymap(&key_map).unwrap();
|
|
|
|
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
|
|
|
|
let keymap = xkb::Keymap::new_from_string(
|
|
&context,
|
|
keymap_str.clone(),
|
|
xkb::KEYMAP_FORMAT_TEXT_V1,
|
|
xkb::KEYMAP_COMPILE_NO_FLAGS,
|
|
).expect("Failed to create keymap");
|
|
|
|
let state = xkb::State::new(&keymap);
|
|
|
|
assert_eq!(state.key_get_one_sym(9), xkb::KEY_a);
|
|
assert_eq!(state.key_get_one_sym(10), xkb::KEY_c);
|
|
}
|
|
|
|
#[test]
|
|
fn test_keymap_second_resolve() {
|
|
let keymaps = generate_keymaps(hashmap!(
|
|
"a".into() => KeyCode { keymap_idx: 1, code: 9 },
|
|
)).unwrap();
|
|
|
|
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
|
|
|
|
let keymap = xkb::Keymap::new_from_string(
|
|
&context,
|
|
keymaps[1].clone(), // this index is part of the test
|
|
xkb::KEYMAP_FORMAT_TEXT_V1,
|
|
xkb::KEYMAP_COMPILE_NO_FLAGS,
|
|
).expect("Failed to create keymap");
|
|
|
|
let state = xkb::State::new(&keymap);
|
|
|
|
assert_eq!(state.key_get_one_sym(9), xkb::KEY_a);
|
|
}
|
|
|
|
#[test]
|
|
fn test_symbolmap_overflow() {
|
|
// The 257th key (U1101) is interesting.
|
|
// Use Unicode encoding for being able to use in xkb keymaps.
|
|
let keynames = (0..258).map(|num| format!("U{:04X}", 0x1000 + num));
|
|
let keycodes = generate_keycodes(keynames);
|
|
|
|
// test now
|
|
let code = keycodes.get("U1101").expect("Did not find the tested keysym");
|
|
assert_eq!(code.keymap_idx, 1);
|
|
}
|
|
}
|