Initial commit

This commit is contained in:
Kenny Levinsen
2023-01-11 00:28:54 +01:00
commit 8e80c0a69c
21 changed files with 4262 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
#![allow(missing_docs, non_upper_case_globals, unused_parens, clippy::all)]
pub const XKB_KEY_BackSpace: u32 = 0xff08; /* Back space, back char */
//pub const XKB_KEY_Tab: u32 = 0xff09;
pub const XKB_KEY_Return: u32 = 0xff0d; /* Return, enter */
//pub const XKB_KEY_Escape: u32 = 0xff1b;
pub const XKB_KEY_Delete: u32 = 0xffff; /* Delete, rubout */
pub const XKB_KEY_Home: u32 = 0xff50;
pub const XKB_KEY_Left: u32 = 0xff51; /* Move left, left arrow */
pub const XKB_KEY_Up: u32 = 0xff52; /* Move up, up arrow */
pub const XKB_KEY_Right: u32 = 0xff53; /* Move right, right arrow */
pub const XKB_KEY_Down: u32 = 0xff54; /* Move down, down arrow */
pub const XKB_KEY_End: u32 = 0xff57; /* EOL */
//pub const XKB_KEY_ISO_Left_Tab: u32 = 0xfe20;
pub const XKB_KEY_e: u32 = 0x0065; /* U+0065 LATIN SMALL LETTER E */
pub const XKB_KEY_a: u32 = 0x0061; /* U+0061 LATIN SMALL LETTER A */
//pub const XKB_KEY_c: u32 = 0x0063; /* U+0063 LATIN SMALL LETTER C */
//pub const XKB_KEY_u: u32 = 0x0075; /* U+0075 LATIN SMALL LETTER U */
+136
View File
@@ -0,0 +1,136 @@
//! Utilities for keymap interpretation of keyboard input
//!
//! This module provides an implementation for `wl_keyboard`
//! objects using `libxkbcommon` to interpret the keyboard input
//! given the user keymap.
//!
//! The entry point of this module is the [`map_keyboard`](fn.map_keyboard.html)
//! function which, given a `wl_seat` and a callback, setup keymap interpretation
//! and key repetition for the `wl_keyboard` of this seat.
//!
//! Key repetition relies on an event source, that needs to be inserted in your
//! calloop event loop. Not doing so will prevent key repetition to work
//! (but the rest of the functionnality will not be affected).
use std::default::Default;
use std::os::unix::io::{AsRawFd, OwnedFd};
use wayland_client::{protocol::wl_keyboard, WEnum};
use xkbcommon::xkb;
pub mod keysyms;
mod state;
use self::state::KbState;
pub use self::state::ModifiersState;
pub struct KeyEvent {
pub serial: u32,
pub time: u32,
pub rawkey: u32,
pub keysym: u32,
pub state: WEnum<wl_keyboard::KeyState>,
pub modifiers: ModifiersState,
pub utf8: Option<String>,
pub repeats: bool,
}
pub struct Keyboard {
state: Option<KbState>,
}
impl Keyboard {
pub fn new() -> Keyboard {
Keyboard { state: None }
}
pub fn keymap(&mut self, format: WEnum<wl_keyboard::KeymapFormat>, fd: OwnedFd, size: u32) {
match format {
WEnum::Value(wl_keyboard::KeymapFormat::XkbV1) => {
self.state = Some(
KbState::new_from_fd(fd.as_raw_fd(), size as usize)
.expect("unable to load keymap"),
);
}
WEnum::Value(wl_keyboard::KeymapFormat::NoKeymap) => {
// TODO: how to handle this (hopefully never occuring) case?
}
_ => unreachable!(),
}
}
pub fn key(
&mut self,
serial: u32,
time: u32,
key: u32,
key_state: WEnum<wl_keyboard::KeyState>,
) -> KeyEvent {
if let Some(state) = &mut self.state {
let (sym, utf8, repeats) = {
// Get the values to generate a key event
let sym = state.get_one_sym_raw(key);
let utf8 = if key_state == WEnum::Value(wl_keyboard::KeyState::Pressed) {
match state.compose_feed(sym) {
Some(xkb::compose::FeedResult::Accepted) => {
if let Some(status) = state.compose_status() {
match status {
xkb::compose::Status::Composed => state.compose_get_utf8(),
xkb::compose::Status::Nothing => state.get_utf8_raw(key),
_ => None,
}
} else {
state.get_utf8_raw(key)
}
}
Some(_) => {
// XKB_COMPOSE_FEED_IGNORED
None
}
None => {
// XKB COMPOSE is not initialized
state.get_utf8_raw(key)
}
}
} else {
None
};
let repeats = state.key_repeats(key + 8);
(sym, utf8, repeats)
};
KeyEvent {
rawkey: key,
keysym: sym,
state: key_state,
modifiers: state.mods_state(),
serial,
time,
utf8,
repeats,
}
} else {
KeyEvent {
rawkey: key,
keysym: 0,
state: key_state,
modifiers: Default::default(),
utf8: None,
repeats: false,
serial,
time,
}
}
}
pub fn modifiers(
&mut self,
mods_depressed: u32,
mods_latched: u32,
mods_locked: u32,
group: u32,
) {
if let Some(state) = &mut self.state {
state.update_modifiers(mods_depressed, mods_latched, mods_locked, group);
}
}
}
+158
View File
@@ -0,0 +1,158 @@
use std::{env, os::unix::io::RawFd};
use xkbcommon::xkb;
pub struct KbState {
xkb_context: xkb::Context,
xkb_keymap: xkb::Keymap,
xkb_state: xkb::State,
xkb_compose_table: Option<xkb::compose::Table>,
xkb_compose_state: Option<xkb::compose::State>,
mods_state: ModifiersState,
}
/// Represents the current state of the keyboard modifiers
///
/// Each field of this struct represents a modifier and is `true` if this modifier is active.
///
/// For some modifiers, this means that the key is currently pressed, others are toggled
/// (like caps lock).
#[derive(Copy, Clone, Debug, Default)]
pub struct ModifiersState {
/// The "control" key
pub ctrl: bool,
/// The "alt" key
pub alt: bool,
/// The "shift" key
pub shift: bool,
/// The "Caps lock" key
pub caps_lock: bool,
/// The "logo" key
///
/// Also known as the "windows" key on most keyboards
pub logo: bool,
/// The "Num lock" key
pub num_lock: bool,
}
impl ModifiersState {
fn new() -> ModifiersState {
ModifiersState::default()
}
fn update_with(&mut self, state: &xkb::State) {
self.ctrl = state.mod_name_is_active("Control", xkb::STATE_MODS_EFFECTIVE);
self.alt = state.mod_name_is_active("Mod1", xkb::STATE_MODS_EFFECTIVE);
self.shift = state.mod_name_is_active("Shift", xkb::STATE_MODS_EFFECTIVE);
self.caps_lock = state.mod_name_is_active("Lock", xkb::STATE_MODS_EFFECTIVE);
self.num_lock = state.mod_name_is_active("Mod2", xkb::STATE_MODS_EFFECTIVE);
self.logo = state.mod_name_is_active("Mod4", xkb::STATE_MODS_EFFECTIVE);
}
}
impl KbState {
pub(crate) fn update_modifiers(
&mut self,
mods_depressed: u32,
mods_latched: u32,
mods_locked: u32,
group: u32,
) {
let mask =
self.xkb_state
.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group);
if (mask & xkb::STATE_MODS_EFFECTIVE) > 0 {
// effective value of mods have changed, we need to update our state
self.mods_state.update_with(&self.xkb_state);
}
}
pub(crate) fn get_one_sym_raw(&mut self, keycode: u32) -> u32 {
self.xkb_state.key_get_one_sym(keycode + 8)
}
pub(crate) fn get_utf8_raw(&mut self, keycode: u32) -> Option<String> {
Some(self.xkb_state.key_get_utf8(keycode + 8))
}
pub(crate) fn compose_feed(&mut self, keysym: u32) -> Option<xkb::compose::FeedResult> {
match &mut self.xkb_compose_state {
Some(compose_state) => Some(compose_state.feed(keysym)),
None => None,
}
}
pub(crate) fn compose_status(&mut self) -> Option<xkb::compose::Status> {
match &mut self.xkb_compose_state {
Some(compose_state) => Some(compose_state.status()),
None => None,
}
}
pub(crate) fn compose_get_utf8(&mut self) -> Option<String> {
match &mut self.xkb_compose_state {
Some(compose_state) => compose_state.utf8(),
None => None,
}
}
pub(crate) fn new_from_fd(fd: RawFd, size: usize) -> Result<KbState, std::io::Error> {
let xkb_context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
let xkb_keymap = unsafe {
xkb::Keymap::new_from_fd(
&xkb_context,
fd,
size,
xkb::KEYMAP_FORMAT_TEXT_V1,
xkb::KEYMAP_COMPILE_NO_FLAGS,
)?
.unwrap()
};
let xkb_state = xkb::State::new(&xkb_keymap);
let mut me = KbState {
xkb_context,
xkb_keymap,
xkb_state,
xkb_compose_table: None,
xkb_compose_state: None,
mods_state: ModifiersState::new(),
};
me.init_compose();
Ok(me)
}
pub(crate) fn init_compose(&mut self) {
let locale = env::var_os("LC_ALL")
.and_then(|v| if v.is_empty() { None } else { Some(v) })
.or_else(|| env::var_os("LC_CTYPE"))
.and_then(|v| if v.is_empty() { None } else { Some(v) })
.or_else(|| env::var_os("LANG"))
.and_then(|v| if v.is_empty() { None } else { Some(v) })
.unwrap_or_else(|| "C".into());
let compose_table = match xkb::compose::Table::new_from_locale(
&self.xkb_context,
&locale,
xkb::compose::COMPILE_NO_FLAGS,
) {
Ok(t) => t,
Err(_) => return,
};
let compose_state = xkb::compose::State::new(&compose_table, xkb::compose::STATE_NO_FLAGS);
self.xkb_compose_table = Some(compose_table);
self.xkb_compose_state = Some(compose_state);
}
pub(crate) fn key_repeats(&mut self, keycode: xkb::Keycode) -> bool {
self.xkb_keymap.key_repeats(keycode)
}
#[inline]
pub(crate) fn mods_state(&self) -> ModifiersState {
self.mods_state
}
}