rewrite in rust
This commit is contained in:
parent
d43f61ec76
commit
4a6b261be0
51 changed files with 3104 additions and 9319 deletions
540
src/core/layout.rs
Normal file
540
src/core/layout.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue