// 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) -> Result { 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) -> 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, }