Example: %YAML 1.2 --- longpress_ms: 600 repeat_ms: 25 layout: latn_qwerty_us.xml wayland: height: 185
782 lines
24 KiB
Rust
782 lines
24 KiB
Rust
// 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);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|