extern crate alloc;
extern crate core;
pub mod map_builder;
use std::collections::BTreeMap;
use std::error::Error;
use std::ffi::{c_char, CStr, CString, NulError};
use std::fmt;
use std::fmt::Formatter;
use std::ptr::null;
use xkbcommon_sys::*;
pub struct Mod {
pub index: xkb_mod_index_t,
pub keycode: xkb_keycode_t,
pub name: String,
}
struct OptCString {
value: Option<CString>,
}
impl OptCString {
fn as_ptr(&self) -> *const c_char {
match &self.value {
None => {null()}
Some(v) => {v.as_ptr()}
}
}
fn from_opt_str(value: Option<&str>) -> Result<Self, NulError> {
Ok(Self{value:
match value {
None => None,
Some(v) => Some(CString::new(v)?)
}})
}
}
#[derive(Clone)]
pub struct Key {
pub keycode: u32,
pub keyname: String,
pub symbols: BTreeMap<xkb_level_index_t, Symbol>,
pub num_layouts: u32,
}
#[derive(Clone)]
pub struct Symbol {
pub keysyms: Vec<u32>,
pub unicode: Option<char>,
pub symbol: Option<String>,
pub display: String,
pub visible: bool,
}
impl Symbol {
fn new(ch: char, keysyms: Vec<u32>, num_layouts: u32) -> Symbol {
let symbol = Self::resolve_keysyms(&keysyms);
let representation: (Option<String>, bool) = if keysyms.iter()
.any(|val| {
#[allow(non_upper_case_globals)]
match *val {XKB_KEY_KP_Space | XKB_KEY_Tab | XKB_KEY_KP_Enter |
XKB_KEY_KP_F1..=XKB_KEY_KP_Delete | XKB_KEY_KP_Equal |
XKB_KEY_KP_Multiply..=XKB_KEY_KP_9 => true,
_ => false
}
}) {
(Some(String::from(symbol.as_ref().unwrap_or(&format!("KP_{}", ch)))), true)
} else {
match ch as u32 {
0x0020 | 0x0085 | 0x00A0 | 0x1680 | 0x2000..=0x200A | 0x2028 | 0x2029 | 0x202F | 0x205F | 0x3000
=> (Self::resolve_keysyms(&keysyms), true),
0x0000..=0x001F | 0x007F..=0x009F |
0x00AD | 0x034F | 0x061C | 0x115F..=0x1160 | 0x17B4..=0x17B5 | 0x180B..=0x180F | 0x200B..=0x200F |
0x202A..=0x202E | 0x2060..=0x206F | 0x3164 | 0xFE00..=0xFE0F | 0xFEFF | 0xFFA0 | 0xFFF0..=0xFFF8 |
0x1BCA0..=0x1BCA3 | 0x1D173..=0x1D17A | 0xE0000..=0xE0FFF
=> (Self::resolve_keysyms(&keysyms), false),
_ => (Some(ch.to_string()), true)
}
};
Symbol {
keysyms,
unicode: Some(ch),
symbol,
display: representation.0.unwrap_or("<No Keysym>".to_string()),
visible: representation.1,
}
}
fn resolve_keysyms(keysyms: &Vec<u32>) -> Option<String> {
let mut symbols: Vec<String> = Vec::new();
let mut buf: Vec<u8> = Vec::with_capacity(32);
buf.resize(32, 0);
if keysyms.is_empty() {
return None;
}
for keysym in keysyms {
unsafe {
let n = xkb_keysym_get_name(*keysym, buf.as_mut_ptr() as _, buf.len() as _);
if n > 0 {
let size = (if n > 32 { 32 } else { n }) as usize;
symbols.push(CString::from_vec_unchecked(buf[0..(size)].to_vec()).into_string().unwrap_or_default());
}
}
}
Some(match symbols.len() {
0 => format!("Resolution Error for {:#10x}", keysyms[0]),
1 => format!("{}", symbols[0].to_owned()),
_ => format!("({})", symbols.join("|"))
})
}
}
impl Key {
fn new_unicode(keycode: u32, keyname: String, symbols: BTreeMap<xkb_level_index_t, Symbol>, num_layouts: u32) -> Key {
Key {
keycode,
keyname,
symbols,
num_layouts,
}
}
fn new_mod(keycode: u32, keyname: String, keysyms: Vec<u32>, num_layouts: u32) -> Key {
let symbol = Symbol::resolve_keysyms(&keysyms);
let display = match &symbol {
Some(s) => s.clone(),
None => "No Keysym".to_string()
};
let mut symbols = BTreeMap::new();
symbols.insert(0, Symbol{keysyms, unicode: None, symbol, display, visible: false});
Key { keycode, keyname, symbols, num_layouts }
}
pub fn symbol_name(&self) -> Option<String> {
self.symbols.first_key_value()?.1.symbol.clone()
}
pub fn keysyms(&self) -> Vec<u32> {
if let Some(s) = self.symbols.first_key_value() {
s.1.keysyms.clone()
} else { Vec::new() }
}
pub fn display(&self) -> String {
if let Some(s) = self.symbols.first_key_value() {
s.1.display.clone()
} else { "".to_string() }
}
#[inline]
pub fn evdev_code(&self) -> u16 {
self.keycode as u16 - 8
}
#[inline]
pub fn visible_unicode(&self) -> Option<char> {
let sym = self.symbols.first_key_value()?.1;
if sym.visible {
sym.unicode
} else { None }
}
}
pub struct Keymap {
pub layouts: Vec<String>,
pub mods: Vec<Mod>,
pub keys: Vec<Key>,
}
impl Keymap {
pub fn from_rmlvo(
rules: Option<&str>, model: Option<&str>, layout: Option<&str>, variant: Option<&str>, options: Option<&str>, all_levels: bool
) -> Result<Keymap, KMError> {
let context;
let keymap;
unsafe {
context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if context.is_null() {
return Err(KMError{message: "xkb_context_new() returned null".to_string()});
}
let rules = OptCString::from_opt_str(rules)?;
let model = OptCString::from_opt_str(model)?;
let layout = OptCString::from_opt_str(layout)?;
let variant = OptCString::from_opt_str(variant)?;
let options = OptCString::from_opt_str(options)?;
let names = xkb_rule_names {
rules: rules.as_ptr(),
model: model.as_ptr(),
layout: layout.as_ptr(),
variant: variant.as_ptr(),
options: options.as_ptr(),
};
keymap = xkb_keymap_new_from_names(context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS);
if keymap.is_null() {
return Err(KMError{message: "xkb_keymap_new_from_names() returned null".to_string()});
}
};
Self::init_data(context, keymap, all_levels)
}
pub fn current_from_xcb(all_levels: bool) -> Result<Keymap, KMError> {
let context;
let keymap;
unsafe {
context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
let conn = match xcb::Connection::connect(None) {
Ok((c, _)) => c,
Err(_) => return Err(KMError { message: "Error creating XCB connection".to_string() })
};
match conn.has_error() {
Ok(_) => {}
Err(e) => println!("{}", e)
}
Self::setup_xkb_extension(&conn)?;
let mut dev_id = xkb_x11_get_core_keyboard_device_id(conn.get_raw_conn() as _);
if dev_id == -1 {
dev_id = 1;
}
keymap = xkb_x11_keymap_new_from_device(context, conn.get_raw_conn() as _, dev_id, XKB_KEYMAP_COMPILE_NO_FLAGS);
};
Self::init_data(context, keymap, all_levels)
}
fn init_data(context: *mut xkb_context, keymap: *mut xkb_keymap, all_levels: bool) -> Result<Keymap, KMError> {
let mut layouts: Vec<String> = Vec::new();
unsafe {
let num_la = xkb_keymap_num_layouts(keymap);
if num_la < 1 {
return Err(KMError{message: "Number of Layouts is 0".to_string()});
}
for idx in 0..num_la {
let name = CStr::from_ptr(xkb_keymap_layout_get_name(keymap, idx)).to_str().unwrap_or_default().to_string();
layouts.push(name);
}
}
let mut mods: Vec<Mod> = Vec::new();
unsafe {
let num_mod = xkb_keymap_num_mods(keymap);
for idx in 0..num_mod {
let name = CStr::from_ptr(xkb_keymap_mod_get_name(keymap, idx)).to_str().unwrap_or_default().to_string();
mods.push(Mod { index: idx, name, keycode: idx })
}
}
let mut keys: Vec<Key> = Vec::new();
unsafe {
let min_keycode = xkb_keymap_min_keycode(keymap);
let max_keycode = xkb_keymap_max_keycode(keymap);
let mut buf: Vec<u8> = Vec::with_capacity(64);
buf.resize(64, 0);
let mut sym_buf: Vec<*const u32> = Vec::with_capacity(64);
sym_buf.resize(64, null());
let layout = 0;
for code in min_keycode..=max_keycode {
let cs = xkb_keymap_key_get_name(keymap, code);
if cs != null() {
let hw_name = CStr::from_ptr(cs).to_str().unwrap_or_default();
let num_layouts = xkb_keymap_num_layouts_for_key(keymap, code);
if num_layouts == 0 {
continue;
}
let num_levels = if all_levels {xkb_keymap_num_levels_for_key(keymap, code, layout)} else {1};
let mut symbols = BTreeMap::new();
for level in 0..num_levels {
let sym_count = xkb_keymap_key_get_syms_by_level(keymap, code, 0, level, sym_buf.as_mut_ptr() as _) as usize;
if sym_count == 0 {continue;}
let mut keysyms: Vec<u32> = Vec::new();
let sym = *sym_buf[0] as xkb_keysym_t;
for i in 0..sym_count {
keysyms.push(*sym_buf[i]);
}
if sym_count > 1 {
println!("{sym_count} symbols for key {hw_name}: {keysyms:?}");
}
let n = xkb_keysym_to_utf8(sym, buf.as_mut_ptr() as _, buf.len());
if n > 0 {
let unicode = CString::from_vec_unchecked(buf[0..(n as usize)].to_vec()).into_string().unwrap_or_default();
let ch = unicode.chars().nth(0).unwrap();
let sym = Symbol::new(ch, keysyms, num_layouts);
symbols.insert(level, sym);
} else {
keys.push(Key::new_mod(code, hw_name.to_string(), keysyms, num_layouts));
};
}
if !symbols.is_empty() {
keys.push(Key::new_unicode(code, hw_name.to_string(), symbols, num_layouts));
}
}
}
}
Ok(Keymap { layouts, mods, keys })
}
fn setup_xkb_extension(conn: &xcb::Connection) -> Result<(), KMError> {
let mut major_ver_out: u16 = 0;
let mut minor_ver_out: u16 = 0;
let mut base_event_out: u8 = 0;
let mut base_error_out: u8 = 0;
unsafe {
let ret = xkb_x11_setup_xkb_extension(conn.get_raw_conn() as _,
XKB_X11_MIN_MAJOR_XKB_VERSION.try_into().unwrap(),
XKB_X11_MIN_MINOR_XKB_VERSION.try_into().unwrap(),
XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
&mut major_ver_out, &mut minor_ver_out,
&mut base_event_out, &mut base_error_out);
if ret != 1 {
return Err(KMError { message: "Error setting up xkb extension".to_string() });
}
}
Ok(())
}
}
#[derive(Debug)]
pub struct KMError {
message: String,
}
impl Error for KMError {}
impl fmt::Display for KMError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)
}
}
impl From<NulError> for KMError {
fn from(e: NulError) -> Self {
KMError{ message: e.to_string() }
}
}
#[cfg(test)]
mod tests {
use crate::Keymap;
use crate::map_builder::{BuildMaps, Fill, MapBuilder, SymbolType};
#[test]
fn load_keymap() {
let keymap = Keymap::from_rmlvo(
Some("evdev"), Some("pc105"), Some("us"), Some("dvp"), Some("caps:swapescape"), true
).unwrap();
let esc = keymap.keys[0].clone();
assert_eq!(esc.evdev_code(), 1);
assert_eq!(esc.symbol_name(), Some("Caps_Lock".to_string()));
}
#[test]
fn load_invalid_keymap() {
assert!(Keymap::from_rmlvo(
Some("evdev"), Some("pc105"), Some("this_should_not_exist"), None, None, false
).is_err())
}
#[test]
fn stuff_is_public_enough() {
let keymap = Keymap::current_from_xcb(true).unwrap();
let (_to, _from) = MapBuilder::default()
.code_evdev()
.symbol(SymbolType::SymbolDisplay)
.fill(Fill::Evdev(true))
.build(&keymap, BuildMaps::Both);
let (_to, _from) = MapBuilder::default()
.code_xkb()
.symbol_unicode()
.replace_symbol_by_function(|x|{x})
.build(&keymap, BuildMaps::Both);
let (_to, _from) = MapBuilder::default()
.code_xkb_hw()
.symbol_codes()
.build(&keymap, BuildMaps::Both);
}
}