unfettered-keyboard/src/core/layout.rs
Richard Acayan d884e71d86 add yaml configuration
Example:

	%YAML 1.2
	---
	longpress_ms: 600
	repeat_ms: 25

	layout: latn_qwerty_us.xml

	wayland:
	  height: 185
2024-08-08 17:51:56 -04:00

782 lines
24 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-License-Identifier: GPL-3.0-only
/*
* Copyright (c) 2024, Richard Acayan. All rights reserved.
*/
use crate::core::Configuration;
use crate::core::Keyboard;
use crate::core::ModState;
use crate::core::expat;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::str::FromStr;
use std::ffi::CStr;
use std::fs::File;
use std::io::Error;
use std::io::ErrorKind;
use std::io::Read;
use std::os::raw::c_char;
use std::os::raw::c_void;
use std::path::Path;
use std::ptr;
use xkeysym::Keysym;
unsafe extern "C" fn start_elem(data: *mut c_void,
c_name: *const expat::XML_Char,
c_attrs: *mut *const expat::XML_Char)
{
let layout = &mut *(data as *mut Layout);
let name = CStr::from_ptr(c_name)
.to_str().expect("layout: XML element name must be UTF-8");
let mut attrs = HashMap::with_capacity(10);
while !ptr::eq(*c_attrs.add(attrs.len() * 2), ptr::null()) {
let k = CStr::from_ptr(*c_attrs.add(attrs.len() * 2))
.to_str().expect("layout: XML attribute name must be UTF-8");
let v = CStr::from_ptr(*c_attrs.add(attrs.len() * 2 + 1))
.to_str().expect("layout: XML attribute value must be UTF-8");
attrs.insert(k, v);
}
layout.start_elem(name, &attrs);
}
unsafe extern "C" fn end_elem(data: *mut c_void, c_name: *const expat::XML_Char)
{
let layout = &mut *(data as *mut Layout);
let name = CStr::from_ptr(c_name)
.to_str().expect("layout: XML element name must be UTF-8");
layout.end_elem(name);
}
pub const MOD_SHIFT: usize = 1;
pub const MOD_CTRL: usize = 2;
pub const MOD_ALT: usize = 3;
pub const MOD_FN: usize = 4;
pub const MODIFIERS_MAX: usize = 4;
#[derive(Clone)]
struct KeyValue(Keysym, String);
impl KeyValue {
fn from(key: Keysym, text: &str) -> KeyValue
{
KeyValue(key, String::from(text))
}
}
pub struct Part {
orig: KeyValue,
val: KeyValue,
key_avail: bool,
text_avail: bool,
presses: u32,
}
impl Part {
pub fn modify_shift(orig_sym: Keysym) -> Keysym
{
match orig_sym {
Keysym::a => Keysym::A,
Keysym::b => Keysym::B,
Keysym::c => Keysym::C,
Keysym::d => Keysym::D,
Keysym::e => Keysym::E,
Keysym::f => Keysym::F,
Keysym::g => Keysym::G,
Keysym::h => Keysym::H,
Keysym::i => Keysym::I,
Keysym::j => Keysym::J,
Keysym::k => Keysym::K,
Keysym::l => Keysym::L,
Keysym::m => Keysym::M,
Keysym::n => Keysym::N,
Keysym::o => Keysym::O,
Keysym::p => Keysym::P,
Keysym::q => Keysym::Q,
Keysym::r => Keysym::R,
Keysym::s => Keysym::S,
Keysym::t => Keysym::T,
Keysym::u => Keysym::U,
Keysym::v => Keysym::V,
Keysym::w => Keysym::W,
Keysym::x => Keysym::X,
Keysym::y => Keysym::Y,
Keysym::z => Keysym::Z,
Keysym::ae => Keysym::AE,
Keysym::oe => Keysym::OE,
Keysym::mu => Keysym::Greek_MU,
_ => orig_sym,
}
}
fn modify_shift_label(orig_sym: &KeyValue) -> KeyValue
{
match orig_sym {
KeyValue(Keysym::a, _)
=> KeyValue::from(Keysym::a, "A"),
KeyValue(Keysym::b, _)
=> KeyValue::from(Keysym::b, "B"),
KeyValue(Keysym::c, _)
=> KeyValue::from(Keysym::c, "C"),
KeyValue(Keysym::d, _)
=> KeyValue::from(Keysym::d, "D"),
KeyValue(Keysym::e, _)
=> KeyValue::from(Keysym::e, "E"),
KeyValue(Keysym::f, _)
=> KeyValue::from(Keysym::f, "F"),
KeyValue(Keysym::g, _)
=> KeyValue::from(Keysym::g, "G"),
KeyValue(Keysym::h, _)
=> KeyValue::from(Keysym::h, "H"),
KeyValue(Keysym::i, _)
=> KeyValue::from(Keysym::i, "I"),
KeyValue(Keysym::j, _)
=> KeyValue::from(Keysym::j, "J"),
KeyValue(Keysym::k, _)
=> KeyValue::from(Keysym::k, "K"),
KeyValue(Keysym::l, _)
=> KeyValue::from(Keysym::l, "L"),
KeyValue(Keysym::m, _)
=> KeyValue::from(Keysym::m, "M"),
KeyValue(Keysym::n, _)
=> KeyValue::from(Keysym::n, "N"),
KeyValue(Keysym::o, _)
=> KeyValue::from(Keysym::o, "O"),
KeyValue(Keysym::p, _)
=> KeyValue::from(Keysym::p, "P"),
KeyValue(Keysym::q, _)
=> KeyValue::from(Keysym::q, "Q"),
KeyValue(Keysym::r, _)
=> KeyValue::from(Keysym::r, "R"),
KeyValue(Keysym::s, _)
=> KeyValue::from(Keysym::s, "S"),
KeyValue(Keysym::t, _)
=> KeyValue::from(Keysym::t, "T"),
KeyValue(Keysym::u, _)
=> KeyValue::from(Keysym::u, "U"),
KeyValue(Keysym::v, _)
=> KeyValue::from(Keysym::v, "V"),
KeyValue(Keysym::w, _)
=> KeyValue::from(Keysym::w, "W"),
KeyValue(Keysym::x, _)
=> KeyValue::from(Keysym::x, "X"),
KeyValue(Keysym::y, _)
=> KeyValue::from(Keysym::y, "Y"),
KeyValue(Keysym::z, _)
=> KeyValue::from(Keysym::z, "Z"),
KeyValue(Keysym::ae, _)
=> KeyValue::from(Keysym::ae, "Æ"),
KeyValue(Keysym::oe, _)
=> KeyValue::from(Keysym::oe, "Œ"),
KeyValue(Keysym::mu, _)
=> KeyValue::from(Keysym::mu, "Μ"),
_ => orig_sym.clone(),
}
}
fn modify_fn(orig_sym: &KeyValue) -> KeyValue
{
match orig_sym {
KeyValue(Keysym::Tab, _)
=> KeyValue::from(Keysym::NoSymbol, "\t"),
KeyValue(Keysym::Up, _)
=> KeyValue::from(Keysym::Prior, ""), // Page Up
KeyValue(Keysym::Down, _)
=> KeyValue::from(Keysym::Next, ""), // Page Down
KeyValue(Keysym::Left, _)
=> KeyValue::from(Keysym::Home, ""),
KeyValue(Keysym::Right, _)
=> KeyValue::from(Keysym::End, ""),
KeyValue(Keysym::Escape, _)
=> KeyValue::from(Keysym::Insert, "Ins"),
KeyValue(Keysym::_1, _)
=> KeyValue::from(Keysym::F1, "F1"),
KeyValue(Keysym::_2, _)
=> KeyValue::from(Keysym::F2, "F2"),
KeyValue(Keysym::_3, _)
=> KeyValue::from(Keysym::F3, "F3"),
KeyValue(Keysym::_4, _)
=> KeyValue::from(Keysym::F4, "F4"),
KeyValue(Keysym::_5, _)
=> KeyValue::from(Keysym::F5, "F5"),
KeyValue(Keysym::_6, _)
=> KeyValue::from(Keysym::F6, "F6"),
KeyValue(Keysym::_7, _)
=> KeyValue::from(Keysym::F7, "F7"),
KeyValue(Keysym::_8, _)
=> KeyValue::from(Keysym::F8, "F8"),
KeyValue(Keysym::_9, _)
=> KeyValue::from(Keysym::F9, "F9"),
KeyValue(Keysym::_0, _)
=> KeyValue::from(Keysym::F10, "F10"),
KeyValue(Keysym::less, _)
=> KeyValue::from(Keysym::guillemetleft, "«"),
KeyValue(Keysym::greater, _)
=> KeyValue::from(Keysym::guillemetright, "»"),
KeyValue(Keysym::braceleft, _)
=> KeyValue::from(Keysym::NoSymbol, ""),
KeyValue(Keysym::braceright, _)
=> KeyValue::from(Keysym::NoSymbol, ""),
KeyValue(Keysym::bracketleft, _)
=> KeyValue::from(Keysym::leftsinglequotemark, ""),
KeyValue(Keysym::bracketright, _)
=> KeyValue::from(Keysym::rightsinglequotemark, ""),
KeyValue(Keysym::parenleft, _)
=> KeyValue::from(Keysym::leftdoublequotemark, ""),
KeyValue(Keysym::parenright, _)
=> KeyValue::from(Keysym::rightdoublequotemark, ""),
KeyValue(Keysym::apostrophe, _)
=> KeyValue::from(Keysym::singlelowquotemark, ""),
KeyValue(Keysym::quotedbl, _)
=> KeyValue::from(Keysym::doublelowquotemark, ""),
KeyValue(Keysym::minus, _)
=> KeyValue::from(Keysym::endash, ""),
KeyValue(Keysym::underscore, _)
=> KeyValue::from(Keysym::emdash, ""),
KeyValue(Keysym::asciicircum, _)
=> KeyValue::from(Keysym::notsign, "¬"),
KeyValue(Keysym::percent, _)
=> KeyValue::from(Keysym::permille, ""),
KeyValue(Keysym::equal, _)
=> KeyValue::from(Keysym::approxeq, ""),
KeyValue(Keysym::u, _)
=> KeyValue::from(Keysym::mu, "µ"),
KeyValue(Keysym::a, _)
=> KeyValue::from(Keysym::ae, "æ"),
KeyValue(Keysym::o, _)
=> KeyValue::from(Keysym::oe, "œ"),
KeyValue(Keysym::asterisk, _)
=> KeyValue::from(Keysym::degree, "°"),
KeyValue(Keysym::period, _)
=> KeyValue::from(Keysym::ellipsis, ""),
KeyValue(Keysym::comma, _)
=> KeyValue::from(Keysym::periodcentered, "·"),
KeyValue(Keysym::exclam, _)
=> KeyValue::from(Keysym::exclamdown, "¡"),
KeyValue(Keysym::question, _)
=> KeyValue::from(Keysym::questiondown, "¿"),
KeyValue(Keysym::bar, _)
=> KeyValue::from(Keysym::brokenbar, "¦"),
KeyValue(Keysym::e, _)
=> KeyValue::from(Keysym::EuroSign, ""),
KeyValue(Keysym::l, _)
=> KeyValue::from(Keysym::sterling, "£"),
KeyValue(Keysym::r, _)
=> KeyValue::from(Keysym::NoSymbol, ""),
KeyValue(Keysym::y, _)
=> KeyValue::from(Keysym::yen, "¥"),
KeyValue(Keysym::c, _)
=> KeyValue::from(Keysym::cent, "¢"),
KeyValue(Keysym::p, _)
=> KeyValue::from(Keysym::NoSymbol, ""),
KeyValue(Keysym::b, _)
=> KeyValue::from(Keysym::NoSymbol, ""),
KeyValue(Keysym::h, _)
=> KeyValue::from(Keysym::NoSymbol, ""),
KeyValue(Keysym::z, _)
=> KeyValue::from(Keysym::NoSymbol, ""),
_ => orig_sym.clone(),
}
}
fn modify_no_fn(orig: &KeyValue) -> KeyValue
{
match orig {
KeyValue(Keysym::F11, _) => KeyValue::from(Keysym::NoSymbol, ""),
KeyValue(Keysym::F12, _) => KeyValue::from(Keysym::NoSymbol, ""),
_ => orig.clone(),
}
}
fn keyvalue_has_text(val: &KeyValue) -> bool
{
match val {
KeyValue(Keysym::BackSpace, _) => false,
KeyValue(Keysym::Tab, _) => false,
KeyValue(Keysym::Return, _) => false,
KeyValue(Keysym::Escape, _) => false,
KeyValue(Keysym::Home, _) => false,
KeyValue(Keysym::Left, _) => false,
KeyValue(Keysym::Up, _) => false,
KeyValue(Keysym::Right, _) => false,
KeyValue(Keysym::Down, _) => false,
KeyValue(Keysym::Prior, _) => false,
KeyValue(Keysym::Next, _) => false,
KeyValue(Keysym::End, _) => false,
KeyValue(Keysym::Insert, _) => false,
KeyValue(Keysym::F1, _) => false,
KeyValue(Keysym::F2, _) => false,
KeyValue(Keysym::F3, _) => false,
KeyValue(Keysym::F4, _) => false,
KeyValue(Keysym::F5, _) => false,
KeyValue(Keysym::F6, _) => false,
KeyValue(Keysym::F7, _) => false,
KeyValue(Keysym::F8, _) => false,
KeyValue(Keysym::F9, _) => false,
KeyValue(Keysym::F10, _) => false,
KeyValue(Keysym::F11, _) => false,
KeyValue(Keysym::F12, _) => false,
KeyValue(Keysym::Delete, _) => false,
KeyValue(_, l) => l != "",
_ => true,
}
}
fn new(orig: KeyValue) -> Part
{
let val = Self::modify_no_fn(&orig);
Part {
orig,
val,
key_avail: false,
text_avail: false,
presses: 0,
}
}
#[inline(always)]
pub fn sym(&self) -> Keysym
{
self.val.0
}
#[inline(always)]
pub fn text(&self) -> &str
{
&self.val.1
}
#[inline(always)]
pub fn press(&mut self)
{
self.presses += 1;
}
#[inline(always)]
pub fn release(&mut self)
{
self.presses -= 1;
}
#[inline(always)]
pub fn presses(&self) -> u32
{
self.presses
}
#[inline(always)]
pub fn text_available(&self) -> bool
{
self.text_avail
}
#[inline(always)]
pub fn key_available(&self) -> bool
{
self.key_avail
}
fn set_text_supported(&mut self, text_supp: bool)
{
self.text_avail = text_supp && Self::keyvalue_has_text(&self.val);
}
fn update_keys_supported<K: Keyboard>(&mut self, kbd: &K)
{
let key_supp = kbd.key_supported(self.val.0);
self.key_avail = self.val.0 != Keysym::NoSymbol && key_supp;
}
pub fn pressable(&self) -> bool
{
self.text_avail || self.key_avail
}
pub fn modifier_id(&self) -> usize
{
match self.val.0 {
Keysym::Shift_L => MOD_SHIFT,
Keysym::Control_L => MOD_CTRL,
Keysym::Alt_L => MOD_ALT,
Keysym::XF86_Fn => MOD_FN,
_ => 0,
}
}
pub fn update_modifiers<K: Keyboard>(&mut self,
kbd: &K,
mod_state: &[ModState],
text_avail: bool)
{
let mut val = self.orig.clone();
if mod_state[MOD_FN - 1] != ModState::Released {
val = Self::modify_fn(&val);
} else {
val = Self::modify_no_fn(&val);
}
if mod_state[MOD_SHIFT - 1] != ModState::Released {
val = Self::modify_shift_label(&val);
}
let key_supp = kbd.key_supported(self.val.0);
self.key_avail = val.0 != Keysym::NoSymbol && key_supp;
self.text_avail = text_avail && Self::keyvalue_has_text(&val);
self.val = val;
}
pub fn display_label(&self) -> &str
{
match self.val.1.as_ref() {
" " => "",
"\t" => "\\t",
l => l,
}
}
}
pub struct Key {
pub parts: [Part; 9],
pub x1: f64,
pub y1: f64,
pub x2: f64,
pub y2: f64,
}
impl Key {
fn new(x1: f64, y1: f64, x2: f64, y2: f64,
center: KeyValue,
top_left: KeyValue,
top_right: KeyValue,
bottom_left: KeyValue,
bottom_right: KeyValue,
left: KeyValue,
right: KeyValue,
top: KeyValue,
bottom: KeyValue) -> Key
{
Key {
x1, y1, x2, y2,
parts: [
Part::new(center),
Part::new(top_left),
Part::new(top_right),
Part::new(bottom_left),
Part::new(bottom_right),
Part::new(left),
Part::new(right),
Part::new(top),
Part::new(bottom),
],
}
}
}
pub struct Layout {
rows: Vec<Vec<Key>>,
width: f64,
height: f64,
row_width: f64,
in_row: u32,
text_supp: bool,
}
const KEYSYMS: [(&str, Keysym, &str); 20] = [
("\\#", Keysym::numbersign, "#"),
("\\?", Keysym::question, "?"),
("\\@", Keysym::at, "@"),
("\\\\", Keysym::backslash, "\\"),
("backspace", Keysym::BackSpace, ""),
("ctrl", Keysym::Control_L, "Ctrl"),
("delete", Keysym::Delete, ""),
("down", Keysym::Down, ""),
("enter", Keysym::Return, ""),
("esc", Keysym::Escape, "Esc"),
("f11_placeholder", Keysym::F11, "F11"),
("f12_placeholder", Keysym::F12, "F12"),
("fn", Keysym::XF86_Fn, "Fn"),
("left", Keysym::Left, ""),
("loc alt", Keysym::Alt_L, "Alt"),
("right", Keysym::Right, ""),
("shift", Keysym::Shift_L, ""),
("space", Keysym::space, " "),
("tab", Keysym::Tab, ""),
("up", Keysym::Up, ""),
];
impl Layout {
pub fn is_keysym_modifier(modifier: usize) -> bool
{
match modifier {
MOD_FN => true,
_ => false,
}
}
pub fn is_label_modifier(modifier: usize) -> bool
{
// Shift does not change the keysym used for input, but changes the
// rendered keysym.
Self::is_keysym_modifier(modifier) || modifier == MOD_SHIFT
}
pub fn modifier_keysym(modifier: usize) -> Keysym
{
match modifier {
MOD_SHIFT => Keysym::Shift_L,
MOD_CTRL => Keysym::Control_L,
MOD_ALT => Keysym::Alt_L,
MOD_FN => Keysym::XF86_Fn,
_ => Keysym::NoSymbol,
}
}
fn get_keysym_by_name(name: &str) -> KeyValue
{
if let Ok(idx) = KEYSYMS.binary_search_by_key(&name, |&(k, _, _)| k) {
return KeyValue::from(KEYSYMS[idx].1, KEYSYMS[idx].2);
}
let mut chars = name.chars();
let c1 = chars.next();
let c2 = chars.next();
match (c1, c2) {
(Some(c), None) => KeyValue::from(Keysym::new(c as u32), name),
_ => KeyValue::from(Keysym::NoSymbol, ""),
}
}
fn start_key(&mut self, attrs: &HashMap<&str, &str>)
{
if self.in_row != 1 {
println!("layout: key element not in row element ignored");
return;
}
let shift = f64::from_str(attrs.get("shift").unwrap_or(&"")).unwrap_or(0.0);
let x1 = self.row_width + shift;
let width = f64::from_str(attrs.get("width").unwrap_or(&"")).unwrap_or(1.0);
let x2 = x1 + width;
self.row_width = x2;
let key = Key::new(
x1, self.height, x2, self.height + 1.0,
Self::get_keysym_by_name(attrs.get("key0").unwrap_or(&"")),
Self::get_keysym_by_name(attrs.get("key1").unwrap_or(&"")),
Self::get_keysym_by_name(attrs.get("key2").unwrap_or(&"")),
Self::get_keysym_by_name(attrs.get("key3").unwrap_or(&"")),
Self::get_keysym_by_name(attrs.get("key4").unwrap_or(&"")),
Self::get_keysym_by_name(attrs.get("key5").unwrap_or(&"")),
Self::get_keysym_by_name(attrs.get("key6").unwrap_or(&"")),
Self::get_keysym_by_name(attrs.get("key7").unwrap_or(&"")),
Self::get_keysym_by_name(attrs.get("key8").unwrap_or(&""))
);
let idx = self.rows.len() - 1;
self.rows[idx].push(key);
}
fn start_row(&mut self)
{
self.rows.push(Vec::new());
self.in_row += 1;
self.row_width = 0.0;
}
fn start_elem(&mut self, name: &str, attrs: &HashMap<&str, &str>)
{
if name == "key" {
self.start_key(attrs);
} else if name == "row" {
self.start_row();
}
}
fn end_row(&mut self)
{
self.in_row -= 1;
if self.row_width > self.width {
self.width = self.row_width;
}
self.height += 1.0;
}
fn end_elem(&mut self, name: &str)
{
if name == "row" {
self.end_row();
}
}
pub fn load(cfg: &Configuration) -> Result<Self, Error>
{
let mut layout = Layout {
rows: Vec::new(),
width: 0.0,
height: 0.0,
row_width: 0.0,
in_row: 0,
text_supp: false,
};
let dir = Path::new("/usr/share/unfettered-keyboard/layouts");
let mut vec = Vec::new();
let path = dir.join(cfg.layout());
let mut file = File::open(path)?;
file.read_to_end(&mut vec)?;
let mut vec2 = Vec::new();
let path = dir.join("bottom_row.xml");
let mut file = File::open(path)?;
file.read_to_end(&mut vec2)?;
unsafe {
let parser = expat::XML_ParserCreate(ptr::null());
if ptr::eq(parser, ptr::null_mut()) {
return Err(Error::new(ErrorKind::OutOfMemory, "Could not parse XML"));
}
expat::XML_SetElementHandler(parser, Some(start_elem), Some(end_elem));
expat::XML_SetUserData(parser, &mut layout as *mut Layout as *mut c_void);
let status = expat::XML_Parse(parser, vec.as_ptr() as *const c_char,
vec.len() as i32, 1);
if status != expat::XML_Status_XML_STATUS_OK {
return Err(Error::new(ErrorKind::InvalidData, "Could not parse XML"));
}
expat::XML_ParserReset(parser, ptr::null());
expat::XML_SetElementHandler(parser, Some(start_elem), Some(end_elem));
expat::XML_SetUserData(parser, &mut layout as *mut Layout as *mut c_void);
let status = expat::XML_Parse(parser, vec2.as_ptr() as *const c_char,
vec2.len() as i32, 1);
if status != expat::XML_Status_XML_STATUS_OK {
return Err(Error::new(ErrorKind::InvalidData, "Could not parse XML"));
}
expat::XML_ParserFree(parser);
};
Ok(layout)
}
#[inline(always)]
pub fn width(&self) -> f64
{
self.width
}
#[inline(always)]
pub fn height(&self) -> f64
{
self.height
}
#[inline(always)]
pub fn rows(&self) -> &Vec<Vec<Key>>
{
&self.rows
}
pub fn locate_key(&self, x: f64, y: f64) -> Option<&Key>
{
let row = &self.rows[y as usize];
let res = row.binary_search_by(|k| {
if k.x2 < x {
Ordering::Less
} else if k.x1 > x {
Ordering::Greater
} else {
Ordering::Equal
}
});
match res {
Ok(i) => Some(&row[i]),
Err(_) => None,
}
}
pub fn locate_key_mut(&mut self, x: f64, y: f64) -> Option<&mut Key>
{
let row = &mut self.rows[y as usize];
let res = row.binary_search_by(|k| {
if k.x2 < x {
Ordering::Less
} else if k.x1 > x {
Ordering::Greater
} else {
Ordering::Equal
}
});
match res {
Ok(i) => Some(&mut row[i]),
Err(_) => None,
}
}
pub fn update_modifiers<K: Keyboard>(&mut self, kbd: &K, mod_state: &[ModState])
{
for row in self.rows.iter_mut() {
for key in row.iter_mut() {
for part in key.parts.iter_mut() {
part.update_modifiers(kbd, mod_state, self.text_supp);
}
}
}
}
pub fn set_text_supported(&mut self, text_supp: bool)
{
self.text_supp = text_supp;
for row in self.rows.iter_mut() {
for key in row.iter_mut() {
for part in key.parts.iter_mut() {
part.set_text_supported(text_supp);
}
}
}
}
pub fn update_keys_supported<K: Keyboard>(&mut self, kbd: &K)
{
for row in self.rows.iter_mut() {
for key in row.iter_mut() {
for part in key.parts.iter_mut() {
part.update_keys_supported(kbd);
}
}
}
}
}