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
13
Cargo.lock
generated
13
Cargo.lock
generated
|
|
@ -740,6 +740,7 @@ dependencies = [
|
||||||
"jay-gfx-types",
|
"jay-gfx-types",
|
||||||
"jay-input-types",
|
"jay-input-types",
|
||||||
"jay-io-uring",
|
"jay-io-uring",
|
||||||
|
"jay-keyboard",
|
||||||
"jay-layout-animation",
|
"jay-layout-animation",
|
||||||
"jay-libinput",
|
"jay-libinput",
|
||||||
"jay-logger",
|
"jay-logger",
|
||||||
|
|
@ -930,6 +931,18 @@ dependencies = [
|
||||||
"uapi",
|
"uapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-keyboard"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"blake3",
|
||||||
|
"jay-input-types",
|
||||||
|
"jay-utils",
|
||||||
|
"kbvm",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jay-layout-animation"
|
name = "jay-layout-animation"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ members = [
|
||||||
"logger",
|
"logger",
|
||||||
"video-types",
|
"video-types",
|
||||||
"input-types",
|
"input-types",
|
||||||
|
"keyboard",
|
||||||
"gfx-types",
|
"gfx-types",
|
||||||
"theme",
|
"theme",
|
||||||
"clientmem",
|
"clientmem",
|
||||||
|
|
@ -99,6 +100,7 @@ jay-bugs = { version = "0.1.0", path = "bugs" }
|
||||||
jay-logger = { version = "0.1.0", path = "logger" }
|
jay-logger = { version = "0.1.0", path = "logger" }
|
||||||
jay-video-types = { version = "0.1.0", path = "video-types" }
|
jay-video-types = { version = "0.1.0", path = "video-types" }
|
||||||
jay-input-types = { version = "0.1.0", path = "input-types" }
|
jay-input-types = { version = "0.1.0", path = "input-types" }
|
||||||
|
jay-keyboard = { version = "0.1.0", path = "keyboard" }
|
||||||
jay-gfx-types = { version = "0.1.0", path = "gfx-types" }
|
jay-gfx-types = { version = "0.1.0", path = "gfx-types" }
|
||||||
jay-theme = { version = "0.1.0", path = "theme" }
|
jay-theme = { version = "0.1.0", path = "theme" }
|
||||||
jay-clientmem = { version = "0.1.0", path = "clientmem" }
|
jay-clientmem = { version = "0.1.0", path = "clientmem" }
|
||||||
|
|
|
||||||
16
keyboard/Cargo.toml
Normal file
16
keyboard/Cargo.toml
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-keyboard"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
description = "Keyboard state and keymap helpers for the Jay compositor"
|
||||||
|
repository = "https://github.com/mahkoh/jay"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
jay-input-types = { version = "0.1.0", path = "../input-types" }
|
||||||
|
jay-utils = { version = "0.1.0", path = "../utils" }
|
||||||
|
|
||||||
|
blake3 = "1.8.2"
|
||||||
|
kbvm = { version = "0.1.6", features = ["compose"] }
|
||||||
|
thiserror = "2.0.11"
|
||||||
|
uapi = "0.2.13"
|
||||||
357
keyboard/src/lib.rs
Normal file
357
keyboard/src/lib.rs
Normal file
|
|
@ -0,0 +1,357 @@
|
||||||
|
use {
|
||||||
|
jay_input_types::{
|
||||||
|
LED_CAPS_LOCK, LED_COMPOSE, LED_KANA, LED_NUM_LOCK, LED_SCROLL_LOCK, Leds,
|
||||||
|
},
|
||||||
|
jay_utils::{
|
||||||
|
event_listener::EventSource,
|
||||||
|
numcell::NumCell,
|
||||||
|
oserror::{OsError, OsErrorExt, OsErrorExt2},
|
||||||
|
syncqueue::SyncQueue,
|
||||||
|
vecset::VecSet,
|
||||||
|
},
|
||||||
|
kbvm::{
|
||||||
|
Components,
|
||||||
|
lookup::LookupTable,
|
||||||
|
state_machine::{self, Event, StateMachine},
|
||||||
|
xkb::{
|
||||||
|
self, Keymap,
|
||||||
|
diagnostic::{Diagnostic, WriteToLog},
|
||||||
|
keymap::{Indicator, IndicatorMatcher},
|
||||||
|
rmlvo::Group,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
std::{
|
||||||
|
cell::{Ref, RefCell},
|
||||||
|
io::Write,
|
||||||
|
rc::Rc,
|
||||||
|
},
|
||||||
|
thiserror::Error,
|
||||||
|
uapi::{OwnedFd, c},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[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),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct KeyboardStateIds {
|
||||||
|
next: NumCell<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for KeyboardStateIds {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
next: NumCell::new(1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyboardStateIds {
|
||||||
|
pub fn next(&self) -> KeyboardStateId {
|
||||||
|
KeyboardStateId(self.next.fetch_add(1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
|
pub struct KeyboardStateId(u64);
|
||||||
|
|
||||||
|
impl KeyboardStateId {
|
||||||
|
pub fn raw(&self) -> u64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_raw(id: u64) -> Self {
|
||||||
|
Self(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for KeyboardStateId {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
std::fmt::Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 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 DynKeyboardState for RefCell<KbvmState> {
|
||||||
|
fn borrow(&self) -> Ref<'_, KeyboardState> {
|
||||||
|
Ref::map(self.borrow(), |v| &v.kb_state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -44,7 +44,7 @@ pub mod transaction;
|
||||||
|
|
||||||
pub use jay_input_types::{
|
pub use jay_input_types::{
|
||||||
AXIS_120, AxisSource, ButtonState, InputDeviceAccelProfile, InputDeviceClickMethod, KeyState,
|
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);
|
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 {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
backend::KeyState,
|
backend::KeyState,
|
||||||
ifs::wl_seat::WlSeatGlobal,
|
ifs::wl_seat::WlSeatGlobal,
|
||||||
keyboard::{DynKeyboardState, KeyboardState, KeyboardStateId, KeymapFd},
|
utils::{syncqueue::SyncQueue, vecset::VecSet},
|
||||||
utils::{
|
|
||||||
oserror::{OsError, OsErrorExt},
|
|
||||||
syncqueue::SyncQueue,
|
|
||||||
vecset::VecSet,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
kbvm::{
|
kbvm::{
|
||||||
Keycode,
|
Keycode,
|
||||||
lookup::LookupTable,
|
state_machine::{Direction, Event},
|
||||||
state_machine::{self, Direction, Event, StateMachine},
|
|
||||||
xkb::{
|
|
||||||
self, Keymap,
|
|
||||||
diagnostic::{Diagnostic, WriteToLog},
|
|
||||||
keymap::{Indicator, IndicatorMatcher},
|
|
||||||
rmlvo::Group,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, Ref, RefCell},
|
cell::{Cell, RefCell},
|
||||||
io::Write,
|
|
||||||
rc::Rc,
|
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 {
|
pub struct PhysicalKeyboardState {
|
||||||
state: Rc<RefCell<KbvmState>>,
|
state: Rc<RefCell<KbvmState>>,
|
||||||
inner: RefCell<PkInner>,
|
inner: RefCell<PkInner>,
|
||||||
|
|
@ -85,156 +29,6 @@ struct PkInner {
|
||||||
event_stash: Vec<Event>,
|
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 {
|
impl PhysicalKeyboardState {
|
||||||
pub fn new(state: &Rc<RefCell<KbvmState>>) -> Self {
|
pub fn new(state: &Rc<RefCell<KbvmState>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
|
||||||
118
src/keyboard.rs
118
src/keyboard.rs
|
|
@ -1,116 +1,4 @@
|
||||||
use {
|
pub use jay_keyboard::{
|
||||||
crate::{
|
DynKeyboardState, KeyboardError, KeyboardState, KeyboardStateId, KeyboardStateIds, KeymapFd,
|
||||||
backend::{LED_CAPS_LOCK, LED_COMPOSE, LED_KANA, LED_NUM_LOCK, LED_SCROLL_LOCK, Leds},
|
LedsListener,
|
||||||
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},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[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 bufio;
|
||||||
pub mod clone3;
|
pub mod clone3;
|
||||||
pub mod double_click_state;
|
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 linkedlist;
|
||||||
pub mod line_logger;
|
pub mod line_logger;
|
||||||
pub mod object_drop_queue;
|
pub mod object_drop_queue;
|
||||||
|
|
|
||||||
|
|
@ -340,6 +340,7 @@ impl<T> LinkedNode<T> {
|
||||||
LinkedNode { data: node.into() }
|
LinkedNode { data: node.into() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn detached(t: T) -> Self {
|
pub fn detached(t: T) -> Self {
|
||||||
Self::new(Some(t))
|
Self::new(Some(t))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,3 +94,5 @@ unsafe impl<A: UnsafeCellCloneSafe, B: UnsafeCellCloneSafe> UnsafeCellCloneSafe
|
||||||
unsafe impl UnsafeCellCloneSafe for Modifiers {}
|
unsafe impl UnsafeCellCloneSafe for Modifiers {}
|
||||||
|
|
||||||
unsafe impl UnsafeCellCloneSafe for Window {}
|
unsafe impl UnsafeCellCloneSafe for Window {}
|
||||||
|
|
||||||
|
unsafe impl<T> UnsafeCellCloneSafe for crate::linkedlist::NodeRef<T> {}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
state::State,
|
linkedlist::{LinkedList, LinkedListIter, LinkedNode},
|
||||||
utils::{
|
queue::AsyncQueue,
|
||||||
linkedlist::{LinkedList, LinkedListIter, LinkedNode},
|
|
||||||
queue::AsyncQueue,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
|
|
@ -13,15 +10,7 @@ use {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn handle_lazy_event_sources(state: Rc<State>) {
|
pub async fn handle_lazy_event_sources_of(sources: &LazyEventSources) {
|
||||||
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 {
|
loop {
|
||||||
let source = sources.queue.pop().await;
|
let source = sources.queue.pop().await;
|
||||||
source.queued.set(false);
|
source.queued.set(false);
|
||||||
|
|
@ -12,10 +12,12 @@ pub mod compat;
|
||||||
pub mod copyhashmap;
|
pub mod copyhashmap;
|
||||||
pub mod double_buffered;
|
pub mod double_buffered;
|
||||||
pub mod errorfmt;
|
pub mod errorfmt;
|
||||||
|
pub mod event_listener;
|
||||||
pub mod fdcloser;
|
pub mod fdcloser;
|
||||||
pub mod free_list;
|
pub mod free_list;
|
||||||
pub mod geometric_decay;
|
pub mod geometric_decay;
|
||||||
pub mod hash_map_ext;
|
pub mod hash_map_ext;
|
||||||
|
pub mod linkedlist;
|
||||||
pub mod log_on_drop;
|
pub mod log_on_drop;
|
||||||
pub mod mmap;
|
pub mod mmap;
|
||||||
pub mod nice;
|
pub mod nice;
|
||||||
|
|
|
||||||
414
utils/src/linkedlist.rs
Normal file
414
utils/src/linkedlist.rs
Normal file
|
|
@ -0,0 +1,414 @@
|
||||||
|
use {
|
||||||
|
crate::numcell::NumCell,
|
||||||
|
std::{
|
||||||
|
cell::Cell,
|
||||||
|
fmt::{Debug, Formatter},
|
||||||
|
mem,
|
||||||
|
ops::Deref,
|
||||||
|
ptr::NonNull,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const LINKED_NODE_REF_COUNT: usize = !(!0 >> 1);
|
||||||
|
|
||||||
|
pub struct LinkedList<T> {
|
||||||
|
root: LinkedNode<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Debug> Debug for LinkedList<T> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_list().entries(self.iter()).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for LinkedList<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> LinkedList<T> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
root: LinkedNode::new(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append_all(&self, other: &LinkedList<T>) {
|
||||||
|
if other.is_empty() || self.root.data == other.root.data {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
let o_root = other.root.data;
|
||||||
|
let o_first = o_root.as_ref().next.get();
|
||||||
|
let o_last = o_root.as_ref().prev.get();
|
||||||
|
let s_first = self.root.data;
|
||||||
|
let s_last = s_first.as_ref().prev.get();
|
||||||
|
o_first.as_ref().prev.set(s_last);
|
||||||
|
s_last.as_ref().next.set(o_first);
|
||||||
|
o_last.as_ref().next.set(s_first);
|
||||||
|
s_first.as_ref().prev.set(o_last);
|
||||||
|
o_root.as_ref().next.set(o_root);
|
||||||
|
o_root.as_ref().prev.set(o_root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn endpoint(&self, ep: NonNull<NodeData<T>>) -> Option<NodeRef<T>> {
|
||||||
|
unsafe {
|
||||||
|
if ep != self.root.data {
|
||||||
|
ep.as_ref().rc.fetch_add(1);
|
||||||
|
Some(NodeRef { data: ep })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.last().is_none()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_not_empty(&self) -> bool {
|
||||||
|
!self.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn last(&self) -> Option<NodeRef<T>> {
|
||||||
|
unsafe { self.endpoint(self.root.data.as_ref().prev.get()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn first(&self) -> Option<NodeRef<T>> {
|
||||||
|
unsafe { self.endpoint(self.root.data.as_ref().next.get()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_last(&self, t: T) -> LinkedNode<T> {
|
||||||
|
self.root.prepend(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_first(&self, t: T) -> LinkedNode<T> {
|
||||||
|
self.root.append(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_last_existing(&self, t: &NodeRef<T>) {
|
||||||
|
self.root.prepend_existing(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_first_existing(&self, t: &NodeRef<T>) {
|
||||||
|
self.root.append_existing(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rotate_last(&self, t: &NodeRef<T>) {
|
||||||
|
unsafe {
|
||||||
|
let root = self.root.data.as_ref();
|
||||||
|
root.prev.get().as_ref().next.set(root.next.get());
|
||||||
|
root.next.get().as_ref().prev.set(root.prev.get());
|
||||||
|
root.prev.set(t.data);
|
||||||
|
root.next.set(t.data.as_ref().next.get());
|
||||||
|
t.data.as_ref().next.get().as_ref().prev.set(self.root.data);
|
||||||
|
t.data.as_ref().next.set(self.root.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> LinkedListIter<T> {
|
||||||
|
unsafe {
|
||||||
|
let root = self.root.data.as_ref();
|
||||||
|
root.rc.fetch_add(1);
|
||||||
|
root.next.get().as_ref().rc.fetch_add(1);
|
||||||
|
LinkedListIter {
|
||||||
|
root: self.root.data,
|
||||||
|
next: root.next.get(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rev_iter(&self) -> RevLinkedListIter<T> {
|
||||||
|
unsafe {
|
||||||
|
let root = self.root.data.as_ref();
|
||||||
|
root.rc.fetch_add(1);
|
||||||
|
root.prev.get().as_ref().rc.fetch_add(1);
|
||||||
|
RevLinkedListIter {
|
||||||
|
root: self.root.data,
|
||||||
|
next: root.prev.get(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LinkedListIter<T> {
|
||||||
|
root: NonNull<NodeData<T>>,
|
||||||
|
next: NonNull<NodeData<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Iterator for LinkedListIter<T> {
|
||||||
|
type Item = NodeRef<T>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.root == self.next {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
let old_next = self.next;
|
||||||
|
self.next = old_next.as_ref().next.get();
|
||||||
|
self.next.as_ref().rc.fetch_add(1);
|
||||||
|
Some(NodeRef { data: old_next })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for LinkedListIter<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
dec_ref_count(self.root, 1);
|
||||||
|
dec_ref_count(self.next, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RevLinkedListIter<T> {
|
||||||
|
root: NonNull<NodeData<T>>,
|
||||||
|
next: NonNull<NodeData<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Iterator for RevLinkedListIter<T> {
|
||||||
|
type Item = NodeRef<T>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.root == self.next {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
let old_next = self.next;
|
||||||
|
self.next = old_next.as_ref().prev.get();
|
||||||
|
self.next.as_ref().rc.fetch_add(1);
|
||||||
|
Some(NodeRef { data: old_next })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for RevLinkedListIter<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
dec_ref_count(self.root, 1);
|
||||||
|
dec_ref_count(self.next, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[must_use]
|
||||||
|
pub struct LinkedNode<T> {
|
||||||
|
data: NonNull<NodeData<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Debug> Debug for LinkedNode<T> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
unsafe { self.data.as_ref().data.as_ref().unwrap_unchecked().fmt(f) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for LinkedNode<T> {
|
||||||
|
type Target = NodeRef<T>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
unsafe { mem::transmute(self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct NodeRef<T> {
|
||||||
|
data: NonNull<NodeData<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Debug> Debug for NodeRef<T> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
unsafe { self.data.as_ref().data.as_ref().unwrap_unchecked().fmt(f) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for NodeRef<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
unsafe { self.data.as_ref().data.as_ref().unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for NodeRef<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
dec_ref_count(self.data, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Clone for NodeRef<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
unsafe {
|
||||||
|
self.data.as_ref().rc.fetch_add(1);
|
||||||
|
Self { data: self.data }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> NodeRef<T> {
|
||||||
|
pub fn prepend(&self, t: T) -> LinkedNode<T> {
|
||||||
|
unsafe { prepend(self.data, t) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append(&self, t: T) -> LinkedNode<T> {
|
||||||
|
unsafe { append(self.data, t) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prepend_existing(&self, t: &NodeRef<T>) {
|
||||||
|
unsafe { prepend_existing(self.data, t) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append_existing(&self, t: &NodeRef<T>) {
|
||||||
|
unsafe { append_existing(self.data, t) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peer<F>(&self, peer: F) -> Option<NodeRef<T>>
|
||||||
|
where
|
||||||
|
F: FnOnce(&NodeData<T>) -> &Cell<NonNull<NodeData<T>>>,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
let data = self.data.as_ref();
|
||||||
|
let other = peer(data).get();
|
||||||
|
if other.as_ref().data.is_some() {
|
||||||
|
other.as_ref().rc.fetch_add(1);
|
||||||
|
Some(NodeRef { data: other })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prev(&self) -> Option<NodeRef<T>> {
|
||||||
|
self.peer(|d| &d.prev)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(&self) -> Option<NodeRef<T>> {
|
||||||
|
self.peer(|d| &d.next)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn detach(&self) {
|
||||||
|
unsafe {
|
||||||
|
let data = self.data.as_ref();
|
||||||
|
data.prev.get().as_ref().next.set(data.next.get());
|
||||||
|
data.next.get().as_ref().prev.set(data.prev.get());
|
||||||
|
data.prev.set(self.data);
|
||||||
|
data.next.set(self.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NodeData<T> {
|
||||||
|
rc: NumCell<usize>,
|
||||||
|
prev: Cell<NonNull<NodeData<T>>>,
|
||||||
|
next: Cell<NonNull<NodeData<T>>>,
|
||||||
|
data: Option<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn dec_ref_count<T>(slf: NonNull<NodeData<T>>, n: usize) {
|
||||||
|
unsafe {
|
||||||
|
if slf.as_ref().rc.fetch_sub(n) == n {
|
||||||
|
drop(Box::from_raw(slf.as_ptr()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for LinkedNode<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
self.detach();
|
||||||
|
dec_ref_count(self.data, LINKED_NODE_REF_COUNT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> LinkedNode<T> {
|
||||||
|
fn new(t: Option<T>) -> Self {
|
||||||
|
let node = Box::leak(Box::new(NodeData {
|
||||||
|
rc: NumCell::new(LINKED_NODE_REF_COUNT),
|
||||||
|
prev: Cell::new(NonNull::dangling()),
|
||||||
|
next: Cell::new(NonNull::dangling()),
|
||||||
|
data: t,
|
||||||
|
}));
|
||||||
|
let ptr = NonNull::from(&mut *node);
|
||||||
|
node.prev.set(ptr);
|
||||||
|
node.next.set(ptr);
|
||||||
|
LinkedNode { data: node.into() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn detached(t: T) -> Self {
|
||||||
|
Self::new(Some(t))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_ref(&self) -> NodeRef<T> {
|
||||||
|
unsafe {
|
||||||
|
self.data.as_ref().rc.fetch_add(1);
|
||||||
|
NodeRef { data: self.data }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn prepend_existing<T>(data: NonNull<NodeData<T>>, t: &NodeRef<T>) {
|
||||||
|
unsafe {
|
||||||
|
let dref = data.as_ref();
|
||||||
|
let tref = t.data.as_ref();
|
||||||
|
if tref.rc.get() < LINKED_NODE_REF_COUNT {
|
||||||
|
log::error!("Trying to prepend a node whose linked node has already been dropped");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
t.detach();
|
||||||
|
tref.prev.set(dref.prev.get());
|
||||||
|
tref.next.set(data);
|
||||||
|
dref.prev.get().as_ref().next.set(t.data);
|
||||||
|
dref.prev.set(t.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn prepend<T>(data: NonNull<NodeData<T>>, t: T) -> LinkedNode<T> {
|
||||||
|
unsafe {
|
||||||
|
let dref = data.as_ref();
|
||||||
|
let node = NonNull::new_unchecked(Box::into_raw(Box::new(NodeData {
|
||||||
|
rc: NumCell::new(LINKED_NODE_REF_COUNT),
|
||||||
|
prev: Cell::new(dref.prev.get()),
|
||||||
|
next: Cell::new(data),
|
||||||
|
data: Some(t),
|
||||||
|
})));
|
||||||
|
dref.prev.get().as_ref().next.set(node);
|
||||||
|
dref.prev.set(node);
|
||||||
|
LinkedNode { data: node }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn append_existing<T>(data: NonNull<NodeData<T>>, t: &NodeRef<T>) {
|
||||||
|
unsafe {
|
||||||
|
let dref = data.as_ref();
|
||||||
|
let tref = t.data.as_ref();
|
||||||
|
if tref.rc.get() < LINKED_NODE_REF_COUNT {
|
||||||
|
log::error!("Trying to append a node whose linked node has already been dropped");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
t.detach();
|
||||||
|
tref.prev.set(data);
|
||||||
|
tref.next.set(dref.next.get());
|
||||||
|
dref.next.get().as_ref().prev.set(t.data);
|
||||||
|
dref.next.set(t.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn append<T>(data: NonNull<NodeData<T>>, t: T) -> LinkedNode<T> {
|
||||||
|
unsafe {
|
||||||
|
let dref = data.as_ref();
|
||||||
|
let node = NonNull::new_unchecked(Box::into_raw(Box::new(NodeData {
|
||||||
|
rc: NumCell::new(LINKED_NODE_REF_COUNT),
|
||||||
|
prev: Cell::new(data),
|
||||||
|
next: Cell::new(dref.next.get()),
|
||||||
|
data: Some(t),
|
||||||
|
})));
|
||||||
|
dref.next.get().as_ref().prev.set(node);
|
||||||
|
dref.next.set(node);
|
||||||
|
LinkedNode { data: node }
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue