1
0
Fork 0
forked from wry/wry

seat: implement per-device keymaps

This commit is contained in:
Julian Orth 2024-04-12 17:06:16 +02:00
parent 225995eb2f
commit 826f40adca
21 changed files with 293 additions and 71 deletions

View file

@ -1,5 +1,9 @@
# Unreleased # Unreleased
- Needs jay-config release.
- Needs jay-toml-config release.
- Needs jay-compositor release.
# 1.0.3 # 1.0.3
- Needs jay-compositor release. - Needs jay-compositor release.

View file

@ -818,6 +818,10 @@ impl Client {
self.send(&ClientMessage::SetSeat { device, seat }) self.send(&ClientMessage::SetSeat { device, seat })
} }
pub fn set_device_keymap(&self, device: InputDevice, keymap: Keymap) {
self.send(&ClientMessage::DeviceSetKeymap { device, keymap })
}
pub fn set_left_handed(&self, device: InputDevice, left_handed: bool) { pub fn set_left_handed(&self, device: InputDevice, left_handed: bool) {
self.send(&ClientMessage::SetLeftHanded { self.send(&ClientMessage::SetLeftHanded {
device, device,

View file

@ -432,6 +432,10 @@ pub enum ClientMessage<'a> {
enabled: bool, enabled: bool,
}, },
GetSocketPath, GetSocketPath,
DeviceSetKeymap {
device: InputDevice,
keymap: Keymap,
},
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]

View file

@ -25,6 +25,16 @@ impl InputDevice {
get!().set_seat(self, seat) get!().set_seat(self, seat)
} }
/// Sets the keymap of the device.
///
/// This overrides the keymap set for the seat. The keymap becomes active when a key
/// on the device is pressed.
///
/// Setting the invalid keymap reverts to the seat keymap.
pub fn set_keymap(self, keymap: Keymap) {
get!().set_device_keymap(self, keymap)
}
/// Returns whether the device has the specified capability. /// Returns whether the device has the specified capability.
pub fn has_capability(self, cap: Capability) -> bool { pub fn has_capability(self, cap: Capability) -> bool {
get!(false).has_capability(self, cap) get!(false).has_capability(self, cap)

View file

@ -1,6 +1,7 @@
# Unreleased # Unreleased
- Add support for wp-alpha-modifier. - Add support for wp-alpha-modifier.
- Add support for per-device keymaps.
# 1.0.3 (2024-04-11) # 1.0.3 (2024-04-11)

View file

@ -19,7 +19,7 @@ use {
ops::DerefMut, ops::DerefMut,
rc::Rc, rc::Rc,
}, },
uapi::c, uapi::{c, OwnedFd},
}; };
#[derive(Args, Debug)] #[derive(Args, Debug)]
@ -119,6 +119,10 @@ pub enum DeviceCommand {
SetPxPerWheelScroll(SetPxPerWheelScrollArgs), SetPxPerWheelScroll(SetPxPerWheelScrollArgs),
/// Set the transformation matrix. /// Set the transformation matrix.
SetTransformMatrix(SetTransformMatrixArgs), SetTransformMatrix(SetTransformMatrixArgs),
/// Set the keymap of this device.
SetKeymap(SetKeymapArgs),
/// Retrieve the keymap of this device.
Keymap,
/// Attach the device to a seat. /// Attach the device to a seat.
Attach(AttachArgs), Attach(AttachArgs),
/// Detach the device from its seat. /// Detach the device from its seat.
@ -292,6 +296,47 @@ impl Input {
}); });
} }
fn prepare_keymap(&self, a: &SetKeymapArgs) -> (Rc<OwnedFd>, usize) {
let map = match &a.file {
None => {
let mut map = vec![];
if let Err(e) = stdin().read_to_end(&mut map) {
eprintln!("Could not read from stdin: {}", ErrorFmt(e));
std::process::exit(1);
}
map
}
Some(f) => match std::fs::read(f) {
Ok(m) => m,
Err(e) => {
eprintln!("Could not read {}: {}", f, ErrorFmt(e));
std::process::exit(1);
}
},
};
let mut memfd =
uapi::memfd_create("keymap", c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING).unwrap();
memfd.write_all(&map).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();
(Rc::new(memfd), map.len())
}
async fn handle_keymap(&self, input: JayInputId) -> Vec<u8> {
let data = Rc::new(RefCell::new(Vec::new()));
jay_input::Keymap::handle(&self.tc, input, data.clone(), |d, map| {
let mem = Rc::new(ClientMem::new(map.keymap.raw(), map.keymap_len as _, true).unwrap())
.offset(0);
mem.read(d.borrow_mut().deref_mut()).unwrap();
});
self.tc.round_trip().await;
data.take()
}
async fn seat(self: &Rc<Self>, input: JayInputId, args: SeatArgs) { async fn seat(self: &Rc<Self>, input: JayInputId, args: SeatArgs) {
let tc = &self.tc; let tc = &self.tc;
match args.command.unwrap_or_default() { match args.command.unwrap_or_default() {
@ -318,40 +363,15 @@ impl Input {
}); });
} }
SeatCommand::SetKeymap(a) => { SeatCommand::SetKeymap(a) => {
let (memfd, len) = self.prepare_keymap(&a);
self.handle_error(input, |e| { self.handle_error(input, |e| {
eprintln!("Could not set keymap: {}", e); eprintln!("Could not set keymap: {}", e);
}); });
let map = match &a.file {
None => {
let mut map = vec![];
if let Err(e) = stdin().read_to_end(&mut map) {
eprintln!("Could not read from stdin: {}", ErrorFmt(e));
std::process::exit(1);
}
map
}
Some(f) => match std::fs::read(f) {
Ok(m) => m,
Err(e) => {
eprintln!("Could not read {}: {}", f, ErrorFmt(e));
std::process::exit(1);
}
},
};
let mut memfd =
uapi::memfd_create("keymap", c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING).unwrap();
memfd.write_all(&map).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();
tc.send(jay_input::SetKeymap { tc.send(jay_input::SetKeymap {
self_id: input, self_id: input,
seat: &args.seat, seat: &args.seat,
keymap: Rc::new(memfd), keymap: memfd,
keymap_len: map.len() as _, keymap_len: len as _,
}); });
} }
SeatCommand::UseHardwareCursor(a) => { SeatCommand::UseHardwareCursor(a) => {
@ -368,20 +388,11 @@ impl Input {
self.handle_error(input, |e| { self.handle_error(input, |e| {
eprintln!("Could not retrieve the keymap: {}", e); eprintln!("Could not retrieve the keymap: {}", e);
}); });
let data = Rc::new(RefCell::new(Vec::new()));
jay_input::Keymap::handle(tc, input, data.clone(), |d, map| {
let mem = Rc::new(
ClientMem::new(map.keymap.raw(), map.keymap_len as _, true).unwrap(),
)
.offset(0);
mem.read(d.borrow_mut().deref_mut()).unwrap();
});
tc.send(jay_input::GetKeymap { tc.send(jay_input::GetKeymap {
self_id: input, self_id: input,
seat: &args.seat, seat: &args.seat,
}); });
tc.round_trip().await; let map = self.handle_keymap(input).await;
let map = data.take();
stdout().write_all(&map).unwrap(); stdout().write_all(&map).unwrap();
} }
SeatCommand::SetCursorSize(a) => { SeatCommand::SetCursorSize(a) => {
@ -530,6 +541,29 @@ impl Input {
id: args.device, id: args.device,
}); });
} }
DeviceCommand::SetKeymap(a) => {
let (memfd, len) = self.prepare_keymap(&a);
self.handle_error(input, |e| {
eprintln!("Could not set keymap: {}", e);
});
tc.send(jay_input::SetDeviceKeymap {
self_id: input,
id: args.device,
keymap: memfd,
keymap_len: len as _,
});
}
DeviceCommand::Keymap => {
self.handle_error(input, |e| {
eprintln!("Could not retrieve the keymap: {}", e);
});
tc.send(jay_input::GetDeviceKeymap {
self_id: input,
id: args.device,
});
let map = self.handle_keymap(input).await;
stdout().write_all(&map).unwrap();
}
} }
tc.round_trip().await; tc.round_trip().await;
} }

View file

@ -299,7 +299,23 @@ impl ConfigProxyHandler {
} else { } else {
self.get_keymap(keymap)? self.get_keymap(keymap)?
}; };
seat.set_keymap(&keymap); seat.set_seat_keymap(&keymap);
Ok(())
}
fn handle_set_device_keymap(
&self,
device: InputDevice,
keymap: Keymap,
) -> Result<(), CphError> {
let dev = self.get_device_handler_data(device)?;
if keymap.is_invalid() {
dev.keymap_id.set(None);
dev.keymap.set(None);
} else {
let map = self.get_keymap(keymap)?;
dev.set_keymap(&map);
};
Ok(()) Ok(())
} }
@ -1746,6 +1762,9 @@ impl ConfigProxyHandler {
self.handle_set_explicit_sync_enabled(enabled) self.handle_set_explicit_sync_enabled(enabled)
} }
ClientMessage::GetSocketPath => self.handle_get_socket_path(), ClientMessage::GetSocketPath => self.handle_get_socket_path(),
ClientMessage::DeviceSetKeymap { device, keymap } => self
.handle_set_device_keymap(device, keymap)
.wrn("set_device_keymap")?,
} }
Ok(()) Ok(())
} }

View file

@ -13,10 +13,11 @@ use {
state::{DeviceHandlerData, InputDeviceData}, state::{DeviceHandlerData, InputDeviceData},
utils::errorfmt::ErrorFmt, utils::errorfmt::ErrorFmt,
wire::{jay_input::*, JayInputId}, wire::{jay_input::*, JayInputId},
xkbcommon::XkbCommonError, xkbcommon::{XkbCommonError, XkbKeymap},
}, },
std::rc::Rc, std::rc::Rc,
thiserror::Error, thiserror::Error,
uapi::OwnedFd,
}; };
pub struct JayInput { pub struct JayInput {
@ -67,8 +68,7 @@ impl JayInput {
}); });
} }
fn send_keymap(&self, data: &WlSeatGlobal) { fn send_keymap(&self, map: &XkbKeymap) {
let map = data.keymap();
self.client.event(Keymap { self.client.event(Keymap {
self_id: self.id, self_id: self.id,
keymap: map.map.clone(), keymap: map.map.clone(),
@ -138,6 +138,20 @@ impl JayInput {
Some(d) => Ok(d.data.clone()), Some(d) => Ok(d.data.clone()),
} }
} }
fn set_keymap_impl<F>(&self, keymap: &OwnedFd, len: u32, f: F) -> Result<(), JayInputError>
where
F: FnOnce(&Rc<XkbKeymap>) -> Result<(), JayInputError>,
{
let cm = Rc::new(ClientMem::new(keymap.raw(), len as _, true)?).offset(0);
let mut map = vec![];
cm.read(&mut map)?;
self.or_error(|| {
let map = self.client.state.xkb_ctx.keymap_from_str(&map)?;
f(&map)?;
Ok(())
})
}
} }
impl JayInputRequestHandler for JayInput { impl JayInputRequestHandler for JayInput {
@ -174,13 +188,9 @@ impl JayInputRequestHandler for JayInput {
} }
fn set_keymap(&self, req: SetKeymap, _slf: &Rc<Self>) -> Result<(), Self::Error> { fn set_keymap(&self, req: SetKeymap, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let cm = Rc::new(ClientMem::new(req.keymap.raw(), req.keymap_len as _, true)?).offset(0); self.set_keymap_impl(&req.keymap, req.keymap_len, |map| {
let mut map = vec![];
cm.read(&mut map)?;
self.or_error(|| {
let map = self.client.state.xkb_ctx.keymap_from_str(&map)?;
let seat = self.seat(req.seat)?; let seat = self.seat(req.seat)?;
seat.set_keymap(&map); seat.set_seat_keymap(&map);
Ok(()) Ok(())
}) })
} }
@ -200,7 +210,7 @@ impl JayInputRequestHandler for JayInput {
fn get_keymap(&self, req: GetKeymap, _slf: &Rc<Self>) -> Result<(), Self::Error> { fn get_keymap(&self, req: GetKeymap, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.or_error(|| { self.or_error(|| {
let seat = self.seat(req.seat)?; let seat = self.seat(req.seat)?;
self.send_keymap(&seat); self.send_keymap(&seat.keymap());
Ok(()) Ok(())
}) })
} }
@ -360,6 +370,24 @@ impl JayInputRequestHandler for JayInput {
} }
}) })
} }
fn set_device_keymap(&self, req: SetDeviceKeymap, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.set_keymap_impl(&req.keymap, req.keymap_len, |map| {
let dev = self.device(req.id)?;
dev.set_keymap(&map);
Ok(())
})
}
fn get_device_keymap(&self, req: GetDeviceKeymap, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.or_error(|| {
let dev = self.device(req.id)?;
if let Some(map) = dev.keymap.get() {
self.send_keymap(&map);
}
Ok(())
})
}
} }
object_base! { object_base! {

View file

@ -63,7 +63,7 @@ use {
WlSeatId, ZwlrDataControlDeviceV1Id, ZwpPrimarySelectionDeviceV1Id, WlSeatId, ZwlrDataControlDeviceV1Id, ZwpPrimarySelectionDeviceV1Id,
ZwpRelativePointerV1Id, ZwpRelativePointerV1Id,
}, },
xkbcommon::{XkbKeymap, XkbState}, xkbcommon::{KeymapId, XkbKeymap, XkbState},
}, },
ahash::AHashMap, ahash::AHashMap,
jay_config::keyboard::mods::Modifiers, jay_config::keyboard::mods::Modifiers,
@ -141,7 +141,10 @@ pub struct WlSeatGlobal {
wlr_data_devices: wlr_data_devices:
CopyHashMap<(ClientId, ZwlrDataControlDeviceV1Id), Rc<ZwlrDataControlDeviceV1>>, CopyHashMap<(ClientId, ZwlrDataControlDeviceV1Id), Rc<ZwlrDataControlDeviceV1>>,
repeat_rate: Cell<(i32, i32)>, repeat_rate: Cell<(i32, i32)>,
kb_map: CloneCell<Rc<XkbKeymap>>, seat_kb_map: CloneCell<Rc<XkbKeymap>>,
seat_kb_map_id: Cell<KeymapId>,
effective_kb_map: CloneCell<Rc<XkbKeymap>>,
effective_kb_map_id: Cell<KeymapId>,
kb_state: RefCell<XkbState>, kb_state: RefCell<XkbState>,
cursor: CloneCell<Option<Rc<dyn Cursor>>>, cursor: CloneCell<Option<Rc<dyn Cursor>>>,
tree_changed: Rc<AsyncEvent>, tree_changed: Rc<AsyncEvent>,
@ -197,7 +200,10 @@ impl WlSeatGlobal {
data_devices: RefCell::new(Default::default()), data_devices: RefCell::new(Default::default()),
primary_selection_devices: RefCell::new(Default::default()), primary_selection_devices: RefCell::new(Default::default()),
repeat_rate: Cell::new((25, 250)), repeat_rate: Cell::new((25, 250)),
kb_map: CloneCell::new(state.default_keymap.clone()), seat_kb_map: CloneCell::new(state.default_keymap.clone()),
seat_kb_map_id: Cell::new(state.default_keymap.id),
effective_kb_map: CloneCell::new(state.default_keymap.clone()),
effective_kb_map_id: Cell::new(state.default_keymap.id),
kb_state: RefCell::new(state.default_keymap.state().unwrap()), kb_state: RefCell::new(state.default_keymap.state().unwrap()),
cursor: Default::default(), cursor: Default::default(),
tree_changed: Default::default(), tree_changed: Default::default(),
@ -238,7 +244,7 @@ impl WlSeatGlobal {
} }
pub fn keymap(&self) -> Rc<XkbKeymap> { pub fn keymap(&self) -> Rc<XkbKeymap> {
self.kb_map.get() self.seat_kb_map.get()
} }
pub fn toplevel_drag(&self) -> Option<Rc<XdgToplevelDragV1>> { pub fn toplevel_drag(&self) -> Option<Rc<XdgToplevelDragV1>> {
@ -507,7 +513,12 @@ impl WlSeatGlobal {
false false
} }
pub fn set_keymap(&self, keymap: &Rc<XkbKeymap>) { pub fn set_seat_keymap(&self, keymap: &Rc<XkbKeymap>) {
self.seat_kb_map.set(keymap.clone());
self.seat_kb_map_id.set(keymap.id);
}
fn set_effective_keymap(&self, keymap: &Rc<XkbKeymap>) {
let state = match keymap.state() { let state = match keymap.state() {
Ok(s) => s, Ok(s) => s,
Err(e) => { Err(e) => {
@ -516,7 +527,8 @@ impl WlSeatGlobal {
} }
}; };
self.keyboard_node.get().node_on_unfocus(self); self.keyboard_node.get().node_on_unfocus(self);
self.kb_map.set(keymap.clone()); self.effective_kb_map.set(keymap.clone());
self.effective_kb_map_id.set(keymap.id);
*self.kb_state.borrow_mut() = state; *self.kb_state.borrow_mut() = state;
self.keymap_version.fetch_add(1); self.keymap_version.fetch_add(1);
self.pressed_keys.borrow_mut().clear(); self.pressed_keys.borrow_mut().clear();

View file

@ -196,7 +196,20 @@ impl WlSeatGlobal {
time_usec, time_usec,
key, key,
state, state,
} => self.key_event(time_usec, key, state), } => {
let desired_kb_map_id = match dev.keymap_id.get() {
Some(id) => id,
None => self.seat_kb_map_id.get(),
};
if desired_kb_map_id != self.effective_kb_map_id.get() {
let map = match dev.keymap.get() {
Some(map) => map,
None => self.seat_kb_map.get(),
};
self.set_effective_keymap(&map);
}
self.key_event(time_usec, key, state)
}
InputEvent::ConnectorPosition { InputEvent::ConnectorPosition {
time_usec, time_usec,
connector, connector,

View file

@ -38,7 +38,7 @@ impl WlKeyboard {
} }
pub fn send_keymap(&self) { pub fn send_keymap(&self) {
let map = self.seat.global.kb_map.get(); let map = self.seat.global.effective_kb_map.get();
let fd = match self.seat.keymap_fd(&map) { let fd = match self.seat.keymap_fd(&map) {
Ok(fd) => fd, Ok(fd) => fd,
Err(e) => { Err(e) => {

View file

@ -74,7 +74,7 @@ use {
ExtForeignToplevelListV1Id, JayRenderCtxId, JaySeatEventsId, JayWorkspaceWatcherId, ExtForeignToplevelListV1Id, JayRenderCtxId, JaySeatEventsId, JayWorkspaceWatcherId,
ZwpLinuxDmabufFeedbackV1Id, ZwpLinuxDmabufFeedbackV1Id,
}, },
xkbcommon::{XkbContext, XkbKeymap}, xkbcommon::{KeymapId, XkbContext, XkbKeymap},
xwayland::{self, XWaylandEvent}, xwayland::{self, XWaylandEvent},
}, },
ahash::AHashMap, ahash::AHashMap,
@ -244,6 +244,15 @@ pub struct DeviceHandlerData {
pub device: Rc<dyn InputDevice>, pub device: Rc<dyn InputDevice>,
pub syspath: Option<String>, pub syspath: Option<String>,
pub devnode: Option<String>, pub devnode: Option<String>,
pub keymap_id: Cell<Option<KeymapId>>,
pub keymap: CloneCell<Option<Rc<XkbKeymap>>>,
}
impl DeviceHandlerData {
pub fn set_keymap(&self, keymap: &Rc<XkbKeymap>) {
self.keymap_id.set(Some(keymap.id));
self.keymap.set(Some(keymap.clone()));
}
} }
pub struct ConnectorData { pub struct ConnectorData {

View file

@ -21,6 +21,8 @@ pub fn handle(state: &Rc<State>, dev: Rc<dyn InputDevice>) {
device: dev.clone(), device: dev.clone(),
syspath: props.syspath, syspath: props.syspath,
devnode: props.devnode, devnode: props.devnode,
keymap_id: Default::default(),
keymap: Default::default(),
}); });
let ae = Rc::new(AsyncEvent::default()); let ae = Rc::new(AsyncEvent::default());
let oh = DeviceHandler { let oh = DeviceHandler {

View file

@ -101,12 +101,15 @@ extern "C" {
pub struct XkbContext { pub struct XkbContext {
context: *mut xkb_context, context: *mut xkb_context,
ids: KeymapIds,
} }
extern "C" { extern "C" {
fn jay_xkbcommon_log_handler_bridge(); fn jay_xkbcommon_log_handler_bridge();
} }
linear_ids!(KeymapIds, KeymapId, u64);
impl XkbContext { impl XkbContext {
pub fn new() -> Result<Self, XkbCommonError> { pub fn new() -> Result<Self, XkbCommonError> {
let res = unsafe { xkb_context_new(XKB_CONTEXT_NO_FLAGS.raw() as _) }; let res = unsafe { xkb_context_new(XKB_CONTEXT_NO_FLAGS.raw() as _) };
@ -117,10 +120,13 @@ impl XkbContext {
xkb_context_set_log_verbosity(res, 10); xkb_context_set_log_verbosity(res, 10);
xkb_context_set_log_fn(res, jay_xkbcommon_log_handler_bridge); xkb_context_set_log_fn(res, jay_xkbcommon_log_handler_bridge);
} }
Ok(Self { context: res }) Ok(Self {
context: res,
ids: Default::default(),
})
} }
fn raw_to_map(raw: *mut xkb_keymap) -> Result<Rc<XkbKeymap>, XkbCommonError> { 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 _) }; let res = unsafe { xkb_keymap_get_as_string(raw, XKB_KEYMAP_FORMAT_TEXT_V1.raw() as _) };
if res.is_null() { if res.is_null() {
unsafe { unsafe {
@ -142,6 +148,7 @@ impl XkbContext {
) )
.unwrap(); .unwrap();
Ok(Rc::new(XkbKeymap { Ok(Rc::new(XkbKeymap {
id: self.ids.next(),
keymap: raw, keymap: raw,
map: Rc::new(memfd), map: Rc::new(memfd),
map_len: str.len() + 1, map_len: str.len() + 1,
@ -164,7 +171,7 @@ impl XkbContext {
if keymap.is_null() { if keymap.is_null() {
return Err(XkbCommonError::KeymapFromBuffer); return Err(XkbCommonError::KeymapFromBuffer);
} }
Self::raw_to_map(keymap) self.raw_to_map(keymap)
} }
} }
} }
@ -178,6 +185,7 @@ impl Drop for XkbContext {
} }
pub struct XkbKeymap { pub struct XkbKeymap {
pub id: KeymapId,
keymap: *mut xkb_keymap, keymap: *mut xkb_keymap,
pub map: Rc<OwnedFd>, pub map: Rc<OwnedFd>,
pub map_len: usize, pub map_len: usize,

View file

@ -241,6 +241,7 @@ pub struct Input {
pub natural_scrolling: Option<bool>, pub natural_scrolling: Option<bool>,
pub px_per_wheel_scroll: Option<f64>, pub px_per_wheel_scroll: Option<f64>,
pub transform_matrix: Option<[[f64; 2]; 2]>, pub transform_matrix: Option<[[f64; 2]; 2]>,
pub keymap: Option<ConfigKeymap>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -4,7 +4,10 @@ use {
context::Context, context::Context,
extractor::{bol, fltorint, opt, recover, str, val, Extractor, ExtractorError}, extractor::{bol, fltorint, opt, recover, str, val, Extractor, ExtractorError},
parser::{DataType, ParseResult, Parser, UnexpectedDataType}, parser::{DataType, ParseResult, Parser, UnexpectedDataType},
parsers::input_match::{InputMatchParser, InputMatchParserError}, parsers::{
input_match::{InputMatchParser, InputMatchParserError},
keymap::KeymapParser,
},
Input, Input,
}, },
toml::{ toml::{
@ -62,7 +65,7 @@ impl<'a> Parser for InputParser<'a> {
natural_scrolling, natural_scrolling,
px_per_wheel_scroll, px_per_wheel_scroll,
), ),
(transform_matrix,), (transform_matrix, keymap),
) = ext.extract(( ) = ext.extract((
( (
opt(str("tag")), opt(str("tag")),
@ -76,7 +79,7 @@ impl<'a> Parser for InputParser<'a> {
recover(opt(bol("natural-scrolling"))), recover(opt(bol("natural-scrolling"))),
recover(opt(fltorint("px-per-wheel-scroll"))), recover(opt(fltorint("px-per-wheel-scroll"))),
), ),
(recover(opt(val("transform-matrix"))),), (recover(opt(val("transform-matrix"))), opt(val("keymap"))),
))?; ))?;
let accel_profile = match accel_profile { let accel_profile = match accel_profile {
None => None, None => None,
@ -109,6 +112,19 @@ impl<'a> Parser for InputParser<'a> {
); );
} }
} }
let keymap = match keymap {
None => None,
Some(map) => match map.parse(&mut KeymapParser {
cx: self.cx,
definition: false,
}) {
Ok(v) => Some(v),
Err(e) => {
log::warn!("Could not parse keymap: {}", self.cx.error(e));
None
}
},
};
Ok(Input { Ok(Input {
tag: tag.despan_into(), tag: tag.despan_into(),
match_: match_val.parse_map(&mut InputMatchParser(self.cx))?, match_: match_val.parse_map(&mut InputMatchParser(self.cx))?,
@ -121,6 +137,7 @@ impl<'a> Parser for InputParser<'a> {
natural_scrolling: natural_scrolling.despan(), natural_scrolling: natural_scrolling.despan(),
px_per_wheel_scroll: px_per_wheel_scroll.despan(), px_per_wheel_scroll: px_per_wheel_scroll.despan(),
transform_matrix, transform_matrix,
keymap,
}) })
} }
} }

View file

@ -92,7 +92,7 @@ impl Action {
Box::new(move || { Box::new(move || {
for c in input_devices() { for c in input_devices() {
if input.match_.matches(c, &state) { if input.match_.matches(c, &state) {
input.apply(c); input.apply(c, &state);
} }
} }
}) })
@ -361,7 +361,7 @@ impl InputMatch {
} }
impl Input { impl Input {
fn apply(&self, c: InputDevice) { fn apply(&self, c: InputDevice, state: &State) {
if let Some(v) = self.accel_profile { if let Some(v) = self.accel_profile {
c.set_accel_profile(v); c.set_accel_profile(v);
} }
@ -389,6 +389,11 @@ impl Input {
if let Some(v) = self.transform_matrix { if let Some(v) = self.transform_matrix {
c.set_transform_matrix(v); c.set_transform_matrix(v);
} }
if let Some(v) = &self.keymap {
if let Some(km) = state.get_keymap(v) {
c.set_keymap(km);
}
}
} }
} }
@ -554,19 +559,25 @@ impl State {
} }
} }
fn set_keymap(&self, map: &ConfigKeymap) { fn get_keymap(&self, map: &ConfigKeymap) -> Option<Keymap> {
let map = match map { let map = match map {
ConfigKeymap::Named(n) => match self.keymaps.get(n) { ConfigKeymap::Named(n) => match self.keymaps.get(n) {
None => { None => {
log::warn!("Unknown keymap {n}"); log::warn!("Unknown keymap {n}");
return; return None;
} }
Some(m) => *m, Some(m) => *m,
}, },
ConfigKeymap::Defined { map, .. } => *map, ConfigKeymap::Defined { map, .. } => *map,
ConfigKeymap::Literal(map) => *map, ConfigKeymap::Literal(map) => *map,
}; };
self.persistent.seat.set_keymap(map); Some(map)
}
fn set_keymap(&self, map: &ConfigKeymap) {
if let Some(map) = self.get_keymap(map) {
self.persistent.seat.set_keymap(map);
}
} }
fn set_status(&self, status: &Option<Status>) { fn set_status(&self, status: &Option<Status>) {
@ -816,7 +827,7 @@ fn load_config(initial_load: bool, persistent: &Rc<PersistentState>) {
move |c| { move |c| {
for input in &config.inputs { for input in &config.inputs {
if input.match_.matches(c, &state) { if input.match_.matches(c, &state) {
input.apply(c); input.apply(c, &state);
} }
} }
} }

View file

@ -790,6 +790,10 @@
"description": "" "description": ""
} }
} }
},
"keymap": {
"description": "The keymap to use for this device.\n\nThis overrides the global keymap. The keymap becomes active when a key is pressed.\n\n- Example:\n\n ```toml\n [[inputs]]\n match.name = \"ZSA Technology Labs Inc ErgoDox EZ\"\n keymap.name = \"external\"\n ```\n",
"$ref": "#/$defs/Keymap"
} }
}, },
"required": [ "required": [

View file

@ -1519,6 +1519,22 @@ The table has the following fields:
The value of this field should be an array of arrays of numbers. The value of this field should be an array of arrays of numbers.
- `keymap` (optional):
The keymap to use for this device.
This overrides the global keymap. The keymap becomes active when a key is pressed.
- Example:
```toml
[[inputs]]
match.name = "ZSA Technology Labs Inc ErgoDox EZ"
keymap.name = "external"
```
The value of this field should be a [Keymap](#types-Keymap).
<a name="types-InputMatch"></a> <a name="types-InputMatch"></a>
### `InputMatch` ### `InputMatch`

View file

@ -1206,6 +1206,21 @@ Input:
match.is-pointer = true match.is-pointer = true
transform-matrix = [[0.35, 0], [0, 0.35]] transform-matrix = [[0.35, 0], [0, 0.35]]
``` ```
keymap:
ref: Keymap
required: false
description: |
The keymap to use for this device.
This overrides the global keymap. The keymap becomes active when a key is pressed.
- Example:
```toml
[[inputs]]
match.name = "ZSA Technology Labs Inc ErgoDox EZ"
keymap.name = "external"
```
AccelProfile: AccelProfile:

View file

@ -99,6 +99,16 @@ request get_device {
id: u32, id: u32,
} }
request set_device_keymap {
id: u32,
keymap: fd,
keymap_len: u32,
}
request get_device_keymap {
id: u32,
}
# events # events
event seat { event seat {