Merge pull request #345 from mahkoh/jorth/kbvm
keyboard: replace xkbcommon by kbvm
This commit is contained in:
commit
4e23f4824e
41 changed files with 794 additions and 892 deletions
71
Cargo.lock
generated
71
Cargo.lock
generated
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
# Unreleased
|
||||
|
||||
- Needs jay-compositor release.
|
||||
|
||||
# 1.8.0
|
||||
|
||||
- Needs jay-config release.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
17
src/bridge.c
17
src/bridge.c
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
})),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
222
src/kbvm.rs
Normal 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
67
src/keyboard.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,6 +249,7 @@ macro_rules! cenum {
|
|||
}
|
||||
}
|
||||
|
||||
#[expect(unused_macros)]
|
||||
macro_rules! bitor {
|
||||
($name:ident) => {
|
||||
impl std::ops::BitOr for $name {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
14
src/state.rs
14
src/state.rs
|
|
@ -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>>,
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
427
src/xkbcommon.rs
427
src/xkbcommon.rs
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue