1
0
Fork 0
forked from wry/wry

Merge pull request #345 from mahkoh/jorth/kbvm

keyboard: replace xkbcommon by kbvm
This commit is contained in:
mahkoh 2025-01-27 10:32:50 +01:00 committed by GitHub
commit 4e23f4824e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 794 additions and 892 deletions

71
Cargo.lock generated
View file

@ -298,6 +298,12 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "debug-fn"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24efe21bd9a78102d1225f10f0a41d9d5b43f4df7ae8235f39a9c79e4d476c1e"
[[package]]
name = "deranged"
version = "0.3.11"
@ -369,6 +375,12 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "foldhash"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
[[package]]
name = "futures-core"
version = "0.3.31"
@ -466,6 +478,9 @@ name = "hashbrown"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
dependencies = [
"foldhash",
]
[[package]]
name = "heck"
@ -564,6 +579,7 @@ dependencies = [
"jay-algorithms",
"jay-config",
"jay-toml-config",
"kbvm",
"libloading",
"linearize",
"log",
@ -628,6 +644,40 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "kbvm"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "723f422224bcf2bcbf788f71bc022a269b5abf7f02c50b07f91cdc9dd67fa902"
dependencies = [
"arrayvec",
"bstr",
"cfg-if",
"debug-fn",
"hashbrown",
"indexmap",
"isnt",
"kbvm-proc",
"linearize",
"log",
"pkg-config",
"secure-execution",
"smallvec",
"thiserror",
"unicode-width",
]
[[package]]
name = "kbvm-proc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28067e7361c0069c3753795d131653f9ea5333aeb35a3855fb2de66447c48ac8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
@ -874,6 +924,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
[[package]]
name = "png"
version = "0.17.16"
@ -1069,6 +1125,15 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "secure-execution"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23ceccce47e43305aa02a0d9182ad09364357d073447435d7ae2e219ce750e55"
dependencies = [
"cfg-if",
]
[[package]]
name = "serde"
version = "1.0.217"
@ -1349,6 +1414,12 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
[[package]]
name = "unicode-width"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"

View file

@ -61,6 +61,7 @@ linearize = { version = "0.1.3", features = ["derive"] }
png = "0.17.13"
rustc-demangle = { version = "0.1.24", optional = true }
tracy-client-sys = { version = "0.24.1", features = ["ondemand", "manual-lifetime", "debuginfod"], optional = true }
kbvm = "0.1.2"
[build-dependencies]
repc = "0.1.1"

View file

@ -9,9 +9,6 @@ use {
#[path = "../src/macros.rs"]
mod macros;
#[path = "../src/xkbcommon/consts.rs"]
mod xkbcommon;
#[path = "../src/libinput/consts.rs"]
mod libinput;
@ -141,21 +138,5 @@ pub fn main() -> anyhow::Result<()> {
write_ty(&mut f, pango::CAIRO_OPERATORS, "cairo_operator_t")?;
write_ty(&mut f, pango::PANGO_ELLIPSIZE_MODES, "PangoEllipsizeMode_")?;
let mut f = open("xkbcommon_tys.rs")?;
write_ty(&mut f, xkbcommon::XKB_LOG_LEVEL, "xkb_log_level")?;
write_ty(&mut f, xkbcommon::XKB_CONTEXT_FLAGS, "xkb_context_flags")?;
write_ty(
&mut f,
xkbcommon::XKB_KEYMAP_COMPILE_FLAGS,
"xkb_keymap_compile_flags",
)?;
write_ty(&mut f, xkbcommon::XKB_KEYMAP_FORMAT, "xkb_keymap_format")?;
write_ty(
&mut f,
xkbcommon::XKB_STATE_COMPONENT,
"xkb_state_component",
)?;
write_ty(&mut f, xkbcommon::XKB_KEY_DIRECTION, "xkb_key_direction")?;
Ok(())
}

View file

@ -1,5 +1,7 @@
# Unreleased
- Needs jay-compositor release.
# 1.8.0
- Needs jay-config release.

View file

@ -1,5 +1,14 @@
# Unreleased
This release replaces xkbcommon by the kbvm crate.
This is a huge change in how input is handled. The intention is that this is completely
invisible to users.
Therefore this release contains only this change. You can downgrade to the previous
release to switch back to xkbcommon without any loss in functionality. In this case please
report what is broken.
# 1.8.0 (2025-01-27)
- Various bugfixes.

View file

@ -30,20 +30,3 @@ void jay_libinput_log_handler_bridge(
jay_libinput_log_handler(libinput, priority, line);
free(line);
}
void jay_xkbcommon_log_handler(
void *ctx,
int xkb_log_level,
const char *line
);
void jay_xkbcommon_log_handler_bridge(
void *ctx,
int xkb_log_level,
const char *format,
va_list args
) {
char *line = fmt(format, args);
jay_xkbcommon_log_handler(ctx, xkb_log_level, line);
free(line);
}

View file

@ -26,6 +26,7 @@ use {
workspace_manager::workspace_manager_done,
},
io_uring::{IoUring, IoUringError},
kbvm::KbvmContext,
leaks,
logger::Logger,
output_schedule::OutputSchedule,
@ -49,7 +50,6 @@ use {
version::VERSION,
video::drm::wait_for_sync_obj::WaitForSyncObj,
wheel::{Wheel, WheelError},
xkbcommon::XkbContext,
},
ahash::AHashSet,
forker::ForkerProxy,
@ -139,8 +139,10 @@ fn start_compositor2(
init_fd_limit();
leaks::init();
clientmem::init()?;
let xkb_ctx = XkbContext::new().unwrap();
let xkb_keymap = xkb_ctx.keymap_from_str(include_str!("keymap.xkb")).unwrap();
let kb_ctx = KbvmContext::default();
let kb_keymap = kb_ctx
.parse_keymap(include_str!("keymap.xkb").as_bytes())
.unwrap();
let engine = AsyncEngine::new();
let ring = IoUring::new(&engine, 32)?;
let _signal_future = sighand::install(&engine, &ring)?;
@ -151,10 +153,10 @@ fn start_compositor2(
scales.add(Scale::from_int(1));
let cpu_worker = Rc::new(CpuWorker::new(&ring, &engine)?);
let state = Rc::new(State {
xkb_ctx,
kb_ctx,
backend: CloneCell::new(Rc::new(DummyBackend)),
forker: Default::default(),
default_keymap: xkb_keymap,
default_keymap: kb_keymap,
eng: engine.clone(),
render_ctx: Default::default(),
drm_feedback: Default::default(),
@ -254,6 +256,7 @@ fn start_compositor2(
wait_for_sync_obj: Rc::new(WaitForSyncObj::new(&ring, &engine)),
explicit_sync_enabled: Cell::new(true),
keyboard_state_ids: Default::default(),
physical_keyboard_ids: Default::default(),
security_context_acceptors: Default::default(),
cursor_user_group_ids: Default::default(),
cursor_user_ids: Default::default(),

View file

@ -10,6 +10,7 @@ use {
format::config_formats,
ifs::wl_seat::{SeatId, WlSeatGlobal},
io_uring::TaskResultExt,
kbvm::{KbvmError, KbvmMap},
output_schedule::map_cursor_hz,
scale::Scale,
state::{ConnectorData, DeviceHandlerData, DrmDevData, OutputData, State},
@ -28,7 +29,6 @@ use {
stack::Stack,
timer::{TimerError, TimerFd},
},
xkbcommon::{XkbCommonError, XkbKeymap},
},
bincode::Options,
jay_config::{
@ -73,7 +73,7 @@ pub(super) struct ConfigProxyHandler {
pub handle_msg: unsafe extern "C" fn(data: *const u8, msg: *const u8, size: usize),
pub state: Rc<State>,
pub next_id: NumCell<u64>,
pub keymaps: CopyHashMap<Keymap, Rc<XkbKeymap>>,
pub keymaps: CopyHashMap<Keymap, Rc<KbvmMap>>,
pub bufs: Stack<Vec<u8>>,
pub workspace_ids: NumCell<u64>,
@ -180,7 +180,7 @@ impl ConfigProxyHandler {
}
fn handle_parse_keymap(&self, keymap: &str) -> Result<(), CphError> {
let (keymap, res) = match self.state.xkb_ctx.keymap_from_str(keymap) {
let (keymap, res) = match self.state.kb_ctx.parse_keymap(keymap.as_bytes()) {
Ok(keymap) => {
let id = Keymap(self.id());
self.keymaps.set(id, keymap);
@ -594,7 +594,7 @@ impl ConfigProxyHandler {
}
}
fn get_keymap(&self, keymap: Keymap) -> Result<Rc<XkbKeymap>, CphError> {
fn get_keymap(&self, keymap: Keymap) -> Result<Rc<KbvmMap>, CphError> {
match self.keymaps.get(&keymap) {
Some(k) => Ok(k),
None => Err(CphError::KeymapDoesNotExist(keymap)),
@ -2007,7 +2007,7 @@ enum CphError {
#[error("Repeat delay is negative")]
NegativeRepeatDelay,
#[error("Parsing failed")]
ParseKeymapError(#[from] XkbCommonError),
ParseKeymapError(#[from] KbvmError),
#[error("Device {0:?} does not exist")]
DeviceDoesNotExist(InputDevice),
#[error("Connector {0:?} does not exist")]

View file

@ -64,9 +64,9 @@ impl EiConnection {
if version == EiVersion(0) {
return;
}
let xkb_state_id = match self.context() {
EiContext::Sender => seat.seat_xkb_state().borrow().id,
EiContext::Receiver => seat.latest_xkb_state().borrow().id,
let kb_state_id = match self.context() {
EiContext::Sender => seat.seat_kb_state().borrow().id,
EiContext::Receiver => seat.latest_kb_state().borrow().id,
};
let seat = Rc::new(EiSeat {
id: self.client.new_id(),
@ -75,7 +75,8 @@ impl EiConnection {
version,
seat: seat.clone(),
capabilities: Cell::new(0),
kb_state_id: Cell::new(xkb_state_id),
kb_state_id: Cell::new(kb_state_id),
keyboard_id: self.client.state.physical_keyboard_ids.next(),
device: Default::default(),
pointer: Default::default(),
pointer_absolute: Default::default(),

View file

@ -189,7 +189,8 @@ impl EiDeviceRequestHandler for EiDevice {
seat.button_event(time, button, pressed);
}
while let Some((button, pressed)) = self.key_changes.pop() {
seat.key_event_with_seat_state(time, button, pressed);
let phy = seat.get_physical_keyboard(self.seat.keyboard_id, None);
phy.phy_state.update(time, seat, button, pressed);
}
if let Some((x, y)) = self.relative_motion.take() {
let x = Fixed::from_f32(x);

View file

@ -6,6 +6,7 @@ use {
ei_ifs::ei_device::{EiDevice, EiDeviceInterface},
ei_object::{EiObject, EiVersion},
},
keyboard::KeyboardState,
leaks::Tracker,
wire_ei::{
ei_keyboard::{
@ -13,7 +14,6 @@ use {
},
EiKeyboardId,
},
xkbcommon::KeyboardState,
},
std::rc::Rc,
thiserror::Error,
@ -36,8 +36,8 @@ impl EiKeyboard {
self.client.event(Keymap {
self_id: self.id,
keymap_type: KEYMAP_TYPE_XKB,
size: state.map_len as _,
keymap: state.map.clone(),
size: state.map.len as _,
keymap: state.map.map.clone(),
});
}
@ -45,18 +45,21 @@ impl EiKeyboard {
self.client.event(Modifiers {
self_id: self.id,
serial: self.client.serial(),
depressed: state.mods.mods_depressed,
locked: state.mods.mods_locked,
latched: state.mods.mods_latched,
group: state.mods.group,
depressed: state.mods.mods_pressed.0,
locked: state.mods.mods_locked.0,
latched: state.mods.mods_latched.0,
group: state.mods.group.0,
});
}
pub fn send_key(&self, key: u32, state: u32) {
pub fn send_key(&self, key: u32, state: KeyState) {
self.client.event(ServerKey {
self_id: self.id,
key,
state,
state: match state {
KeyState::Released => 0,
KeyState::Pressed => 1,
},
});
}
}

View file

@ -16,7 +16,8 @@ use {
EiContext,
},
fixed::Fixed,
ifs::wl_seat::{wl_pointer::PendingScroll, WlSeatGlobal},
ifs::wl_seat::{wl_pointer::PendingScroll, PhysicalKeyboardId, WlSeatGlobal},
keyboard::{DynKeyboardState, KeyboardState, KeyboardStateId},
leaks::Tracker,
tree::Node,
utils::{array, bitflags::BitflagsExt, clonecell::CloneCell},
@ -26,7 +27,6 @@ use {
},
EiSeatId,
},
xkbcommon::{DynKeyboardState, KeyboardState, KeyboardStateId},
},
std::{cell::Cell, rc::Rc},
thiserror::Error,
@ -49,6 +49,7 @@ pub struct EiSeat {
pub seat: Rc<WlSeatGlobal>,
pub capabilities: Cell<u64>,
pub kb_state_id: Cell<KeyboardStateId>,
pub keyboard_id: PhysicalKeyboardId,
pub device: CloneCell<Option<Rc<EiDevice>>>,
pub pointer: CloneCell<Option<Rc<EiPointer>>>,
@ -75,7 +76,11 @@ impl EiSeat {
}
}
pub fn handle_xkb_state_change(self: &Rc<Self>, old_id: KeyboardStateId, new: &KeyboardState) {
pub fn handle_keyboard_state_change(
self: &Rc<Self>,
old_id: KeyboardStateId,
new: &KeyboardState,
) {
if self.keyboard.is_none() {
return;
}
@ -94,7 +99,7 @@ impl EiSeat {
if self.is_sender() {
return;
}
self.handle_xkb_state_change(old_id, state);
self.handle_keyboard_state_change(old_id, state);
return;
}
if let Some(kb) = self.keyboard.get() {
@ -106,7 +111,7 @@ impl EiSeat {
self: &Rc<Self>,
time_usec: u64,
key: u32,
state: u32,
state: KeyState,
kb_state: &KeyboardState,
) {
if self.is_sender() {
@ -114,7 +119,7 @@ impl EiSeat {
}
let old_id = self.kb_state_id.get();
if old_id != kb_state.id {
self.handle_xkb_state_change(old_id, kb_state);
self.handle_keyboard_state_change(old_id, kb_state);
}
if let Some(kb) = self.keyboard.get() {
kb.send_key(key, state);
@ -298,8 +303,8 @@ impl EiSeat {
fn get_kb_state(&self) -> Rc<dyn DynKeyboardState> {
match self.context() {
EiContext::Sender => self.seat.seat_xkb_state(),
EiContext::Receiver => self.seat.latest_xkb_state(),
EiContext::Sender => self.seat.seat_kb_state(),
EiContext::Receiver => self.seat.latest_kb_state(),
}
}

View file

@ -1,12 +1,9 @@
use {
crate::{
gfx_apis::gl::{
egl::sys::{EGLDisplay, EGL_EXTENSIONS},
gl::sys::GL_EXTENSIONS,
sys::{EGL, GLESV2},
RenderError,
},
utils::trim::AsciiTrim,
crate::gfx_apis::gl::{
egl::sys::{EGLDisplay, EGL_EXTENSIONS},
gl::sys::GL_EXTENSIONS,
sys::{EGL, GLESV2},
RenderError,
},
ahash::AHashSet,
bstr::ByteSlice,
@ -21,7 +18,7 @@ unsafe fn get_extensions(ext: *const c::c_char) -> Option<AHashSet<String>> {
let mut res = AHashSet::new();
let ext = unsafe { CStr::from_ptr(ext).to_bytes() };
for part in ext.split_str(" ") {
let name = part.trim();
let name = part.trim_ascii();
if name.len() > 0 {
if let Ok(s) = str::from_utf8(name) {
res.insert(s.to_string());

View file

@ -4,6 +4,7 @@ use {
client::{Client, ClientError},
clientmem::{ClientMem, ClientMemError},
ifs::wl_seat::WlSeatGlobal,
kbvm::{KbvmError, KbvmMap},
leaks::Tracker,
libinput::consts::{
AccelProfile, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
@ -13,7 +14,6 @@ use {
state::{DeviceHandlerData, InputDeviceData},
utils::errorfmt::ErrorFmt,
wire::{jay_input::*, JayInputId},
xkbcommon::{XkbCommonError, XkbKeymap},
},
std::rc::Rc,
thiserror::Error,
@ -72,11 +72,11 @@ impl JayInput {
});
}
fn send_keymap(&self, map: &XkbKeymap) {
fn send_keymap(&self, map: &KbvmMap) {
self.client.event(Keymap {
self_id: self.id,
keymap: map.map.clone(),
keymap_len: (map.map_len - 1) as _,
keymap: map.map.map.clone(),
keymap_len: (map.map.len - 1) as _,
});
}
@ -167,7 +167,7 @@ impl JayInput {
fn set_keymap_impl<F>(&self, keymap: &Rc<OwnedFd>, len: u32, f: F) -> Result<(), JayInputError>
where
F: FnOnce(&Rc<XkbKeymap>) -> Result<(), JayInputError>,
F: FnOnce(&Rc<KbvmMap>) -> Result<(), JayInputError>,
{
let cm = Rc::new(ClientMem::new_private(
keymap,
@ -180,7 +180,7 @@ impl JayInput {
let mut map = vec![];
cm.read(&mut map)?;
self.or_error(|| {
let map = self.client.state.xkb_ctx.keymap_from_str(&map)?;
let map = self.client.state.kb_ctx.parse_keymap(&map)?;
f(&map)?;
Ok(())
})
@ -489,7 +489,7 @@ pub enum JayInputError {
#[error("Could not access client memory")]
ClientMemError(#[from] ClientMemError),
#[error("Could not parse keymap")]
XkbCommonError(#[from] XkbCommonError),
ParseKeymap(#[from] KbvmError),
#[error("Output is not connected")]
OutputNotConnected,
}

View file

@ -14,7 +14,6 @@ use {
leaks::Tracker,
object::{Object, Version},
wire::{jay_seat_events::*, JaySeatEventsId},
xkbcommon::ModifierState,
},
std::{convert::Infallible, rc::Rc},
};
@ -26,12 +25,12 @@ pub struct JaySeatEvents {
}
impl JaySeatEvents {
pub fn send_modifiers(&self, seat: SeatId, mods: &ModifierState) {
pub fn send_modifiers(&self, seat: SeatId, mods: &kbvm::Components) {
self.client.event(Modifiers {
self_id: self.id,
seat: seat.raw(),
modifiers: mods.mods_effective,
group: mods.group,
modifiers: mods.mods.0,
group: mods.group.0,
});
}

View file

@ -72,6 +72,8 @@ use {
},
xdg_toplevel_drag_v1::XdgToplevelDragV1,
},
kbvm::{KbvmMap, KbvmMapId, KbvmState, PhysicalKeyboardState},
keyboard::{DynKeyboardState, KeyboardState, KeyboardStateId, KeymapFd},
leaks::Tracker,
object::{Object, Version},
rect::Rect,
@ -82,8 +84,8 @@ use {
},
utils::{
asyncevent::AsyncEvent, bindings::PerClientBindings, clonecell::CloneCell,
copyhashmap::CopyHashMap, errorfmt::ErrorFmt, linkedlist::LinkedNode, numcell::NumCell,
rc_eq::rc_eq, smallmap::SmallMap,
copyhashmap::CopyHashMap, linkedlist::LinkedNode, numcell::NumCell, rc_eq::rc_eq,
smallmap::SmallMap,
},
wire::{
wl_seat::*, ExtIdleNotificationV1Id, WlDataDeviceId, WlKeyboardId, WlPointerId,
@ -91,7 +93,6 @@ use {
ZwpTextInputV3Id,
},
wire_ei::EiSeatId,
xkbcommon::{DynKeyboardState, KeyboardState, KeymapId, XkbKeymap, XkbState},
},
ahash::AHashMap,
smallvec::SmallVec,
@ -103,7 +104,6 @@ use {
rc::{Rc, Weak},
},
thiserror::Error,
uapi::OwnedFd,
};
pub use {
event_handling::NodeSeatState,
@ -143,6 +143,13 @@ impl Drop for DroppedDnd {
}
}
linear_ids!(PhysicalKeyboardIds, PhysicalKeyboardId, u64);
pub struct PhysicalKeyboard {
has_custom_map: Cell<bool>,
pub phy_state: PhysicalKeyboardState,
}
linear_ids!(SeatIds, SeatId);
pub struct WlSeatGlobal {
@ -168,10 +175,12 @@ pub struct WlSeatGlobal {
>,
data_control_devices: CopyHashMap<DataControlDeviceId, Rc<dyn DynDataControlDevice>>,
repeat_rate: Cell<(i32, i32)>,
seat_kb_map: CloneCell<Rc<XkbKeymap>>,
seat_xkb_state: CloneCell<Rc<RefCell<XkbState>>>,
seat_kb_map: CloneCell<Rc<KbvmMap>>,
seat_kb_state: CloneCell<Rc<RefCell<KbvmState>>>,
latest_kb_state: CloneCell<Rc<dyn DynKeyboardState>>,
xkb_states: CopyHashMap<KeymapId, Weak<RefCell<XkbState>>>,
latest_kb_state_id: Cell<KeyboardStateId>,
kb_states: CopyHashMap<KbvmMapId, Weak<RefCell<KbvmState>>>,
kb_devices: CopyHashMap<PhysicalKeyboardId, Rc<PhysicalKeyboard>>,
cursor_user_group: Rc<CursorUserGroup>,
pointer_cursor: Rc<CursorUser>,
tree_changed: Rc<AsyncEvent>,
@ -213,13 +222,11 @@ const CHANGE_TREE: u32 = 1 << 1;
impl WlSeatGlobal {
pub fn new(name: GlobalName, seat_name: &str, state: &Rc<State>) -> Rc<Self> {
let seat_xkb_state = state
.default_keymap
.state(state.keyboard_state_ids.next())
.map(|s| Rc::new(RefCell::new(s)))
.unwrap();
let xkb_states = CopyHashMap::new();
xkb_states.set(state.default_keymap.id, Rc::downgrade(&seat_xkb_state));
let seat_kb_state = state.default_keymap.state(state.keyboard_state_ids.next());
let latest_kb_state_id = seat_kb_state.kb_state.id;
let seat_kb_state = Rc::new(RefCell::new(seat_kb_state));
let kb_states = CopyHashMap::new();
kb_states.set(state.default_keymap.id, Rc::downgrade(&seat_kb_state));
let cursor_user_group = CursorUserGroup::create(state);
let cursor_user = cursor_user_group.create_user();
cursor_user.activate();
@ -242,9 +249,11 @@ impl WlSeatGlobal {
primary_selection_devices: RefCell::new(Default::default()),
repeat_rate: Cell::new((25, 250)),
seat_kb_map: CloneCell::new(state.default_keymap.clone()),
seat_xkb_state: CloneCell::new(seat_xkb_state.clone()),
latest_kb_state: CloneCell::new(seat_xkb_state.clone()),
xkb_states,
seat_kb_state: CloneCell::new(seat_kb_state.clone()),
latest_kb_state: CloneCell::new(seat_kb_state.clone()),
latest_kb_state_id: Cell::new(latest_kb_state_id),
kb_states,
kb_devices: Default::default(),
cursor_user_group,
pointer_cursor: cursor_user,
tree_changed: Default::default(),
@ -317,7 +326,7 @@ impl WlSeatGlobal {
}
}
pub fn keymap(&self) -> Rc<XkbKeymap> {
pub fn keymap(&self) -> Rc<KbvmMap> {
self.seat_kb_map.get()
}
@ -495,53 +504,46 @@ impl WlSeatGlobal {
false
}
pub fn set_seat_keymap(&self, keymap: &Rc<XkbKeymap>) {
let Some(xkb_state) = self.get_xkb_state(keymap) else {
return;
};
pub fn set_seat_keymap(&self, keymap: &Rc<KbvmMap>) {
self.seat_kb_map.set(keymap.clone());
let old = self.seat_xkb_state.set(xkb_state.clone());
if !rc_eq(&old, &xkb_state) {
self.handle_xkb_state_change(&old.borrow(), &xkb_state.borrow());
let new = self.get_kb_state(keymap);
let old = self.seat_kb_state.set(new.clone());
if rc_eq(&old, &new) {
return;
}
self.kb_devices.lock().retain(|_, p| p.has_custom_map.get());
self.handle_keyboard_state_change(&old.borrow().kb_state, &new.borrow().kb_state);
}
fn handle_xkb_state_change(&self, old: &XkbState, new: &XkbState) {
fn handle_keyboard_state_change(&self, old: &KeyboardState, new: &KeyboardState) {
self.for_each_ei_seat(|ei_seat| {
ei_seat.handle_xkb_state_change(old.kb_state.id, &new.kb_state);
ei_seat.handle_keyboard_state_change(old.id, new);
});
let Some(surface) = self.keyboard_node.get().node_into_surface() else {
return;
};
let serial = surface.client.next_serial();
self.surface_kb_event(Version::ALL, &surface, |kb| {
if kb.kb_state_id() == old.kb_state.id {
if kb.kb_state_id() == old.id {
kb.send_leave(serial, surface.id);
kb.enter(serial, surface.id, &new.kb_state);
kb.enter(serial, surface.id, new);
}
});
}
pub fn get_xkb_state(&self, keymap: &Rc<XkbKeymap>) -> Option<Rc<RefCell<XkbState>>> {
if let Some(weak) = self.xkb_states.get(&keymap.id) {
pub fn get_kb_state(&self, keymap: &Rc<KbvmMap>) -> Rc<RefCell<KbvmState>> {
if let Some(weak) = self.kb_states.get(&keymap.id) {
if let Some(state) = weak.upgrade() {
return Some(state);
return state;
}
}
self.xkb_states
self.kb_states
.lock()
.retain(|_, state| state.strong_count() > 0);
match keymap.state(self.state.keyboard_state_ids.next()) {
Ok(s) => {
let s = Rc::new(RefCell::new(s));
self.xkb_states.set(keymap.id, Rc::downgrade(&s));
Some(s)
}
Err(e) => {
log::error!("Could not create xkb state: {}", ErrorFmt(e));
None
}
}
let s = keymap.state(self.state.keyboard_state_ids.next());
let s = Rc::new(RefCell::new(s));
self.kb_states.set(keymap.id, Rc::downgrade(&s));
s
}
pub fn prepare_for_lock(self: &Rc<Self>) {
@ -1030,16 +1032,17 @@ impl WlSeatGlobal {
self.update_capabilities();
}
pub fn remove_ei_seat(&self, ei: &EiSeat) {
pub fn remove_ei_seat(self: &Rc<Self>, ei: &EiSeat) {
self.ei_seats.remove(&(ei.client.id, ei.id));
self.destroy_physical_keyboard(ei.keyboard_id);
self.update_capabilities();
}
pub fn seat_xkb_state(&self) -> Rc<dyn DynKeyboardState> {
self.seat_xkb_state.get()
pub fn seat_kb_state(&self) -> Rc<dyn DynKeyboardState> {
self.seat_kb_state.get()
}
pub fn latest_xkb_state(&self) -> Rc<dyn DynKeyboardState> {
pub fn latest_kb_state(&self) -> Rc<dyn DynKeyboardState> {
self.latest_kb_state.get()
}
@ -1090,6 +1093,33 @@ impl WlSeatGlobal {
}
self.focus_node_with_serial(node, serial);
}
pub fn get_physical_keyboard(
&self,
id: PhysicalKeyboardId,
map: Option<&Rc<KbvmMap>>,
) -> Rc<PhysicalKeyboard> {
if let Some(d) = self.kb_devices.get(&id) {
return d;
}
let state = match map {
Some(m) => self.get_kb_state(m),
_ => self.get_kb_state(&self.seat_kb_map.get()),
};
let d = Rc::new(PhysicalKeyboard {
has_custom_map: Cell::new(map.is_some()),
phy_state: PhysicalKeyboardState::new(&state),
});
self.kb_devices.set(id, d.clone());
d
}
pub fn destroy_physical_keyboard(self: &Rc<Self>, id: PhysicalKeyboardId) {
let Some(kb) = self.kb_devices.remove(&id) else {
return;
};
kb.phy_state.destroy(self.state.now_usec(), self);
}
}
impl CursorUserOwner for WlSeatGlobal {
@ -1148,11 +1178,15 @@ impl WlSeat {
})
}
pub fn keymap_fd(&self, state: &KeyboardState) -> Result<Rc<OwnedFd>, WlKeyboardError> {
pub fn keymap_fd(&self, state: &KeyboardState) -> Result<KeymapFd, WlKeyboardError> {
let fd = match self.client.is_xwayland {
true => &state.xwayland_map,
_ => &state.map,
};
if self.version >= READ_ONLY_KEYMAP_SINCE {
return Ok(state.map.clone());
return Ok(fd.clone());
}
Ok(state.create_new_keymap_fd()?)
Ok(fd.create_unprotected_fd()?)
}
}
@ -1177,7 +1211,7 @@ impl WlSeatRequestHandler for WlSeat {
p.enter(
self.client.next_serial(),
surface.id,
&self.global.seat_xkb_state.get().borrow().kb_state,
&self.global.seat_kb_state.get().borrow().kb_state,
);
}
}
@ -1264,17 +1298,20 @@ pub fn collect_kb_foci(node: Rc<dyn Node>) -> SmallVec<[Rc<WlSeatGlobal>; 3]> {
impl DeviceHandlerData {
pub fn set_seat(&self, seat: Option<Rc<WlSeatGlobal>>) {
let old = self.seat.set(seat.clone());
if let Some(old) = old {
if let Some(new) = &seat {
if let Some(new) = &seat {
if let Some(old) = self.seat.get() {
if old.id() == new.id() {
return;
}
}
let xkb_state = self.get_effective_xkb_state(&old);
let xkb_state = &mut *xkb_state.borrow_mut();
xkb_state.reset();
old.handle_xkb_state_change(xkb_state, xkb_state);
} else {
if self.seat.is_none() {
return;
}
}
self.destroy_physical_keyboard_state();
let old = self.seat.set(seat.clone());
if let Some(old) = old {
if let Some(info) = &self.tablet_init {
old.tablet_remove_tablet(info.id);
}
@ -1286,7 +1323,6 @@ impl DeviceHandlerData {
old.update_capabilities();
}
}
self.update_xkb_state();
if let Some(seat) = &seat {
if let Some(info) = &self.tablet_init {
seat.tablet_add_tablet(self.device.id(), info);
@ -1301,34 +1337,15 @@ impl DeviceHandlerData {
}
}
pub fn set_keymap(&self, keymap: Option<Rc<XkbKeymap>>) {
self.keymap.set(keymap);
self.update_xkb_state();
}
fn get_effective_xkb_state(&self, seat: &WlSeatGlobal) -> Rc<RefCell<XkbState>> {
match self.xkb_state.get() {
Some(s) => s,
_ => seat.seat_xkb_state.get(),
}
}
fn update_xkb_state(&self) {
let Some(seat) = self.seat.get() else {
self.xkb_state.take();
return;
fn destroy_physical_keyboard_state(&self) {
if let Some(seat) = self.seat.get() {
seat.destroy_physical_keyboard(self.keyboard_id);
};
let old = self.get_effective_xkb_state(&seat);
self.xkb_state.take();
if let Some(keymap) = self.keymap.get() {
if let Some(state) = seat.get_xkb_state(&keymap) {
self.xkb_state.set(Some(state));
}
}
let new = self.get_effective_xkb_state(&seat);
if !rc_eq(&old, &new) {
seat.handle_xkb_state_change(&old.borrow(), &new.borrow());
}
}
pub fn set_keymap(&self, keymap: Option<Rc<KbvmMap>>) {
self.destroy_physical_keyboard_state();
self.keymap.set(keymap);
}
pub fn set_output(&self, output: Option<&WlOutputGlobal>) {

View file

@ -19,7 +19,7 @@ use {
wl_seat::{
tablet::{TabletPad, TabletPadId, TabletTool, TabletToolId},
text_input::TextDisconnectReason,
wl_keyboard::{self, WlKeyboard},
wl_keyboard::WlKeyboard,
wl_pointer::{
self, PendingScroll, WlPointer, AXIS_DISCRETE_SINCE_VERSION,
AXIS_RELATIVE_DIRECTION_SINCE_VERSION, AXIS_SOURCE_SINCE_VERSION,
@ -33,15 +33,19 @@ use {
},
wl_surface::{xdg_surface::xdg_popup::XdgPopup, WlSurface},
},
kbvm::KbvmState,
keyboard::KeyboardState,
object::Version,
rect::Rect,
state::DeviceHandlerData,
tree::{Direction, Node, ToplevelNode},
utils::{bitflags::BitflagsExt, hash_map_ext::HashMapExt, smallmap::SmallMap},
utils::{
bitflags::BitflagsExt, hash_map_ext::HashMapExt, smallmap::SmallMap,
syncqueue::SyncQueue,
},
wire::WlDataOfferId,
xkbcommon::{KeyboardState, XkbState, XKB_KEY_DOWN, XKB_KEY_UP},
},
isnt::std_1::primitive::{IsntSlice2Ext, IsntSliceExt},
isnt::std_1::primitive::IsntSliceExt,
jay_config::{
input::SwitchEvent,
keyboard::{
@ -49,8 +53,9 @@ use {
syms::{KeySym, SYM_Escape},
},
},
kbvm::{state_machine::Event, ModifierMask},
smallvec::SmallVec,
std::{cell::RefCell, collections::hash_map::Entry, rc::Rc},
std::{cell::RefCell, collections::hash_map::Entry, mem, rc::Rc},
};
#[derive(Default)]
@ -319,7 +324,11 @@ impl WlSeatGlobal {
time_usec,
key,
state,
} => self.key_event(time_usec, key, state, || dev.get_effective_xkb_state(self)),
} => {
self.get_physical_keyboard(dev.keyboard_id, dev.keymap.get().as_ref())
.phy_state
.update(time_usec, self, key, state);
}
InputEvent::ConnectorPosition {
time_usec,
connector,
@ -778,130 +787,125 @@ impl WlSeatGlobal {
self.touch_owner.frame(self);
}
pub fn key_event_with_seat_state(
pub fn key_events(
self: &Rc<Self>,
time_usec: u64,
key: u32,
key_state: KeyState,
events: &SyncQueue<Event>,
kbvm_state_rc: &Rc<RefCell<KbvmState>>,
) {
self.key_event(time_usec, key, key_state, || self.seat_xkb_state.get());
}
pub(super) fn key_event<F>(
self: &Rc<Self>,
time_usec: u64,
key: u32,
key_state: KeyState,
mut get_state: F,
) where
F: FnMut() -> Rc<RefCell<XkbState>>,
{
let mut xkb_state_rc = get_state();
let mut xkb_state = xkb_state_rc.borrow_mut();
let (state, xkb_dir) = {
match key_state {
KeyState::Released => {
if xkb_state.kb_state.pressed_keys.not_contains(&key) {
return;
}
(wl_keyboard::RELEASED, XKB_KEY_UP)
}
KeyState::Pressed => {
if xkb_state.kb_state.pressed_keys.contains(&key) {
return;
}
(wl_keyboard::PRESSED, XKB_KEY_DOWN)
}
}
};
let mut kbvm_state = kbvm_state_rc.borrow_mut();
self.latest_kb_state.set(kbvm_state_rc.clone());
self.latest_kb_state_id.set(kbvm_state.kb_state.id);
let mut shortcuts = SmallVec::<[_; 1]>::new();
let new_mods;
{
let mut mods = xkb_state.mods().mods_effective & !(CAPS.0 | NUM.0);
if state == wl_keyboard::RELEASED {
mods |= RELEASE.0;
}
let scs = &*self.shortcuts.borrow();
let keysyms = xkb_state.unmodified_keysyms(key);
let mut revert_pointer_to_default = false;
for &sym in keysyms {
if sym == SYM_Escape.0 && mods == 0 {
revert_pointer_to_default = true;
let mut components_changed = false;
while let Some(event) = events.pop() {
components_changed |= kbvm_state.kb_state.mods.apply_event(event);
let (key_state, kc) = match event {
Event::KeyDown(kc) => (KeyState::Pressed, kc),
Event::KeyUp(kc) => (KeyState::Released, kc),
_ => continue,
};
let update_pressed_keys = |kbvm_state: &mut KbvmState| {
let pk = &mut kbvm_state.kb_state.pressed_keys;
match key_state {
KeyState::Released => pk.remove(&kc.to_evdev()),
KeyState::Pressed => pk.insert(kc.to_evdev()),
}
if !self.state.lock.locked.get() {
if let Some(key_mods) = scs.get(&sym) {
for (key_mods, mask) in key_mods {
if mods & mask == key_mods {
shortcuts.push(InvokedShortcut {
unmasked_mods: Modifiers(mods),
effective_mods: Modifiers(key_mods),
sym: KeySym(sym),
});
};
shortcuts.clear();
{
let mut mods = kbvm_state.kb_state.mods.mods.0 & !(CAPS.0 | NUM.0);
if key_state == KeyState::Released {
mods |= RELEASE.0;
}
let scs = &*self.shortcuts.borrow();
let keysyms = kbvm_state.map.lookup_table.lookup(
kbvm_state.kb_state.mods.group,
ModifierMask::default(),
kc,
);
let mut revert_pointer_to_default = false;
for props in keysyms {
let sym = props.keysym().0;
if sym == SYM_Escape.0 && mods == 0 {
revert_pointer_to_default = true;
}
if !self.state.lock.locked.get() {
if let Some(key_mods) = scs.get(&sym) {
for (key_mods, mask) in key_mods {
if mods & mask == key_mods {
shortcuts.push(InvokedShortcut {
unmasked_mods: Modifiers(mods),
effective_mods: Modifiers(key_mods),
sym: KeySym(sym),
});
}
}
}
}
}
}
if revert_pointer_to_default {
drop(xkb_state);
self.pointer_owner.revert_to_default(self);
xkb_state = xkb_state_rc.borrow_mut();
}
new_mods = xkb_state.update(key, xkb_dir);
}
self.state.for_each_seat_tester(|t| {
t.send_key(self.id, time_usec, key, key_state);
});
let node = self.keyboard_node.get();
let input_method_grab = self.input_method_grab.get();
let mut forward = true;
if shortcuts.is_not_empty() {
self.forward.set(state == wl_keyboard::RELEASED);
if let Some(config) = self.state.config.get() {
let id = xkb_state.kb_state.id;
drop(xkb_state);
for shortcut in shortcuts {
config.invoke_shortcut(self.id(), &shortcut);
}
xkb_state_rc = get_state();
xkb_state = xkb_state_rc.borrow_mut();
if id != xkb_state.kb_state.id {
return;
if revert_pointer_to_default {
drop(kbvm_state);
self.pointer_owner.revert_to_default(self);
kbvm_state = kbvm_state_rc.borrow_mut();
}
}
forward = self.forward.get();
}
if forward {
match &input_method_grab {
Some(g) => g.on_key(time_usec, key, state, &xkb_state.kb_state),
_ => node.node_on_key(self, time_usec, key, state, &xkb_state.kb_state),
}
self.for_each_ei_seat(|ei_seat| {
ei_seat.handle_key(time_usec, key, state, &xkb_state.kb_state);
});
}
if new_mods {
self.for_each_ei_seat(|ei_seat| {
ei_seat.handle_modifiers_changed(&xkb_state.kb_state);
});
self.state.for_each_seat_tester(|t| {
t.send_modifiers(self.id, &xkb_state.kb_state.mods);
t.send_key(self.id, time_usec, kc.to_evdev(), key_state);
});
match &input_method_grab {
Some(g) => g.on_modifiers(&xkb_state.kb_state),
_ => node.node_on_mods(self, &xkb_state.kb_state),
if shortcuts.is_not_empty() {
self.forward.set(key_state == KeyState::Released);
if let Some(config) = self.state.config.get() {
drop(kbvm_state);
for shortcut in &shortcuts {
config.invoke_shortcut(self.id(), shortcut);
}
kbvm_state = kbvm_state_rc.borrow_mut();
if kbvm_state.kb_state.id != self.latest_kb_state_id.get() {
update_pressed_keys(&mut kbvm_state);
kbvm_state.apply_events(events);
return;
}
}
if !self.forward.get() {
update_pressed_keys(&mut kbvm_state);
continue;
}
}
self.send_components(&mut components_changed, &kbvm_state);
match self.input_method_grab.get() {
Some(g) => g.on_key(time_usec, kc.to_evdev(), key_state, &kbvm_state.kb_state),
_ => self.keyboard_node.get().node_on_key(
self,
time_usec,
kc.to_evdev(),
key_state,
&kbvm_state.kb_state,
),
}
self.for_each_ei_seat(|ei_seat| {
ei_seat.handle_key(time_usec, kc.to_evdev(), key_state, &kbvm_state.kb_state);
});
update_pressed_keys(&mut kbvm_state);
}
match key_state {
KeyState::Released => {
xkb_state.kb_state.pressed_keys.remove(&key);
}
KeyState::Pressed => {
xkb_state.kb_state.pressed_keys.insert(key);
}
self.send_components(&mut components_changed, &kbvm_state);
}
fn send_components(&self, components_changed: &mut bool, kbvm_state: &KbvmState) {
if !mem::take(components_changed) {
return;
}
let kb_state = &kbvm_state.kb_state;
self.for_each_ei_seat(|ei_seat| {
ei_seat.handle_modifiers_changed(kb_state);
});
self.state.for_each_seat_tester(|t| {
t.send_modifiers(self.id, &kb_state.mods);
});
match self.input_method_grab.get() {
Some(g) => g.on_modifiers(kb_state),
_ => self.keyboard_node.get().node_on_mods(self, kb_state),
}
drop(xkb_state);
self.latest_kb_state.set(xkb_state_rc);
}
pub(super) fn for_each_ei_seat(&self, mut f: impl FnMut(&Rc<EiSeat>)) {
@ -1331,7 +1335,7 @@ impl WlSeatGlobal {
surface: &WlSurface,
time_usec: u64,
key: u32,
state: u32,
state: KeyState,
kb_state: &KeyboardState,
) {
let serial = surface.client.next_serial();

View file

@ -1,12 +1,13 @@
use {
crate::{
backend::KeyState,
client::{Client, ClientError},
ifs::wl_seat::{text_input::zwp_input_method_v2::ZwpInputMethodV2, wl_keyboard},
keyboard::{KeyboardState, KeyboardStateId},
leaks::Tracker,
object::{Object, Version},
utils::errorfmt::ErrorFmt,
wire::{zwp_input_method_keyboard_grab_v2::*, ZwpInputMethodKeyboardGrabV2Id},
xkbcommon::{KeyboardState, KeyboardStateId},
},
std::{cell::Cell, rc::Rc},
thiserror::Error,
@ -27,7 +28,7 @@ impl ZwpInputMethodKeyboardGrabV2 {
}
fn send_keymap(&self, kb_state: &KeyboardState) {
let map = match kb_state.create_new_keymap_fd() {
let map = match kb_state.map.create_unprotected_fd() {
Ok(m) => m,
Err(e) => {
log::error!("Could not create new keymap fd: {}", ErrorFmt(e));
@ -37,8 +38,8 @@ impl ZwpInputMethodKeyboardGrabV2 {
self.client.event(Keymap {
self_id: self.id,
format: wl_keyboard::XKB_V1,
fd: map,
size: kb_state.map_len as _,
fd: map.map,
size: map.len as _,
});
}
@ -48,7 +49,7 @@ impl ZwpInputMethodKeyboardGrabV2 {
self.kb_state_id.set(kb_state.id);
}
pub fn on_key(&self, time_usec: u64, key: u32, state: u32, kb_state: &KeyboardState) {
pub fn on_key(&self, time_usec: u64, key: u32, state: KeyState, kb_state: &KeyboardState) {
let serial = self.client.next_serial();
if self.kb_state_id.get() != kb_state.id {
self.update_state(serial, kb_state);
@ -56,13 +57,16 @@ impl ZwpInputMethodKeyboardGrabV2 {
self.send_key(serial, time_usec, key, state);
}
fn send_key(&self, serial: u64, time_usec: u64, key: u32, state: u32) {
fn send_key(&self, serial: u64, time_usec: u64, key: u32, state: KeyState) {
self.client.event(Key {
self_id: self.id,
serial: serial as _,
time: (time_usec / 1000) as _,
key,
state,
state: match state {
KeyState::Released => wl_keyboard::RELEASED,
KeyState::Pressed => wl_keyboard::PRESSED,
},
})
}
@ -78,10 +82,10 @@ impl ZwpInputMethodKeyboardGrabV2 {
self.client.event(Modifiers {
self_id: self.id,
serial: serial as _,
mods_depressed: kb_state.mods.mods_depressed,
mods_latched: kb_state.mods.mods_latched,
mods_locked: kb_state.mods.mods_locked,
group: kb_state.mods.group,
mods_depressed: kb_state.mods.mods_pressed.0,
mods_latched: kb_state.mods.mods_latched.0,
mods_locked: kb_state.mods.mods_locked.0,
group: kb_state.mods.group.0,
})
}

View file

@ -13,11 +13,11 @@ use {
ZwpInputPopupSurfaceV2, ZwpInputPopupSurfaceV2Error,
},
},
keyboard::KeyboardStateId,
leaks::Tracker,
object::{Object, Version},
utils::{clonecell::CloneCell, numcell::NumCell, smallmap::SmallMap},
wire::{zwp_input_method_v2::*, ZwpInputMethodV2Id, ZwpInputPopupSurfaceV2Id},
xkbcommon::KeyboardStateId,
},
std::{
cell::{Cell, RefCell},

View file

@ -1,14 +1,19 @@
use {
crate::{
backend::KeyState,
client::ClientError,
ifs::wl_seat::WlSeat,
keyboard::{KeyboardError, KeyboardState, KeyboardStateId},
leaks::Tracker,
object::{Object, Version},
utils::errorfmt::ErrorFmt,
utils::{errorfmt::ErrorFmt, vecset::VecSet},
wire::{wl_keyboard::*, WlKeyboardId, WlSurfaceId},
xkbcommon::{KeyboardState, KeyboardStateId, ModifierState, XkbCommonError},
},
std::{cell::Cell, rc::Rc},
kbvm::Components,
std::{
cell::{Cell, RefCell},
rc::Rc,
},
thiserror::Error,
};
@ -25,6 +30,7 @@ pub struct WlKeyboard {
id: WlKeyboardId,
seat: Rc<WlSeat>,
kb_state_id: Cell<KeyboardStateId>,
pressed_keys: RefCell<VecSet<u32>>,
pub tracker: Tracker<Self>,
}
@ -34,6 +40,7 @@ impl WlKeyboard {
id,
seat: seat.clone(),
kb_state_id: Cell::new(KeyboardStateId::from_raw(0)),
pressed_keys: Default::default(),
tracker: Default::default(),
}
}
@ -73,8 +80,8 @@ impl WlKeyboard {
self.seat.client.event(Keymap {
self_id: self.id,
format: XKB_V1,
fd,
size: state.map_len as _,
fd: fd.map,
size: fd.len as _,
});
}
@ -88,6 +95,11 @@ impl WlKeyboard {
}
fn send_enter(&self, serial: u64, surface: WlSurfaceId, keys: &[u32]) {
{
let pk = &mut self.pressed_keys.borrow_mut();
pk.clear();
pk.extend(keys);
}
self.seat.client.event(Enter {
self_id: self.id,
serial: serial as _,
@ -109,7 +121,7 @@ impl WlKeyboard {
serial: u64,
time: u32,
key: u32,
state: u32,
state: KeyState,
surface: WlSurfaceId,
kb_state: &KeyboardState,
) {
@ -119,13 +131,31 @@ impl WlKeyboard {
self.send_key(serial, time, key, state);
}
fn send_key(&self, serial: u64, time: u32, key: u32, state: u32) {
fn send_key(&self, serial: u64, time: u32, key: u32, state: KeyState) {
{
let pk = &mut self.pressed_keys.borrow_mut();
match state {
KeyState::Released => {
if !pk.remove(&key) {
return;
}
}
KeyState::Pressed => {
if !pk.insert(key) {
return;
}
}
}
}
self.seat.client.event(Key {
self_id: self.id,
serial: serial as _,
time,
key,
state,
state: match state {
KeyState::Released => RELEASED,
KeyState::Pressed => PRESSED,
},
})
}
@ -137,14 +167,14 @@ impl WlKeyboard {
}
}
fn send_modifiers(&self, serial: u64, mods: &ModifierState) {
fn send_modifiers(&self, serial: u64, mods: &Components) {
self.seat.client.event(Modifiers {
self_id: self.id,
serial: serial as _,
mods_depressed: mods.mods_depressed,
mods_latched: mods.mods_latched,
mods_locked: mods.mods_locked,
group: mods.group,
mods_depressed: mods.mods_pressed.0,
mods_latched: mods.mods_latched.0,
mods_locked: mods.mods_locked.0,
group: mods.group.0,
})
}
@ -181,6 +211,6 @@ pub enum WlKeyboardError {
#[error(transparent)]
ClientError(Box<ClientError>),
#[error(transparent)]
XkbCommonError(#[from] XkbCommonError),
KeyboardError(#[from] KeyboardError),
}
efrom!(WlKeyboardError, ClientError);

View file

@ -3,10 +3,10 @@ use {
client::{Client, ClientCaps, ClientError, CAP_VIRTUAL_KEYBOARD_MANAGER},
globals::{Global, GlobalName},
ifs::wl_seat::zwp_virtual_keyboard_v1::ZwpVirtualKeyboardV1,
keyboard::KeyboardState,
leaks::Tracker,
object::{Object, Version},
wire::{zwp_virtual_keyboard_manager_v1::*, ZwpVirtualKeyboardManagerV1Id},
xkbcommon::KeyboardState,
},
std::{cell::RefCell, rc::Rc},
thiserror::Error,
@ -87,7 +87,7 @@ impl ZwpVirtualKeyboardManagerV1RequestHandler for ZwpVirtualKeyboardManagerV1 {
kb_state: Rc::new(RefCell::new(KeyboardState {
id: self.client.state.keyboard_state_ids.next(),
map: seat_keymap.map.clone(),
map_len: seat_keymap.map_len,
xwayland_map: seat_keymap.xwayland_map.clone(),
pressed_keys: Default::default(),
mods: Default::default(),
})),

View file

@ -1,5 +1,6 @@
use {
crate::{
backend::KeyState,
client::{Client, ClientError},
clientmem::{ClientMem, ClientMemError},
ifs::{
@ -9,10 +10,11 @@ use {
},
wl_surface::WlSurface,
},
kbvm::KbvmError,
keyboard::KeyboardState,
leaks::Tracker,
object::{Object, Version},
wire::{zwp_virtual_keyboard_v1::*, ZwpVirtualKeyboardV1Id},
xkbcommon::{KeyboardState, XkbCommonError},
},
std::{cell::RefCell, rc::Rc},
thiserror::Error,
@ -73,13 +75,13 @@ impl ZwpVirtualKeyboardV1RequestHandler for ZwpVirtualKeyboardV1 {
let map = self
.client
.state
.xkb_ctx
.keymap_from_str(&map)
.kb_ctx
.parse_keymap(&map)
.map_err(ZwpVirtualKeyboardV1Error::ParseKeymap)?;
*self.kb_state.borrow_mut() = KeyboardState {
id: self.client.state.keyboard_state_ids.next(),
map: map.map.clone(),
map_len: map.map_len,
xwayland_map: map.xwayland_map.clone(),
pressed_keys: Default::default(),
mods: Default::default(),
};
@ -89,19 +91,20 @@ impl ZwpVirtualKeyboardV1RequestHandler for ZwpVirtualKeyboardV1 {
fn key(&self, req: Key, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let kb_state = &mut *self.kb_state.borrow_mut();
let contains = kb_state.pressed_keys.contains(&req.key);
let valid = match req.state {
wl_keyboard::RELEASED => contains,
wl_keyboard::PRESSED => !contains,
let (state, valid) = match req.state {
wl_keyboard::RELEASED => (KeyState::Released, contains),
wl_keyboard::PRESSED => (KeyState::Pressed, !contains),
_ => return Err(ZwpVirtualKeyboardV1Error::UnknownState(req.state)),
};
if valid {
self.for_each_kb(|serial, surface, kb| {
kb.on_key(serial, req.time, req.key, req.state, surface.id, kb_state);
kb.on_key(serial, req.time, req.key, state, surface.id, kb_state);
});
match req.state {
wl_keyboard::RELEASED => kb_state.pressed_keys.remove(&req.key),
_ => kb_state.pressed_keys.insert(req.key),
};
self.seat.latest_kb_state_id.set(kb_state.id);
self.seat.latest_kb_state.set(self.kb_state.clone());
}
Ok(())
@ -109,14 +112,15 @@ impl ZwpVirtualKeyboardV1RequestHandler for ZwpVirtualKeyboardV1 {
fn modifiers(&self, req: Modifiers, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let kb_state = &mut *self.kb_state.borrow_mut();
kb_state.mods.mods_depressed = req.mods_depressed;
kb_state.mods.mods_latched = req.mods_latched;
kb_state.mods.mods_locked = req.mods_locked;
kb_state.mods.mods_effective = req.mods_depressed | req.mods_latched | req.mods_locked;
kb_state.mods.group = req.group;
kb_state.mods.mods_pressed.0 = req.mods_depressed;
kb_state.mods.mods_latched.0 = req.mods_latched;
kb_state.mods.mods_locked.0 = req.mods_locked;
kb_state.mods.group_locked.0 = req.group;
kb_state.mods.update_effective();
self.for_each_kb(|serial, surface, kb| {
kb.on_mods_changed(serial, surface.id, &kb_state);
});
self.seat.latest_kb_state_id.set(kb_state.id);
self.seat.latest_kb_state.set(self.kb_state.clone());
Ok(())
}
@ -153,6 +157,6 @@ pub enum ZwpVirtualKeyboardV1Error {
#[error("Could not read the keymap")]
ReadKeymap(#[source] ClientMemError),
#[error("Could not parse the keymap")]
ParseKeymap(#[source] XkbCommonError),
ParseKeymap(#[source] KbvmError),
}
efrom!(ZwpVirtualKeyboardV1Error, ClientError);

View file

@ -65,6 +65,7 @@ use {
zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
},
io_uring::IoUringError,
keyboard::KeyboardState,
leaks::Tracker,
object::{Object, Version},
rect::{DamageQueue, Rect, Region},
@ -88,7 +89,6 @@ use {
wl_surface::*, WlOutputId, WlSurfaceId, ZwpIdleInhibitorV1Id,
ZwpLinuxDmabufFeedbackV1Id,
},
xkbcommon::KeyboardState,
xwayland::XWaylandEvent,
},
ahash::AHashMap,
@ -1741,7 +1741,7 @@ impl Node for WlSurface {
seat: &WlSeatGlobal,
time_usec: u64,
key: u32,
state: u32,
state: KeyState,
kb_state: &KeyboardState,
) {
seat.key_surface(self, time_usec, key, state, kb_state);

View file

@ -3,7 +3,7 @@ use {
backend::KeyState,
clientmem::ClientMem,
it::{test_error::TestResult, testrun::TestRun},
xkbcommon::XkbContext,
kbvm::KbvmContext,
},
bstr::ByteSlice,
std::rc::Rc,
@ -14,9 +14,9 @@ testcase!();
async fn test(run: Rc<TestRun>) -> TestResult {
let virtual_keymap_str = {
let xkb = XkbContext::new()?;
let map = xkb.keymap_from_str(VIRTUAL_KEYMAP).unwrap();
read_keymap(&map.map, map.map_len)
let xkb = KbvmContext::default();
let map = xkb.parse_keymap(VIRTUAL_KEYMAP.as_bytes()).unwrap();
read_keymap(&map.map.map, map.map.len)
};
let ds = run.create_default_setup().await?;

222
src/kbvm.rs Normal file
View file

@ -0,0 +1,222 @@
use {
crate::{
backend::KeyState,
ifs::wl_seat::WlSeatGlobal,
keyboard::{DynKeyboardState, KeyboardState, KeyboardStateId, KeymapFd},
utils::{oserror::OsError, syncqueue::SyncQueue, vecset::VecSet},
},
kbvm::{
lookup::LookupTable,
state_machine::{self, Direction, Event, StateMachine},
xkb::{
self,
diagnostic::{Diagnostic, WriteToLog},
Keymap,
},
Keycode,
},
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,
pub ids: KbvmMapIds,
}
impl Default for KbvmContext {
fn default() -> Self {
let mut ctx = xkb::Context::builder();
ctx.enable_environment(true);
Self {
ctx: ctx.build(),
ids: Default::default(),
}
}
}
linear_ids!(KbvmMapIds, KbvmMapId, u64);
pub struct KbvmMap {
pub id: KbvmMapId,
pub state_machine: StateMachine,
pub lookup_table: LookupTable,
pub map: KeymapFd,
pub xwayland_map: KeymapFd,
}
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 builder = map.to_builder();
Ok(Rc::new(KbvmMap {
id: self.ids.next(),
state_machine: builder.build_state_machine(),
map: create_keymap_memfd(&map, false).map_err(KbvmError::KeymapMemfd)?,
xwayland_map: create_keymap_memfd(&map, true).map_err(KbvmError::KeymapMemfd)?,
lookup_table: builder.build_lookup_table(),
}))
}
}
fn create_keymap_memfd(map: &Keymap, xwayland: bool) -> Result<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)?;
memfd.write_all(str.as_bytes())?;
memfd.write_all(&[0])?;
uapi::lseek(memfd.raw(), 0, c::SEEK_SET)?;
uapi::fcntl_add_seals(
memfd.raw(),
c::F_SEAL_SEAL | c::F_SEAL_GROW | c::F_SEAL_SHRINK | c::F_SEAL_WRITE,
)?;
Ok(KeymapFd {
map: Rc::new(memfd),
len: str.len() + 1,
})
}
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.map.clone(),
xwayland_map: self.xwayland_map.clone(),
pressed_keys: Default::default(),
mods: 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.mods.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;
}
}
}
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,
},
);
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);
}
}

67
src/keyboard.rs Normal file
View file

@ -0,0 +1,67 @@
use {
crate::utils::{oserror::OsError, vecset::VecSet},
kbvm::Components,
std::{
cell::{Ref, RefCell},
rc::Rc,
},
thiserror::Error,
uapi::{c, Errno, OwnedFd},
};
#[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: KeymapFd,
pub xwayland_map: KeymapFd,
pub pressed_keys: VecSet<u32>,
pub mods: Components,
}
pub trait DynKeyboardState {
fn borrow(&self) -> Ref<'_, KeyboardState>;
}
impl DynKeyboardState for RefCell<KeyboardState> {
fn borrow(&self) -> Ref<'_, KeyboardState> {
self.borrow()
}
}
#[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 = match uapi::memfd_create("shared-keymap", c::MFD_CLOEXEC) {
Ok(fd) => fd,
Err(e) => return Err(KeyboardError::KeymapMemfd(e.into())),
};
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);
match res {
Ok(_) | Err(Errno(c::EINTR)) => {}
Err(e) => return Err(KeyboardError::KeymapCopy(e.into())),
}
}
Ok(Self {
map: Rc::new(fd),
len: self.len,
})
}
}

View file

@ -22,7 +22,7 @@ use {
},
},
udev::UdevError,
utils::{errorfmt::ErrorFmt, oserror::OsError, ptr_ext::PtrExt, trim::AsciiTrim},
utils::{errorfmt::ErrorFmt, oserror::OsError, ptr_ext::PtrExt},
},
bstr::ByteSlice,
isnt::std_1::primitive::IsntConstPtrExt,
@ -182,5 +182,9 @@ unsafe extern "C" fn jay_libinput_log_handler(
LIBINPUT_LOG_PRIORITY_ERROR => log::Level::Error,
_ => log::Level::Error,
};
log::log!(priority, "libinput: {}", str.to_bytes().trim().as_bstr());
log::log!(
priority,
"libinput: {}",
str.to_bytes().trim_ascii().as_bstr()
);
}

View file

@ -181,24 +181,14 @@ impl Log for LogWrapper {
let buffer = unsafe { &mut *buffer };
buffer.clear();
let now = SystemTime::now();
let _ = if let Some(mp) = record.module_path() {
writeln!(
buffer,
"[{} {:5} {}] {}",
humantime::format_rfc3339_millis(now),
record.level(),
mp,
record.args(),
)
} else {
writeln!(
buffer,
"[{} {:5}] {}",
humantime::format_rfc3339_millis(now),
record.level(),
record.args(),
)
};
let _ = writeln!(
buffer,
"[{} {:5} {}] {}",
humantime::format_rfc3339_millis(now),
record.level(),
record.target(),
record.args(),
);
let mut fd = Fd::new(self.logger.file_fd.load(Relaxed));
let _ = fd.write_all(buffer);
}

View file

@ -249,6 +249,7 @@ macro_rules! cenum {
}
}
#[expect(unused_macros)]
macro_rules! bitor {
($name:ident) => {
impl std::ops::BitOr for $name {

View file

@ -74,6 +74,8 @@ mod ifs;
mod io_uring;
#[cfg(feature = "it")]
mod it;
mod kbvm;
mod keyboard;
mod libinput;
mod logger;
mod logind;
@ -108,7 +110,6 @@ mod wire_ei;
mod wire_xcon;
mod wl_usr;
mod xcon;
mod xkbcommon;
mod xwayland;
fn main() {

View file

@ -48,7 +48,7 @@ use {
wl_output::{OutputGlobalOpt, OutputId, PersistentOutputState},
wl_seat::{
tablet::{TabletIds, TabletInit, TabletPadIds, TabletPadInit, TabletToolIds},
SeatIds, WlSeatGlobal,
PhysicalKeyboardId, PhysicalKeyboardIds, SeatIds, WlSeatGlobal,
},
wl_surface::{
tray::TrayItemIds,
@ -66,6 +66,8 @@ use {
zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1Global,
},
io_uring::IoUring,
kbvm::{KbvmContext, KbvmMap},
keyboard::KeyboardStateIds,
leaks::Tracker,
logger::Logger,
rect::{Rect, Region},
@ -99,7 +101,6 @@ use {
ExtForeignToplevelListV1Id, ExtIdleNotificationV1Id, JayRenderCtxId, JaySeatEventsId,
JayWorkspaceWatcherId, ZwpLinuxDmabufFeedbackV1Id,
},
xkbcommon::{KeyboardStateIds, XkbContext, XkbKeymap, XkbState},
xwayland::{self, XWaylandEvent},
},
ahash::{AHashMap, AHashSet},
@ -121,10 +122,10 @@ use {
};
pub struct State {
pub xkb_ctx: XkbContext,
pub kb_ctx: KbvmContext,
pub backend: CloneCell<Rc<dyn Backend>>,
pub forker: CloneCell<Option<Rc<ForkerProxy>>>,
pub default_keymap: Rc<XkbKeymap>,
pub default_keymap: Rc<KbvmMap>,
pub eng: Rc<AsyncEngine>,
pub render_ctx: CloneCell<Option<Rc<dyn GfxContext>>>,
pub drm_feedback: CloneCell<Option<Rc<DrmFeedback>>>,
@ -205,6 +206,7 @@ pub struct State {
pub wait_for_sync_obj: Rc<WaitForSyncObj>,
pub explicit_sync_enabled: Cell<bool>,
pub keyboard_state_ids: KeyboardStateIds,
pub physical_keyboard_ids: PhysicalKeyboardIds,
pub security_context_acceptors: SecurityContextAcceptors,
pub cursor_user_group_ids: CursorUserGroupIds,
pub cursor_user_ids: CursorUserIds,
@ -326,13 +328,13 @@ pub struct InputDeviceData {
}
pub struct DeviceHandlerData {
pub keyboard_id: PhysicalKeyboardId,
pub seat: CloneCell<Option<Rc<WlSeatGlobal>>>,
pub px_per_scroll_wheel: Cell<f64>,
pub device: Rc<dyn InputDevice>,
pub syspath: Option<String>,
pub devnode: Option<String>,
pub keymap: CloneCell<Option<Rc<XkbKeymap>>>,
pub xkb_state: CloneCell<Option<Rc<RefCell<XkbState>>>>,
pub keymap: CloneCell<Option<Rc<KbvmMap>>>,
pub output: CloneCell<Option<Rc<OutputGlobalOpt>>>,
pub tablet_init: Option<Box<TabletInit>>,
pub tablet_pad_init: Option<Box<TabletPadInit>>,

View file

@ -16,13 +16,13 @@ pub fn handle(state: &Rc<State>, dev: Rc<dyn InputDevice>) {
Some(dev_t) => udev_props(dev_t, 3),
};
let data = Rc::new(DeviceHandlerData {
keyboard_id: state.physical_keyboard_ids.next(),
seat: Default::default(),
px_per_scroll_wheel: Cell::new(PX_PER_SCROLL),
device: dev.clone(),
syspath: props.syspath,
devnode: props.devnode,
keymap: Default::default(),
xkb_state: Default::default(),
output: Default::default(),
tablet_init: dev.tablet_info(),
tablet_pad_init: dev.tablet_pad_info(),

View file

@ -15,10 +15,10 @@ use {
},
wl_surface::{tray::TrayItemId, WlSurface},
},
keyboard::KeyboardState,
rect::Rect,
renderer::Renderer,
utils::numcell::NumCell,
xkbcommon::KeyboardState,
},
jay_config::Direction as JayDirection,
std::{
@ -189,7 +189,7 @@ pub trait Node: 'static {
seat: &WlSeatGlobal,
time_usec: u64,
key: u32,
state: u32,
state: KeyState,
kb_state: &KeyboardState,
) {
let _ = seat;

View file

@ -57,7 +57,6 @@ pub mod timer;
pub mod toplevel_identifier;
pub mod transform_ext;
pub mod tri;
pub mod trim;
pub mod unlink_on_drop;
pub mod vec_ext;
pub mod vecdeque_ext;

View file

@ -1,5 +1,5 @@
use {
crate::utils::{errorfmt::ErrorFmt, oserror::OsError, trim::AsciiTrim},
crate::utils::{errorfmt::ErrorFmt, oserror::OsError},
bstr::ByteSlice,
uapi::{c, OwnedFd},
};
@ -12,7 +12,7 @@ pub struct PidInfo {
pub fn get_pid_info(uid: c::uid_t, pid: c::pid_t) -> PidInfo {
let comm = match std::fs::read(format!("/proc/{}/comm", pid)) {
Ok(name) => name.trim().as_bstr().to_string(),
Ok(name) => name.trim_ascii_end().as_bstr().to_string(),
Err(e) => {
log::warn!("Could not read `comm` of pid {}: {}", pid, ErrorFmt(e));
"Unknown".to_string()

View file

@ -29,6 +29,13 @@ impl<T> SyncQueue<T> {
}
}
pub fn append(&self, src: &mut Vec<T>) {
unsafe {
self.el.get().deref_mut().extend(src.drain(..));
}
}
#[inline]
pub fn pop(&self) -> Option<T> {
unsafe { self.el.get().deref_mut().pop_front() }
}

View file

@ -1,33 +0,0 @@
pub trait AsciiTrim {
fn trim(&self) -> &[u8];
fn trim_start(&self) -> &[u8];
fn trim_end(&self) -> &[u8];
}
impl AsciiTrim for [u8] {
fn trim(&self) -> &[u8] {
self.trim_start().trim_end()
}
fn trim_start(&self) -> &[u8] {
let mut s = self;
while let Some((b, r)) = s.split_first() {
if !matches!(*b, b' ' | b'\t' | b'\n') {
break;
}
s = r;
}
s
}
fn trim_end(&self) -> &[u8] {
let mut s = self;
while let Some((b, r)) = s.split_last() {
if !matches!(*b, b' ' | b'\t' | b'\n') {
break;
}
s = r;
}
s
}
}

View file

@ -19,7 +19,6 @@ impl<T> Deref for VecSet<T> {
}
impl<T> VecSet<T> {
#[expect(dead_code)]
pub fn clear(&mut self) {
self.vec.clear();
}
@ -43,4 +42,17 @@ impl<T: PartialEq> VecSet<T> {
}
false
}
pub fn pop(&mut self) -> Option<T> {
self.vec.pop()
}
pub fn extend(&mut self, vals: &[T])
where
T: Copy,
{
for v in vals.iter().copied() {
self.insert(v);
}
}
}

View file

@ -3,7 +3,7 @@
use {
crate::{
utils::{bitflags::BitflagsExt, oserror::OsError, trim::AsciiTrim},
utils::{bitflags::BitflagsExt, oserror::OsError},
video::drm::{
DrmBlob, DrmCardResources, DrmConnector, DrmConnectorInfo, DrmCrtc, DrmEncoder,
DrmEncoderInfo, DrmError, DrmFb, DrmModeInfo, DrmPlane, DrmPlaneInfo, DrmProperty,
@ -163,7 +163,7 @@ pub fn get_device_name_from_fd2(fd: c::c_int) -> Result<Ustring, OsError> {
break;
}
if let Some(pf) = buf.strip_prefix(b"DEVNAME=") {
return Ok(uapi::format_ustr!("/dev/{}", pf.trim_end().as_bstr()));
return Ok(uapi::format_ustr!("/dev/{}", pf.trim_ascii_end().as_bstr()));
}
}
Err(OsError(c::ENOENT))

View file

@ -1,427 +0,0 @@
#![allow(non_camel_case_types, improper_ctypes)]
mod consts;
include!(concat!(env!("OUT_DIR"), "/xkbcommon_tys.rs"));
pub use consts::*;
use {
crate::utils::{
errorfmt::ErrorFmt, oserror::OsError, ptr_ext::PtrExt, trim::AsciiTrim, vecset::VecSet,
},
bstr::{BStr, ByteSlice},
isnt::std_1::primitive::IsntConstPtrExt,
std::{
cell::{Ref, RefCell},
ffi::CStr,
io::Write,
ops::Deref,
ptr,
rc::Rc,
},
thiserror::Error,
uapi::{c, Errno, OwnedFd},
};
#[derive(Debug, Error)]
pub enum XkbCommonError {
#[error("Could not create an xkbcommon context")]
CreateContext,
#[error("Could not create an xkbcommon state")]
CreateState,
#[error("Could not create keymap from buffer")]
KeymapFromBuffer,
#[error("Could not convert the keymap to a string")]
AsStr,
#[error("Could not create a keymap memfd")]
KeymapMemfd(#[source] OsError),
#[error("Could not copy the keymap")]
KeymapCopy(#[source] OsError),
}
struct xkb_context;
struct xkb_keymap;
struct xkb_state;
type xkb_keycode_t = u32;
type xkb_layout_index_t = u32;
type xkb_level_index_t = u32;
type xkb_keysym_t = u32;
type xkb_mod_mask_t = u32;
#[repr(C)]
struct xkb_rule_names {
rules: *const c::c_char,
model: *const c::c_char,
layout: *const c::c_char,
variant: *const c::c_char,
options: *const c::c_char,
}
impl Default for xkb_rule_names {
fn default() -> Self {
Self {
rules: ptr::null(),
model: ptr::null(),
layout: ptr::null(),
variant: ptr::null(),
options: ptr::null(),
}
}
}
#[link(name = "xkbcommon")]
unsafe extern "C" {
fn xkb_context_new(flags: xkb_context_flags) -> *mut xkb_context;
fn xkb_context_unref(context: *mut xkb_context);
fn xkb_context_set_log_verbosity(context: *mut xkb_context, verbosity: c::c_int);
fn xkb_context_set_log_fn(context: *mut xkb_context, log_fn: unsafe extern "C" fn());
fn xkb_keymap_new_from_buffer(
context: *mut xkb_context,
buffer: *const u8,
length: usize,
format: xkb_keymap_format,
flags: xkb_keymap_compile_flags,
) -> *mut xkb_keymap;
fn xkb_keymap_get_as_string(
keymap: *mut xkb_keymap,
format: xkb_keymap_format,
) -> *mut c::c_char;
fn xkb_keymap_unref(keymap: *mut xkb_keymap);
// fn xkb_keymap_ref(keymap: *mut xkb_keymap) -> *mut xkb_keymap;
fn xkb_keymap_key_get_syms_by_level(
keymap: *mut xkb_keymap,
key: xkb_keycode_t,
layout: xkb_layout_index_t,
level: xkb_level_index_t,
syms_out: *mut *const xkb_keysym_t,
) -> c::c_int;
fn xkb_state_unref(state: *mut xkb_state);
fn xkb_state_new(keymap: *mut xkb_keymap) -> *mut xkb_state;
fn xkb_state_update_key(
state: *mut xkb_state,
key: u32,
direction: xkb_key_direction,
) -> xkb_state_component;
fn xkb_state_serialize_mods(state: *mut xkb_state, components: xkb_state_component) -> u32;
fn xkb_state_serialize_layout(state: *mut xkb_state, components: xkb_state_component) -> u32;
fn xkb_state_update_mask(
state: *mut xkb_state,
depressed_mods: xkb_mod_mask_t,
latched_mods: xkb_mod_mask_t,
locked_mods: xkb_mod_mask_t,
depressed_layout: xkb_layout_index_t,
latched_layout: xkb_layout_index_t,
locked_layout: xkb_layout_index_t,
) -> xkb_state_component;
}
pub struct XkbContext {
context: *mut xkb_context,
ids: KeymapIds,
}
unsafe extern "C" {
fn jay_xkbcommon_log_handler_bridge();
}
linear_ids!(KeymapIds, KeymapId, u64);
impl XkbContext {
pub fn new() -> Result<Self, XkbCommonError> {
let res = unsafe { xkb_context_new(XKB_CONTEXT_NO_FLAGS.raw() as _) };
if res.is_null() {
return Err(XkbCommonError::CreateContext);
}
unsafe {
xkb_context_set_log_verbosity(res, 10);
xkb_context_set_log_fn(res, jay_xkbcommon_log_handler_bridge);
}
Ok(Self {
context: res,
ids: Default::default(),
})
}
fn raw_to_map(&self, raw: *mut xkb_keymap) -> Result<Rc<XkbKeymap>, XkbCommonError> {
let res = unsafe { xkb_keymap_get_as_string(raw, XKB_KEYMAP_FORMAT_TEXT_V1.raw() as _) };
if res.is_null() {
unsafe {
xkb_keymap_unref(raw);
}
return Err(XkbCommonError::AsStr);
}
let str = XkbKeymapStr {
s: unsafe { CStr::from_ptr(res).to_bytes().as_bstr() },
};
let mut memfd =
uapi::memfd_create("keymap", c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING).unwrap();
memfd.write_all(str.as_bytes()).unwrap();
memfd.write_all(&[0]).unwrap();
uapi::lseek(memfd.raw(), 0, c::SEEK_SET).unwrap();
uapi::fcntl_add_seals(
memfd.raw(),
c::F_SEAL_SEAL | c::F_SEAL_GROW | c::F_SEAL_SHRINK | c::F_SEAL_WRITE,
)
.unwrap();
Ok(Rc::new(XkbKeymap {
id: self.ids.next(),
keymap: raw,
map: Rc::new(memfd),
map_len: str.len() + 1,
}))
}
pub fn keymap_from_str<S>(&self, s: &S) -> Result<Rc<XkbKeymap>, XkbCommonError>
where
S: AsRef<[u8]> + ?Sized,
{
let s = s.as_ref();
unsafe {
let keymap = xkb_keymap_new_from_buffer(
self.context,
s.as_ptr(),
s.len(),
XKB_KEYMAP_FORMAT_TEXT_V1.raw(),
0,
);
if keymap.is_null() {
return Err(XkbCommonError::KeymapFromBuffer);
}
self.raw_to_map(keymap)
}
}
}
impl Drop for XkbContext {
fn drop(&mut self) {
unsafe {
xkb_context_unref(self.context);
}
}
}
pub struct XkbKeymap {
pub id: KeymapId,
keymap: *mut xkb_keymap,
pub map: Rc<OwnedFd>,
pub map_len: usize,
}
impl XkbKeymap {
pub fn state(self: &Rc<Self>, id: KeyboardStateId) -> Result<XkbState, XkbCommonError> {
let res = unsafe { xkb_state_new(self.keymap) };
if res.is_null() {
return Err(XkbCommonError::CreateState);
}
Ok(XkbState {
map: self.clone(),
state: res,
kb_state: KeyboardState {
id,
map: self.map.clone(),
map_len: self.map_len,
pressed_keys: Default::default(),
mods: Default::default(),
},
})
}
}
impl Drop for XkbKeymap {
fn drop(&mut self) {
unsafe {
xkb_keymap_unref(self.keymap);
}
}
}
pub struct XkbKeymapStr {
s: *const BStr,
}
impl Deref for XkbKeymapStr {
type Target = BStr;
fn deref(&self) -> &Self::Target {
unsafe { self.s.deref() }
}
}
impl Drop for XkbKeymapStr {
fn drop(&mut self) {
unsafe { c::free(self.s as _) }
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct ModifierState {
pub mods_depressed: u32,
pub mods_latched: u32,
pub mods_locked: u32,
pub mods_effective: u32,
pub group: u32,
}
linear_ids!(KeyboardStateIds, KeyboardStateId, u64);
pub struct KeyboardState {
pub id: KeyboardStateId,
pub map: Rc<OwnedFd>,
pub map_len: usize,
pub pressed_keys: VecSet<u32>,
pub mods: ModifierState,
}
pub trait DynKeyboardState {
fn borrow(&self) -> Ref<'_, KeyboardState>;
}
impl DynKeyboardState for RefCell<KeyboardState> {
fn borrow(&self) -> Ref<'_, KeyboardState> {
self.borrow()
}
}
pub struct XkbState {
map: Rc<XkbKeymap>,
state: *mut xkb_state,
pub kb_state: KeyboardState,
}
impl DynKeyboardState for RefCell<XkbState> {
fn borrow(&self) -> Ref<'_, KeyboardState> {
Ref::map(self.borrow(), |v| &v.kb_state)
}
}
impl KeyboardState {
pub fn create_new_keymap_fd(&self) -> Result<Rc<OwnedFd>, XkbCommonError> {
let fd = match uapi::memfd_create("shared-keymap", c::MFD_CLOEXEC) {
Ok(fd) => fd,
Err(e) => return Err(XkbCommonError::KeymapMemfd(e.into())),
};
let target = self.map_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);
match res {
Ok(_) | Err(Errno(c::EINTR)) => {}
Err(e) => return Err(XkbCommonError::KeymapCopy(e.into())),
}
}
Ok(Rc::new(fd))
}
}
impl XkbState {
pub fn mods(&self) -> ModifierState {
self.kb_state.mods
}
fn fetch(&mut self, changes: xkb_state_component) -> bool {
unsafe {
if changes != 0 {
self.kb_state.mods.mods_depressed =
xkb_state_serialize_mods(self.state, XKB_STATE_MODS_DEPRESSED.raw() as _);
self.kb_state.mods.mods_latched =
xkb_state_serialize_mods(self.state, XKB_STATE_MODS_LATCHED.raw() as _);
self.kb_state.mods.mods_locked =
xkb_state_serialize_mods(self.state, XKB_STATE_MODS_LOCKED.raw() as _);
self.kb_state.mods.mods_effective = self.kb_state.mods.mods_depressed
| self.kb_state.mods.mods_latched
| self.kb_state.mods.mods_locked;
self.kb_state.mods.group =
xkb_state_serialize_layout(self.state, XKB_STATE_LAYOUT_EFFECTIVE.raw() as _);
true
} else {
false
}
}
}
pub fn update(&mut self, key: u32, direction: XkbKeyDirection) -> bool {
unsafe {
let changes = xkb_state_update_key(self.state, key + 8, direction.raw() as _);
self.fetch(changes)
}
}
pub fn reset(&mut self) {
let new_state = match self.map.state(self.kb_state.id) {
Ok(s) => s,
Err(e) => {
log::error!("Could not reset XKB state: {}", ErrorFmt(e));
return;
}
};
*self = new_state;
}
#[expect(dead_code)]
pub fn set(
&mut self,
mods_depressed: u32,
mods_latched: u32,
mods_locked: u32,
group: u32,
) -> bool {
unsafe {
let changes = xkb_state_update_mask(
self.state,
mods_depressed,
mods_latched,
mods_locked,
0,
0,
group,
);
self.fetch(changes)
}
}
pub fn unmodified_keysyms(&self, key: u32) -> &[xkb_keysym_t] {
let mut res = ptr::null();
unsafe {
let num = xkb_keymap_key_get_syms_by_level(
self.map.keymap,
key + 8,
self.kb_state.mods.group,
0,
&mut res,
);
if num > 0 {
std::slice::from_raw_parts(res, num as usize)
} else {
&[]
}
}
}
}
impl Drop for XkbState {
fn drop(&mut self) {
unsafe {
xkb_state_unref(self.state);
}
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn jay_xkbcommon_log_handler(
_ctx: *mut xkb_context,
level: xkb_log_level,
line: *const c::c_char,
) {
assert!(line.is_not_null());
let buf = unsafe { CStr::from_ptr(line) };
let level = match XkbLogLevel(level) {
XKB_LOG_LEVEL_CRITICAL | XKB_LOG_LEVEL_ERROR => log::Level::Error,
XKB_LOG_LEVEL_WARNING => log::Level::Warn,
XKB_LOG_LEVEL_INFO => log::Level::Info,
XKB_LOG_LEVEL_DEBUG => log::Level::Debug,
_ => log::Level::Error,
};
log::log!(level, "xkbcommon: {}", buf.to_bytes().trim_end().as_bstr());
}

View file

@ -1,58 +0,0 @@
#![allow(dead_code)]
cenum! {
XkbLogLevel, XKB_LOG_LEVEL;
XKB_LOG_LEVEL_CRITICAL = 10,
XKB_LOG_LEVEL_ERROR = 20,
XKB_LOG_LEVEL_WARNING = 30,
XKB_LOG_LEVEL_INFO = 40,
XKB_LOG_LEVEL_DEBUG = 50,
}
cenum! {
XkbContextFlags, XKB_CONTEXT_FLAGS;
XKB_CONTEXT_NO_FLAGS = 0,
XKB_CONTEXT_NO_DEFAULT_INCLUDES = 1 << 0,
XKB_CONTEXT_NO_ENVIRONMENT_NAMES = 1 << 1,
}
bitor!(XkbContextFlags);
cenum! {
XkbKeymapCompileFlags, XKB_KEYMAP_COMPILE_FLAGS;
XKB_KEYMAP_COMPILE_NO_FLAGS = 0,
}
bitor!(XkbKeymapCompileFlags);
cenum! {
XkbKeymapFormat, XKB_KEYMAP_FORMAT;
XKB_KEYMAP_FORMAT_TEXT_V1 = 1,
}
cenum! {
XkbStateComponent, XKB_STATE_COMPONENT;
XKB_STATE_MODS_DEPRESSED = 1 << 0,
XKB_STATE_MODS_LATCHED = 1 << 1,
XKB_STATE_MODS_LOCKED = 1 << 2,
XKB_STATE_MODS_EFFECTIVE = 1 << 3,
XKB_STATE_LAYOUT_DEPRESSED = 1 << 4,
XKB_STATE_LAYOUT_LATCHED = 1 << 5,
XKB_STATE_LAYOUT_LOCKED = 1 << 6,
XKB_STATE_LAYOUT_EFFECTIVE = 1 << 7,
XKB_STATE_LEDS = 1 << 8,
}
bitor!(XkbStateComponent);
cenum! {
XkbKeyDirection, XKB_KEY_DIRECTION;
XKB_KEY_UP = 0,
XKB_KEY_DOWN = 1,
}