rewrite in rust

This commit is contained in:
Richard Acayan 2024-07-18 20:32:55 -04:00
parent d43f61ec76
commit 4a6b261be0
51 changed files with 3104 additions and 9319 deletions

540
src/core/layout.rs Normal file
View file

@ -0,0 +1,540 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Copyright (c) 2024, Richard Acayan. All rights reserved.
*/
use core::button::ModState;
use core::expat;
use core::xkeysym::Keysym;
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;
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;
pub struct Part {
orig_sym: Keysym,
sym: Keysym,
// TODO: use accessors here
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_fn(orig_sym: Keysym) -> Keysym
{
match orig_sym {
Keysym::Up => Keysym::Prior, // Page Up
Keysym::Down => Keysym::Next, // Page Down
Keysym::Left => Keysym::Home,
Keysym::Right => Keysym::End,
Keysym::Escape => Keysym::Insert,
Keysym::_1 => Keysym::F1,
Keysym::_2 => Keysym::F2,
Keysym::_3 => Keysym::F3,
Keysym::_4 => Keysym::F4,
Keysym::_5 => Keysym::F5,
Keysym::_6 => Keysym::F6,
Keysym::_7 => Keysym::F7,
Keysym::_8 => Keysym::F8,
Keysym::_9 => Keysym::F9,
Keysym::_0 => Keysym::F10,
Keysym::less => Keysym::guillemetleft,
Keysym::greater => Keysym::guillemetright,
// Keysym::braceleft => Keysym::from_char('\u{2039}'),
// Keysym::braceright => Keysym::from_char('\u{203A}'),
Keysym::bracketleft => Keysym::leftsinglequotemark,
Keysym::bracketright => Keysym::rightsinglequotemark,
Keysym::parenleft => Keysym::leftdoublequotemark,
Keysym::parenright => Keysym::rightdoublequotemark,
Keysym::apostrophe => Keysym::singlelowquotemark,
Keysym::quotedbl => Keysym::doublelowquotemark,
Keysym::minus => Keysym::endash,
Keysym::underscore => Keysym::emdash,
Keysym::asciicircum => Keysym::notsign,
Keysym::percent => Keysym::permille,
Keysym::equal => Keysym::approxeq,
Keysym::u => Keysym::mu,
Keysym::a => Keysym::ae,
Keysym::o => Keysym::oe,
Keysym::asterisk => Keysym::degree,
Keysym::period => Keysym::ellipsis,
Keysym::comma => Keysym::periodcentered,
Keysym::exclam => Keysym::exclamdown,
Keysym::question => Keysym::questiondown,
Keysym::bar => Keysym::brokenbar,
Keysym::e => Keysym::EuroSign,
Keysym::l => Keysym::sterling,
// Keysym::r => Keysym::from_char('\u{20B9}'),
Keysym::y => Keysym::yen,
Keysym::c => Keysym::cent,
// Keysym::p => Keysym::from_char('\u{20BD}'),
// Keysym::b => Keysym::from_char('\u{20B1}'),
// Keysym::h => Keysym::from_char('\u{20B4}'),
// Keysym::z => Keysym::from_char('\u{20BF}'),
_ => orig_sym,
}
}
fn modify_no_fn(orig_sym: Keysym) -> Keysym
{
match orig_sym {
/*
Keysym::F11 => Keysym::NoSymbol,
Keysym::F12 => Keysym::NoSymbol,
*/
_ => orig_sym,
}
}
fn new(orig_sym: Keysym) -> Part
{
Part {
orig_sym,
sym: Self::modify_no_fn(orig_sym),
presses: 0,
}
}
#[inline(always)]
pub fn sym(&self) -> Keysym
{
self.sym
}
#[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
}
pub fn modifier_id(&self) -> usize
{
match self.sym {
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(&mut self, mod_state: &[ModState])
{
let mut sym = self.orig_sym;
if mod_state[MOD_FN - 1] != ModState::Released {
sym = Self::modify_fn(sym);
} else {
sym = Self::modify_no_fn(sym);
}
self.sym = sym;
}
pub fn display_symbol(&self, mod_state: &[ModState]) -> Keysym
{
let mut sym = self.sym;
if mod_state[MOD_SHIFT - 1] != ModState::Released {
sym = Self::modify_shift(sym);
}
sym
}
}
pub struct Key {
// NOPUSH
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: Keysym,
top_left: Keysym,
top_right: Keysym,
bottom_left: Keysym,
bottom_right: Keysym,
left: Keysym,
right: Keysym,
top: Keysym,
bottom: Keysym) -> 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,
}
const KEYSYMS: [(&str, Keysym); 20] = [
("\\#", Keysym::numbersign),
("\\?", Keysym::question),
("\\@", Keysym::at),
("\\\\", Keysym::backslash),
("backspace", Keysym::BackSpace),
("ctrl", Keysym::Control_L),
("delete", Keysym::Delete),
("down", Keysym::Down),
("enter", Keysym::Return),
("esc", Keysym::Escape),
("f11_placeholder", Keysym::F11),
("f12_placeholder", Keysym::F12),
("fn", Keysym::XF86_Fn),
("left", Keysym::Left),
("loc alt", Keysym::Alt_L),
("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) -> Keysym
{
if let Ok(idx) = KEYSYMS.binary_search_by_key(&name, |&(k, _)| k) {
return KEYSYMS[idx].1;
}
let mut chars = name.chars();
let c1 = chars.next();
let c2 = chars.next();
match (c1, c2) {
(Some(c), None) => Keysym::new(c as u32),
_ => 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(dir: &Path, filename: &str) -> Result<Self, Error>
{
let mut layout = Layout {
rows: Vec::new(),
width: 0.0,
height: 0.0,
row_width: 0.0,
in_row: 0,
};
let mut vec = Vec::new();
let path = dir.join(filename);
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(&mut self, 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(mod_state);
}
}
}
}
}