// 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(&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(&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>, 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 { 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> { &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, 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(&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); } } } } }