Initial commit
This commit is contained in:
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user