diff --git a/src/input/keyboard/mod.rs b/src/input/keyboard/mod.rs index 1c00d0c2eead..76cbab37f3cf 100644 --- a/src/input/keyboard/mod.rs +++ b/src/input/keyboard/mod.rs @@ -269,6 +269,41 @@ impl KbdInternal { }) } + fn new_from_keymap_string( + keymap_str: String, + repeat_rate: i32, + repeat_delay: i32, + ) -> Result, ()> { + let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); + let keymap = xkb::Keymap::new_from_string( + &context, + keymap_str, + xkb::KEYMAP_FORMAT_TEXT_V1, + xkb::KEYMAP_COMPILE_NO_FLAGS, + ) + .ok_or(())?; + let state = xkb::State::new(&keymap); + let led_mapping = LedMapping::from_keymap(&keymap); + let led_state = LedState::from_state(&state, &led_mapping); + Ok(KbdInternal { + focus: None, + pending_focus: None, + pressed_keys: HashSet::new(), + forwarded_pressed_keys: HashSet::new(), + mods_state: ModifiersState::default(), + xkb: Arc::new(Mutex::new(Xkb { + context, + keymap, + state, + })), + repeat_rate, + repeat_delay, + led_mapping, + led_state, + grab: GrabStatus::None, + }) + } + // returns whether the modifiers or led state has changed fn key_input(&mut self, keycode: Keycode, state: KeyState) -> (bool, bool) { // track pressed keys as xkbcommon does not seem to expose it :( @@ -702,6 +737,49 @@ impl KeyboardHandle { }) } + /// Create a keyboard handler from a pre-compiled keymap string + pub(crate) fn new_from_keymap_string( + keymap_str: String, + repeat_delay: i32, + repeat_rate: i32, + ) -> Result { + let span = info_span!("input_keyboard"); + let _guard = span.enter(); + + info!("Initializing a xkbcommon handler with keymap string"); + let internal = + KbdInternal::new_from_keymap_string(keymap_str, repeat_rate, repeat_delay).map_err(|_| { + debug!("Loading keymap from string failed"); + Error::BadKeymap + })?; + + let xkb = internal.xkb.lock().unwrap(); + + info!(name = xkb.keymap.layouts().next(), "Loaded Keymap"); + + #[cfg(feature = "wayland_frontend")] + let keymap_file = KeymapFile::new(&xkb.keymap); + #[cfg(feature = "wayland_frontend")] + let active_keymap = keymap_file.id(); + + drop(xkb); + drop(_guard); + Ok(Self { + arc: Arc::new(KbdRc { + #[cfg(feature = "wayland_frontend")] + keymap: Mutex::new(keymap_file), + internal: Mutex::new(internal), + #[cfg(feature = "wayland_frontend")] + known_kbds: Mutex::new(Vec::new()), + #[cfg(feature = "wayland_frontend")] + last_enter: Mutex::new(None), + #[cfg(feature = "wayland_frontend")] + active_keymap: RwLock::new(active_keymap), + span, + }), + }) + } + #[cfg(feature = "wayland_frontend")] #[instrument(parent = &self.arc.span, skip(self, data, keymap))] pub(crate) fn change_keymap( diff --git a/src/input/mod.rs b/src/input/mod.rs index c499c69f5f0e..a4052931dc45 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -579,6 +579,33 @@ impl Seat { Ok(keyboard) } + /// Add a keyboard with a pre-compiled keymap string (XKB_KEYMAP_FORMAT_TEXT_V1). + /// + /// This bypasses the RMLVO rules lookup, so it does not require xkeyboard-config + /// data files on disk. + pub fn add_keyboard_from_keymap_string( + &mut self, + keymap_str: String, + repeat_delay: i32, + repeat_rate: i32, + ) -> Result, KeyboardError> { + let mut inner = self.arc.inner.lock().unwrap(); + let keyboard = self::keyboard::KeyboardHandle::new_from_keymap_string( + keymap_str, + repeat_delay, + repeat_rate, + )?; + if inner.keyboard.is_some() { + inner.keyboard = None; + #[cfg(feature = "wayland_frontend")] + inner.send_all_caps(); + } + inner.keyboard = Some(keyboard.clone()); + #[cfg(feature = "wayland_frontend")] + inner.send_all_caps(); + Ok(keyboard) + } + /// Access the keyboard of this seat if any pub fn get_keyboard(&self) -> Option> { self.arc.inner.lock().unwrap().keyboard.clone()