feat(teipe): initial commit

This commit is contained in:
2024-08-18 17:43:59 +02:00
commit 7f2dac5a61
30 changed files with 3390 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
use super::{
error,
ffi::{self, context},
};
use std::{ffi::CStr, ptr::NonNull};
pub struct Context {
ctx: NonNull<ffi::context>,
}
impl Context {
pub fn new() -> Result<Self, error::Error> {
let ctx = NonNull::new(unsafe { ffi::context_new(ffi::context_flags::NoFlags) })
.ok_or(error::Error::ContextNewFailed)?;
unsafe {
let ctx = ctx.as_ptr();
if let Some(level) = log::max_level().to_level() {
ffi::context_set_log_level(ctx, level.into());
ffi::log_set_callback(Some(log_callback));
ffi::context_set_log_fn(ctx, Some(ffi::log_logger));
} else {
ffi::context_set_log_fn(ctx, Some(null_handler))
}
}
Ok(Self { ctx })
}
pub(crate) fn as_raw(&self) -> *mut context {
self.ctx.as_ptr()
}
}
impl Drop for Context {
fn drop(&mut self) {
unsafe { ffi::context_unref(self.as_raw()) }
}
}
unsafe extern "C" fn null_handler(
_ctx: *mut context,
_priority: ffi::log_level,
_format: *const ::std::os::raw::c_char,
_args: *mut ffi::__va_list_tag,
) {
// no op
}
unsafe extern "C" fn log_callback(
_ctx: *mut context,
priority: ffi::log_level,
str: *const std::os::raw::c_char,
) {
match unsafe { CStr::from_ptr(str).to_str() } {
Ok(str) => {
log::log!(
target: "libxkbcommon",
priority.into(),
"{str}"
);
}
Err(e) => log::error!("Failed to log message with severity {priority:?}.\n\t{e}"),
}
}

View File

@@ -0,0 +1,15 @@
use std::fmt::{Debug, Display};
#[derive(Debug)]
pub enum Error {
ContextNewFailed,
InvalidKeymap,
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Debug::fmt(&self, f)
}
}
impl std::error::Error for Error {}

61
libxkbcommonrs/src/ffi.rs Normal file
View File

@@ -0,0 +1,61 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(unused)]
use std::fmt::{Debug, Display};
pub mod syms {
include!(concat!(env!("OUT_DIR"), "/libxkbcommon-keysyms.rs"));
}
include!(concat!(env!("OUT_DIR"), "/libxkbcommon.rs"));
impl From<log_level> for log::Level {
fn from(value: log_level) -> Self {
match value {
log_level::Debug => log::Level::Debug,
log_level::Info => log::Level::Info,
log_level::Warning => log::Level::Warn,
log_level::Error => log::Level::Error,
_ => log::Level::Debug,
}
}
}
impl From<log::Level> for log_level {
fn from(value: log::Level) -> Self {
match value {
log::Level::Trace | log::Level::Debug => log_level::Debug,
log::Level::Info => log_level::Info,
log::Level::Warn => log_level::Warning,
log::Level::Error => log_level::Error,
}
}
}
impl Display for Keysym {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut buffer = [0_u8; 32];
let written =
unsafe { keysym_to_utf8(*self, buffer.as_mut_ptr() as *mut _, buffer.len()) } as usize;
let str = std::str::from_utf8(&buffer[0..written]).unwrap_or("<invalid>");
write!(f, "{str}")
}
}
impl std::fmt::Debug for Keysym {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut buffer = [0_u8; 32];
let written =
unsafe { keysym_get_name(*self, buffer.as_mut_ptr() as *mut _, buffer.len()) } as usize;
let str = std::str::from_utf8(&buffer[0..written]).unwrap_or("<invalid>");
write!(f, "{str}")
}
}
impl From<u32> for Keysym {
fn from(value: u32) -> Self {
Self(value)
}
}

View File

@@ -0,0 +1,133 @@
use std::ffi::c_void;
use std::{ffi::CStr, ptr::NonNull};
use crate::{error, ffi, LayoutID, LayoutRef};
use super::context::Context;
pub use ffi::keycode_t as Key;
pub use ffi::layout_index_t as Level;
pub use ffi::Keysym;
pub type KeyIter<'a> = Box<dyn FnMut(Key) + 'a>;
pub struct Keymap {
keymap: NonNull<ffi::keymap>,
format: ffi::keymap_format,
}
impl Keymap {
pub fn new(ctx: &Context, buffer: &[u8]) -> Result<Self, error::Error> {
let format = ffi::keymap_format::TextV1;
let keymap = unsafe {
ffi::keymap_new_from_buffer(
ctx.as_raw(),
buffer.as_ptr() as *const i8,
buffer.len(),
format,
ffi::keymap_compile_flags::NoFlags,
)
};
Ok(Self {
keymap: NonNull::new(keymap).ok_or(error::Error::InvalidKeymap)?,
format,
})
}
pub(crate) fn as_raw(&self) -> *mut ffi::keymap {
self.keymap.as_ptr()
}
}
impl<'a> Keymap {
pub fn get_layouts(&'a self) -> Vec<LayoutRef<'a>> {
let num_layouts = unsafe { ffi::keymap_num_layouts(self.as_raw()) };
let mut v = Vec::with_capacity(num_layouts as usize);
for i in 0..num_layouts {
v.push(LayoutRef::new(self, i));
}
v
}
pub fn get_sims_by_level(
&'a self,
key: Key,
layout: Option<LayoutID>,
level: Level,
) -> &'a [Keysym] {
let mut syms_out: *const Keysym = std::ptr::null_mut();
unsafe {
let ptr = &mut syms_out as *mut _;
let len = ffi::keymap_key_get_syms_by_level(
self.as_raw(),
key,
layout.unwrap_or_default(),
level,
ptr,
);
if syms_out.is_null() || !syms_out.is_aligned() {
&[]
} else {
std::slice::from_raw_parts(syms_out, len as usize)
}
}
}
pub fn num_levels_for_key(&self, key: Key, layout: Option<LayoutID>) -> u32 {
unsafe { ffi::keymap_num_levels_for_key(self.as_raw(), key, layout.unwrap_or_default()) }
}
pub fn key_for_each(&'a self, visitor: KeyIter<'a>) {
let ptr = (&visitor) as *const _;
unsafe {
ffi::keymap_key_for_each(self.as_raw(), Some(key_iter), ptr as *mut c_void);
}
}
pub fn key_get_name(&self, key: Key) -> Option<&str> {
let ptr = unsafe { ffi::keymap_key_get_name(self.as_raw(), key) };
if ptr.is_null() {
return None;
}
unsafe { CStr::from_ptr(ptr) }
.to_str()
.inspect_err(|e| log::warn!("Failed to get name for key {key}: {e}"))
.ok()
}
}
impl std::fmt::Debug for Keymap {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let str = unsafe { CStr::from_ptr(ffi::keymap_get_as_string(self.as_raw(), self.format)) }
.to_str()
.unwrap();
write!(f, "{str}")
}
}
pub fn utf32_to_keysim(utf32: &[u32]) -> Vec<Keysym> {
let mut ret = Vec::with_capacity(utf32.len());
for c in utf32 {
let ks = unsafe { ffi::utf32_to_keysym(*c) };
ret.push(ks);
}
ret
}
pub fn keycode_is_legal_ext(key: Key) -> bool {
key <= ffi::KEYCODE_MAX
}
pub fn keycode_is_legal_x11(key: Key) -> bool {
(8..=255).contains(&key)
}
unsafe extern "C" fn key_iter(
_keymap: *mut ffi::keymap,
key: ffi::keycode_t,
data: *mut ::std::os::raw::c_void,
) {
let visitor = unsafe { &mut *(data as *mut KeyIter<'_>) };
visitor(key);
}

View File

@@ -0,0 +1,33 @@
use std::{ffi::CStr, fmt::Display};
use crate::{ffi, Keymap};
pub type LayoutID = ffi::layout_index_t;
#[derive(Copy, Clone)]
pub struct LayoutRef<'a> {
keymap: &'a Keymap,
id: ffi::layout_index_t,
}
impl<'a> LayoutRef<'a> {
pub fn new(keymap: &'a Keymap, id: ffi::layout_index_t) -> Self {
Self { keymap, id }
}
pub fn get_name(&'a self) -> &'a str {
unsafe { CStr::from_ptr(ffi::keymap_layout_get_name(self.keymap.as_raw(), self.id)) }
.to_str()
.unwrap_or("<invalid>")
}
pub fn get_id(&self) -> LayoutID {
self.id
}
}
impl<'a> Display for LayoutRef<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.get_name())
}
}

11
libxkbcommonrs/src/lib.rs Normal file
View File

@@ -0,0 +1,11 @@
mod context;
mod error;
mod ffi;
mod keymap;
mod layout;
pub use context::Context;
pub use keymap::*;
pub use layout::*;
pub use ffi::syms;