// 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::CStr, os::raw::c_char}; use scopeguard::ScopeGuard; use anyhow::Result; use thiserror::Error; use super::{ ffi::{ xkb_state, xkb_state_key_get_one_sym, xkb_state_key_get_utf8, xkb_state_new, xkb_state_unref, xkb_state_update_key, }, keymap::Keymap, }; pub struct State { state: *mut xkb_state, } impl State { pub fn new(keymap: &Keymap) -> Result { let raw_state = unsafe { xkb_state_new(keymap.get_handle()) }; let state = scopeguard::guard(raw_state, |raw_state| unsafe { xkb_state_unref(raw_state); }); if raw_state.is_null() { return Err(StateError::FailedCreation().into()); } Ok(Self { state: ScopeGuard::into_inner(state), }) } pub fn update_key(&self, code: u32, pressed: bool) { let direction = if pressed { super::ffi::xkb_key_direction::DOWN } else { super::ffi::xkb_key_direction::UP }; unsafe { xkb_state_update_key(self.state, code, direction); } } pub fn get_string(&self, code: u32) -> Option { let mut buffer: [c_char; 16] = [0; 16]; let len = unsafe { xkb_state_key_get_utf8( self.state, code, buffer.as_mut_ptr(), std::mem::size_of_val(&buffer), ) }; if len > 0 { let content_raw = unsafe { CStr::from_ptr(buffer.as_ptr()) }; let string = content_raw.to_string_lossy().to_string(); if string.is_empty() { None } else { Some(string) } } else { None } } pub fn get_sym(&self, code: u32) -> Option { let sym = unsafe { xkb_state_key_get_one_sym(self.state, code) }; if sym == 0 { None } else { Some(sym) } } } impl Drop for State { fn drop(&mut self) { unsafe { xkb_state_unref(self.state); } } } #[derive(Error, Debug)] pub enum StateError { #[error("could not create xkb state")] FailedCreation(), }