espanso/espanso-inject/src/evdev/keymap.rs
2021-08-10 21:11:04 +02:00

113 lines
2.9 KiB
Rust

// This code is a port of the libxkbcommon "interactive-evdev.c" example
// https://github.com/xkbcommon/libxkbcommon/blob/master/tools/interactive-evdev.c
use std::ffi::CString;
use scopeguard::ScopeGuard;
use anyhow::Result;
use thiserror::Error;
use crate::KeyboardConfig;
use super::{
context::Context,
ffi::{
xkb_keymap, xkb_keymap_new_from_names, xkb_keymap_unref, xkb_rule_names,
XKB_KEYMAP_COMPILE_NO_FLAGS,
},
};
pub struct Keymap {
keymap: *mut xkb_keymap,
}
impl Keymap {
pub fn new(context: &Context, rmlvo: Option<KeyboardConfig>) -> Result<Keymap> {
let owned_rmlvo = Self::generate_owned_rmlvo(rmlvo);
let names = Self::generate_names(&owned_rmlvo);
let raw_keymap = unsafe {
xkb_keymap_new_from_names(context.get_handle(), &names, XKB_KEYMAP_COMPILE_NO_FLAGS)
};
let keymap = scopeguard::guard(raw_keymap, |raw_keymap| unsafe {
xkb_keymap_unref(raw_keymap);
});
if raw_keymap.is_null() {
return Err(KeymapError::FailedCreation().into());
}
Ok(Self {
keymap: ScopeGuard::into_inner(keymap),
})
}
pub fn get_handle(&self) -> *mut xkb_keymap {
self.keymap
}
fn generate_owned_rmlvo(rmlvo: Option<KeyboardConfig>) -> OwnedRawKeyboardConfig {
let rules = rmlvo
.as_ref()
.and_then(|config| config.rules.clone())
.unwrap_or_default();
let model = rmlvo
.as_ref()
.and_then(|config| config.model.clone())
.unwrap_or_default();
let layout = rmlvo
.as_ref()
.and_then(|config| config.layout.clone())
.unwrap_or_default();
let variant = rmlvo
.as_ref()
.and_then(|config| config.variant.clone())
.unwrap_or_default();
let options = rmlvo
.as_ref()
.and_then(|config| config.options.clone())
.unwrap_or_default();
OwnedRawKeyboardConfig {
rules: CString::new(rules).expect("unable to create CString for keymap"),
model: CString::new(model).expect("unable to create CString for keymap"),
layout: CString::new(layout).expect("unable to create CString for keymap"),
variant: CString::new(variant).expect("unable to create CString for keymap"),
options: CString::new(options).expect("unable to create CString for keymap"),
}
}
fn generate_names(owned_config: &OwnedRawKeyboardConfig) -> xkb_rule_names {
xkb_rule_names {
rules: owned_config.rules.as_ptr(),
model: owned_config.model.as_ptr(),
layout: owned_config.layout.as_ptr(),
variant: owned_config.variant.as_ptr(),
options: owned_config.options.as_ptr(),
}
}
}
impl Drop for Keymap {
fn drop(&mut self) {
unsafe {
xkb_keymap_unref(self.keymap);
}
}
}
#[derive(Error, Debug)]
pub enum KeymapError {
#[error("could not create xkb keymap")]
FailedCreation(),
}
struct OwnedRawKeyboardConfig {
rules: CString,
model: CString,
layout: CString,
variant: CString,
options: CString,
}