307 lines
9.2 KiB
Rust
307 lines
9.2 KiB
Rust
use {
|
|
crate::{
|
|
backend::KeyState,
|
|
ifs::wl_seat::WlSeatGlobal,
|
|
keyboard::{DynKeyboardState, KeyboardState, KeyboardStateId, KeymapFd},
|
|
utils::{
|
|
oserror::{OsError, OsErrorExt},
|
|
syncqueue::SyncQueue,
|
|
vecset::VecSet,
|
|
},
|
|
},
|
|
kbvm::{
|
|
Keycode,
|
|
lookup::LookupTable,
|
|
state_machine::{self, Direction, Event, StateMachine},
|
|
xkb::{
|
|
self, Keymap,
|
|
diagnostic::{Diagnostic, WriteToLog},
|
|
keymap::{Indicator, IndicatorMatcher},
|
|
rmlvo::Group,
|
|
},
|
|
},
|
|
std::{
|
|
cell::{Cell, Ref, RefCell},
|
|
io::Write,
|
|
rc::Rc,
|
|
},
|
|
thiserror::Error,
|
|
uapi::c,
|
|
};
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum KbvmError {
|
|
#[error("could not parse the keymap")]
|
|
CouldNotParseKeymap(#[source] Diagnostic),
|
|
#[error("Could not create a keymap memfd")]
|
|
KeymapMemfd(#[source] OsError),
|
|
}
|
|
|
|
pub struct KbvmContext {
|
|
pub ctx: xkb::Context,
|
|
}
|
|
|
|
impl Default for KbvmContext {
|
|
fn default() -> Self {
|
|
let mut ctx = xkb::Context::builder();
|
|
ctx.enable_environment(true);
|
|
Self { ctx: ctx.build() }
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
|
pub struct KbvmMapId([u8; 32]);
|
|
|
|
pub struct KbvmMap {
|
|
pub id: KbvmMapId,
|
|
pub state_machine: StateMachine,
|
|
pub lookup_table: LookupTable,
|
|
pub map: KeymapFd,
|
|
pub xwayland_map: KeymapFd,
|
|
pub has_indicators: bool,
|
|
pub num_lock: Option<IndicatorMatcher>,
|
|
pub caps_lock: Option<IndicatorMatcher>,
|
|
pub scroll_lock: Option<IndicatorMatcher>,
|
|
pub compose: Option<IndicatorMatcher>,
|
|
pub kana: Option<IndicatorMatcher>,
|
|
}
|
|
|
|
pub struct KbvmState {
|
|
pub map: Rc<KbvmMap>,
|
|
pub state: state_machine::State,
|
|
pub kb_state: KeyboardState,
|
|
}
|
|
|
|
pub struct PhysicalKeyboardState {
|
|
state: Rc<RefCell<KbvmState>>,
|
|
inner: RefCell<PkInner>,
|
|
events: SyncQueue<Event>,
|
|
flushing: Cell<bool>,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct PkInner {
|
|
pressed_keys: VecSet<u32>,
|
|
event_stash: Vec<Event>,
|
|
}
|
|
|
|
impl DynKeyboardState for RefCell<KbvmState> {
|
|
fn borrow(&self) -> Ref<'_, KeyboardState> {
|
|
Ref::map(self.borrow(), |v| &v.kb_state)
|
|
}
|
|
}
|
|
|
|
impl KbvmContext {
|
|
pub fn parse_keymap(&self, keymap: &[u8]) -> Result<Rc<KbvmMap>, KbvmError> {
|
|
let map = self
|
|
.ctx
|
|
.keymap_from_bytes(WriteToLog, None, keymap)
|
|
.map_err(KbvmError::CouldNotParseKeymap)?;
|
|
let id = KbvmMapId(*blake3::hash(keymap).as_bytes());
|
|
self.create_keymap(id, map)
|
|
}
|
|
|
|
pub fn keymap_from_rmlvo(
|
|
&self,
|
|
rules: Option<&str>,
|
|
model: Option<&str>,
|
|
layout: Option<&str>,
|
|
variant: Option<&str>,
|
|
options: Option<&str>,
|
|
) -> Result<Rc<KbvmMap>, KbvmError> {
|
|
let mut groups = None::<Vec<_>>;
|
|
if layout.is_some() || variant.is_some() {
|
|
groups = Some(
|
|
Group::from_layouts_and_variants(
|
|
layout.unwrap_or_default(),
|
|
variant.unwrap_or_default(),
|
|
)
|
|
.collect(),
|
|
);
|
|
}
|
|
let mut options_vec = None::<Vec<_>>;
|
|
if let Some(options) = options {
|
|
options_vec = Some(options.split(",").collect());
|
|
}
|
|
self.keymap_from_names(rules, model, groups.as_deref(), options_vec.as_deref())
|
|
}
|
|
|
|
pub fn keymap_from_names(
|
|
&self,
|
|
rules: Option<&str>,
|
|
model: Option<&str>,
|
|
groups: Option<&[Group<'_>]>,
|
|
options: Option<&[&str]>,
|
|
) -> Result<Rc<KbvmMap>, KbvmError> {
|
|
let map = self
|
|
.ctx
|
|
.keymap_from_names(WriteToLog, rules, model, groups, options);
|
|
let id = KbvmMapId(*blake3::hash(map.format().to_string().as_bytes()).as_bytes());
|
|
self.create_keymap(id, map)
|
|
}
|
|
|
|
fn create_keymap(&self, id: KbvmMapId, map: Keymap) -> Result<Rc<KbvmMap>, KbvmError> {
|
|
let mut has_indicators = false;
|
|
let mut num_lock = None;
|
|
let mut caps_lock = None;
|
|
let mut scroll_lock = None;
|
|
let mut compose = None;
|
|
let mut kana = None;
|
|
for indicator in map.indicators() {
|
|
match indicator.name() {
|
|
Indicator::NUM_LOCK => num_lock = Some(indicator.matcher()),
|
|
Indicator::CAPS_LOCK => caps_lock = Some(indicator.matcher()),
|
|
Indicator::SCROLL_LOCK => scroll_lock = Some(indicator.matcher()),
|
|
Indicator::COMPOSE => compose = Some(indicator.matcher()),
|
|
Indicator::KANA => kana = Some(indicator.matcher()),
|
|
_ => continue,
|
|
}
|
|
has_indicators = true;
|
|
}
|
|
let builder = map.to_builder();
|
|
let (_, xwayland_map) = create_keymap_memfd(&map, true).map_err(KbvmError::KeymapMemfd)?;
|
|
let (_, map) = create_keymap_memfd(&map, false).map_err(KbvmError::KeymapMemfd)?;
|
|
Ok(Rc::new(KbvmMap {
|
|
id,
|
|
state_machine: builder.build_state_machine(),
|
|
map,
|
|
xwayland_map,
|
|
lookup_table: builder.build_lookup_table(),
|
|
has_indicators,
|
|
num_lock,
|
|
caps_lock,
|
|
scroll_lock,
|
|
compose,
|
|
kana,
|
|
}))
|
|
}
|
|
}
|
|
|
|
fn create_keymap_memfd(map: &Keymap, xwayland: bool) -> Result<(String, KeymapFd), OsError> {
|
|
let mut format = map.format();
|
|
if xwayland {
|
|
format = format.lookup_only(true).rename_long_keys(true);
|
|
}
|
|
let str = format!("{}\n", format);
|
|
let mut memfd =
|
|
uapi::memfd_create("keymap", c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING).to_os_error()?;
|
|
memfd.write_all(str.as_bytes())?;
|
|
memfd.write_all(&[0])?;
|
|
uapi::lseek(memfd.raw(), 0, c::SEEK_SET).to_os_error()?;
|
|
uapi::fcntl_add_seals(
|
|
memfd.raw(),
|
|
c::F_SEAL_SEAL | c::F_SEAL_GROW | c::F_SEAL_SHRINK | c::F_SEAL_WRITE,
|
|
)
|
|
.to_os_error()?;
|
|
let fd = KeymapFd {
|
|
map: Rc::new(memfd),
|
|
len: str.len() + 1,
|
|
};
|
|
Ok((str, fd))
|
|
}
|
|
|
|
impl KbvmMap {
|
|
pub fn state(self: &Rc<Self>, id: KeyboardStateId) -> KbvmState {
|
|
KbvmState {
|
|
map: self.clone(),
|
|
state: self.state_machine.create_state(),
|
|
kb_state: KeyboardState {
|
|
id,
|
|
map: self.clone(),
|
|
pressed_keys: Default::default(),
|
|
mods: Default::default(),
|
|
leds: Default::default(),
|
|
leds_changed: Default::default(),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl KbvmState {
|
|
pub fn apply_events(&mut self, events: &SyncQueue<Event>) {
|
|
let state = &mut self.kb_state;
|
|
while let Some(event) = events.pop() {
|
|
state.apply_event(event);
|
|
match event {
|
|
Event::KeyDown(kc) => {
|
|
state.pressed_keys.insert(kc.to_evdev());
|
|
}
|
|
Event::KeyUp(kc) => {
|
|
state.pressed_keys.remove(&kc.to_evdev());
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PhysicalKeyboardState {
|
|
pub fn new(state: &Rc<RefCell<KbvmState>>) -> Self {
|
|
Self {
|
|
state: state.clone(),
|
|
inner: Default::default(),
|
|
events: Default::default(),
|
|
flushing: Cell::new(false),
|
|
}
|
|
}
|
|
|
|
fn flush(&self, time_usec: u64, seat: &Rc<WlSeatGlobal>) {
|
|
if self.flushing.replace(true) {
|
|
return;
|
|
}
|
|
seat.key_events(time_usec, &self.events, &self.state);
|
|
self.flushing.set(false);
|
|
}
|
|
|
|
pub fn update(&self, time_usec: u64, seat: &Rc<WlSeatGlobal>, key: u32, key_state: KeyState) {
|
|
{
|
|
let inner = &mut *self.inner.borrow_mut();
|
|
match key_state {
|
|
KeyState::Released => {
|
|
if !inner.pressed_keys.remove(&key) {
|
|
return;
|
|
}
|
|
}
|
|
KeyState::Pressed => {
|
|
if !inner.pressed_keys.insert(key) {
|
|
return;
|
|
}
|
|
}
|
|
KeyState::Repeated => {
|
|
return;
|
|
}
|
|
}
|
|
let state = &mut *self.state.borrow_mut();
|
|
state.map.state_machine.handle_key(
|
|
&mut state.state,
|
|
&mut inner.event_stash,
|
|
Keycode::from_evdev(key),
|
|
match key_state {
|
|
KeyState::Released => Direction::Up,
|
|
KeyState::Pressed => Direction::Down,
|
|
KeyState::Repeated => unreachable!(),
|
|
},
|
|
);
|
|
self.events.append(&mut inner.event_stash);
|
|
}
|
|
self.flush(time_usec, seat);
|
|
}
|
|
|
|
pub fn destroy(&self, time_usec: u64, seat: &Rc<WlSeatGlobal>) {
|
|
{
|
|
let inner = &mut *self.inner.borrow_mut();
|
|
let state = &mut *self.state.borrow_mut();
|
|
let sm = &state.map.state_machine;
|
|
while let Some(key) = inner.pressed_keys.pop() {
|
|
sm.handle_key(
|
|
&mut state.state,
|
|
&mut inner.event_stash,
|
|
Keycode::from_evdev(key),
|
|
Direction::Up,
|
|
);
|
|
}
|
|
self.events.append(&mut inner.event_stash);
|
|
}
|
|
self.flush(time_usec, seat);
|
|
}
|
|
}
|