keyboard: move keymap state into workspace crate
This commit is contained in:
parent
151dc313ba
commit
7d9cd198ba
13 changed files with 832 additions and 342 deletions
|
|
@ -44,7 +44,7 @@ pub mod transaction;
|
|||
|
||||
pub use jay_input_types::{
|
||||
AXIS_120, AxisSource, ButtonState, InputDeviceAccelProfile, InputDeviceClickMethod, KeyState,
|
||||
LED_CAPS_LOCK, LED_COMPOSE, LED_KANA, LED_NUM_LOCK, LED_SCROLL_LOCK, Leds, ScrollAxis,
|
||||
Leds, ScrollAxis,
|
||||
};
|
||||
|
||||
linear_ids!(ConnectorIds, ConnectorId);
|
||||
|
|
|
|||
216
src/kbvm.rs
216
src/kbvm.rs
|
|
@ -1,77 +1,21 @@
|
|||
pub use jay_keyboard::{KbvmContext, KbvmError, KbvmMap, KbvmMapId, KbvmState};
|
||||
|
||||
use {
|
||||
crate::{
|
||||
backend::KeyState,
|
||||
ifs::wl_seat::WlSeatGlobal,
|
||||
keyboard::{DynKeyboardState, KeyboardState, KeyboardStateId, KeymapFd},
|
||||
utils::{
|
||||
oserror::{OsError, OsErrorExt},
|
||||
syncqueue::SyncQueue,
|
||||
vecset::VecSet,
|
||||
},
|
||||
utils::{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,
|
||||
},
|
||||
state_machine::{Direction, Event},
|
||||
},
|
||||
std::{
|
||||
cell::{Cell, Ref, RefCell},
|
||||
io::Write,
|
||||
cell::{Cell, RefCell},
|
||||
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>,
|
||||
|
|
@ -85,156 +29,6 @@ struct PkInner {
|
|||
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 {
|
||||
|
|
|
|||
118
src/keyboard.rs
118
src/keyboard.rs
|
|
@ -1,116 +1,4 @@
|
|||
use {
|
||||
crate::{
|
||||
backend::{LED_CAPS_LOCK, LED_COMPOSE, LED_KANA, LED_NUM_LOCK, LED_SCROLL_LOCK, Leds},
|
||||
kbvm::KbvmMap,
|
||||
utils::{
|
||||
event_listener::EventSource,
|
||||
oserror::{OsError, OsErrorExt, OsErrorExt2},
|
||||
vecset::VecSet,
|
||||
},
|
||||
},
|
||||
kbvm::{Components, state_machine::Event},
|
||||
std::{
|
||||
cell::{Ref, RefCell},
|
||||
rc::Rc,
|
||||
},
|
||||
thiserror::Error,
|
||||
uapi::{OwnedFd, c},
|
||||
pub use jay_keyboard::{
|
||||
DynKeyboardState, KeyboardError, KeyboardState, KeyboardStateId, KeyboardStateIds, KeymapFd,
|
||||
LedsListener,
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum KeyboardError {
|
||||
#[error("Could not create a keymap memfd")]
|
||||
KeymapMemfd(#[source] OsError),
|
||||
#[error("Could not copy the keymap")]
|
||||
KeymapCopy(#[source] OsError),
|
||||
}
|
||||
|
||||
linear_ids!(KeyboardStateIds, KeyboardStateId, u64);
|
||||
|
||||
pub struct KeyboardState {
|
||||
pub id: KeyboardStateId,
|
||||
pub map: Rc<KbvmMap>,
|
||||
pub pressed_keys: VecSet<u32>,
|
||||
pub mods: Components,
|
||||
pub leds: Leds,
|
||||
pub leds_changed: EventSource<dyn LedsListener>,
|
||||
}
|
||||
|
||||
pub trait LedsListener {
|
||||
fn leds(&self, leds: Leds);
|
||||
}
|
||||
|
||||
pub trait DynKeyboardState {
|
||||
fn borrow(&self) -> Ref<'_, KeyboardState>;
|
||||
}
|
||||
|
||||
impl DynKeyboardState for RefCell<KeyboardState> {
|
||||
fn borrow(&self) -> Ref<'_, KeyboardState> {
|
||||
self.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardState {
|
||||
pub fn apply_event(&mut self, event: Event) -> bool {
|
||||
let changed = self.mods.apply_event(event);
|
||||
if changed && self.map.has_indicators {
|
||||
self.update_leds();
|
||||
}
|
||||
changed
|
||||
}
|
||||
|
||||
pub fn update_leds(&mut self) {
|
||||
if !self.map.has_indicators {
|
||||
return;
|
||||
}
|
||||
let mut new = Leds::none();
|
||||
macro_rules! map_led {
|
||||
($field:ident, $led:ident) => {
|
||||
if let Some(m) = &self.map.$field
|
||||
&& m.matches(&self.mods)
|
||||
{
|
||||
new |= $led;
|
||||
}
|
||||
};
|
||||
}
|
||||
map_led!(num_lock, LED_NUM_LOCK);
|
||||
map_led!(caps_lock, LED_CAPS_LOCK);
|
||||
map_led!(scroll_lock, LED_SCROLL_LOCK);
|
||||
map_led!(compose, LED_COMPOSE);
|
||||
map_led!(kana, LED_KANA);
|
||||
if new != self.leds {
|
||||
self.leds = new;
|
||||
for listener in self.leds_changed.iter() {
|
||||
listener.leds(new);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct KeymapFd {
|
||||
pub map: Rc<OwnedFd>,
|
||||
pub len: usize,
|
||||
}
|
||||
|
||||
impl KeymapFd {
|
||||
pub fn create_unprotected_fd(&self) -> Result<Self, KeyboardError> {
|
||||
let fd = uapi::memfd_create("shared-keymap", c::MFD_CLOEXEC)
|
||||
.map_os_err(KeyboardError::KeymapMemfd)?;
|
||||
let target = self.len as c::off_t;
|
||||
let mut pos = 0;
|
||||
while pos < target {
|
||||
let rem = target - pos;
|
||||
let res = uapi::sendfile(fd.raw(), self.map.raw(), Some(&mut pos), rem as usize)
|
||||
.to_os_error();
|
||||
match res {
|
||||
Ok(_) | Err(OsError(c::EINTR)) => {}
|
||||
Err(e) => return Err(KeyboardError::KeymapCopy(e)),
|
||||
}
|
||||
}
|
||||
Ok(Self {
|
||||
map: Rc::new(fd),
|
||||
len: self.len,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
14
src/utils.rs
14
src/utils.rs
|
|
@ -72,7 +72,19 @@ pub mod buffd;
|
|||
pub mod bufio;
|
||||
pub mod clone3;
|
||||
pub mod double_click_state;
|
||||
pub mod event_listener;
|
||||
pub mod event_listener {
|
||||
pub use jay_utils::event_listener::*;
|
||||
|
||||
use {crate::state::State, std::rc::Rc};
|
||||
|
||||
pub async fn handle_lazy_event_sources(state: Rc<State>) {
|
||||
handle_lazy_event_sources_of(&state.lazy_event_sources).await;
|
||||
}
|
||||
|
||||
pub async fn handle_post_layout_event_sources(state: Rc<State>) {
|
||||
handle_lazy_event_sources_of(&state.post_layout_event_sources).await;
|
||||
}
|
||||
}
|
||||
pub mod linkedlist;
|
||||
pub mod line_logger;
|
||||
pub mod object_drop_queue;
|
||||
|
|
|
|||
|
|
@ -1,164 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
state::State,
|
||||
utils::{
|
||||
linkedlist::{LinkedList, LinkedListIter, LinkedNode},
|
||||
queue::AsyncQueue,
|
||||
},
|
||||
},
|
||||
std::{
|
||||
cell::Cell,
|
||||
ops::Deref,
|
||||
rc::{Rc, Weak},
|
||||
},
|
||||
};
|
||||
|
||||
pub async fn handle_lazy_event_sources(state: Rc<State>) {
|
||||
handle_lazy_event_sources_of(&state.lazy_event_sources).await;
|
||||
}
|
||||
|
||||
pub async fn handle_post_layout_event_sources(state: Rc<State>) {
|
||||
handle_lazy_event_sources_of(&state.post_layout_event_sources).await;
|
||||
}
|
||||
|
||||
async fn handle_lazy_event_sources_of(sources: &LazyEventSources) {
|
||||
loop {
|
||||
let source = sources.queue.pop().await;
|
||||
source.queued.set(false);
|
||||
for listener in source.listeners.iter() {
|
||||
listener.triggered();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventSource<T: ?Sized> {
|
||||
listeners: LinkedList<Weak<T>>,
|
||||
on_attach: Cell<Option<Box<dyn FnOnce()>>>,
|
||||
}
|
||||
|
||||
pub struct EventListener<T: ?Sized> {
|
||||
link: LinkedNode<Weak<T>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LazyEventSources {
|
||||
queue: AsyncQueue<Rc<LazyEventSource>>,
|
||||
}
|
||||
|
||||
pub trait LazyEventSourceListener {
|
||||
fn triggered(self: Rc<Self>);
|
||||
}
|
||||
|
||||
pub struct LazyEventSource {
|
||||
sources: Rc<LazyEventSources>,
|
||||
queued: Cell<bool>,
|
||||
listeners: EventSource<dyn LazyEventSourceListener>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Default for EventSource<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
listeners: Default::default(),
|
||||
on_attach: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> EventSource<T> {
|
||||
pub fn clear(&self) {
|
||||
self.on_attach.take();
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> EventSourceIter<T> {
|
||||
EventSourceIter {
|
||||
iter: self.listeners.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_listeners(&self) -> bool {
|
||||
self.listeners.is_not_empty()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.listeners.is_empty()
|
||||
}
|
||||
|
||||
pub fn on_attach(&self, f: Box<dyn FnOnce()>) {
|
||||
self.on_attach.set(Some(f));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventSourceIter<T: ?Sized> {
|
||||
iter: LinkedListIter<Weak<T>>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Iterator for EventSourceIter<T> {
|
||||
type Item = Rc<T>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
for weak in self.iter.by_ref() {
|
||||
if let Some(t) = weak.upgrade() {
|
||||
return Some(t);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> EventListener<T> {
|
||||
pub fn new(t: Weak<T>) -> Self {
|
||||
Self {
|
||||
link: LinkedNode::detached(t),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn attach(&self, source: &EventSource<T>) {
|
||||
source.listeners.add_last_existing(&self.link);
|
||||
if let Some(on_attach) = source.on_attach.take() {
|
||||
on_attach();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detach(&self) {
|
||||
self.link.detach();
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Option<Rc<T>> {
|
||||
self.link.upgrade()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for LazyEventSource {
|
||||
type Target = EventSource<dyn LazyEventSourceListener>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.listeners
|
||||
}
|
||||
}
|
||||
|
||||
impl LazyEventSource {
|
||||
pub fn trigger(self: &Rc<Self>) {
|
||||
if self.listeners.is_empty() {
|
||||
return;
|
||||
}
|
||||
if self.queued.replace(true) {
|
||||
return;
|
||||
}
|
||||
self.sources.queue.push(self.clone());
|
||||
}
|
||||
}
|
||||
|
||||
impl LazyEventSources {
|
||||
#[allow(dead_code)]
|
||||
pub fn create_source(self: &Rc<Self>) -> Rc<LazyEventSource> {
|
||||
Rc::new(LazyEventSource {
|
||||
sources: self.clone(),
|
||||
queued: Default::default(),
|
||||
listeners: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.queue.clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -340,6 +340,7 @@ impl<T> LinkedNode<T> {
|
|||
LinkedNode { data: node.into() }
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn detached(t: T) -> Self {
|
||||
Self::new(Some(t))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue