diff --git a/src/input/keyboard/mod.rs b/src/input/keyboard/mod.rs index d0c5618e39ac..6180e53b639a 100644 --- a/src/input/keyboard/mod.rs +++ b/src/input/keyboard/mod.rs @@ -90,26 +90,9 @@ where unsafe impl Send for KbdInternal {} impl KbdInternal { - fn new(xkb_config: XkbConfig<'_>, repeat_rate: i32, repeat_delay: i32) -> Result, ()> { - // we create a new contex for each keyboard because libxkbcommon is actually NOT threadsafe - // so confining it inside the KbdInternal allows us to use Rusts mutability rules to make - // sure nothing goes wrong. - // - // FIXME: This is an issue with the xkbcommon-rs crate that does not reflect this - // non-threadsafety properly. - let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); - let keymap = xkb::Keymap::new_from_names( - &context, - &xkb_config.rules, - &xkb_config.model, - &xkb_config.layout, - &xkb_config.variant, - xkb_config.options, - xkb::KEYMAP_COMPILE_NO_FLAGS, - ) - .ok_or(())?; + fn new(keymap: xkb::Keymap, repeat_rate: i32, repeat_delay: i32) -> KbdInternal { let state = xkb::State::new(&keymap); - Ok(KbdInternal { + KbdInternal { focus: None, pending_focus: None, pressed_keys: Vec::new(), @@ -119,7 +102,7 @@ impl KbdInternal { repeat_rate, repeat_delay, grab: GrabStatus::None, - }) + } } // return true if modifier state has changed @@ -417,10 +400,59 @@ impl KeyboardHandle { "rules" => xkb_config.rules, "model" => xkb_config.model, "layout" => xkb_config.layout, "variant" => xkb_config.variant, "options" => &xkb_config.options ); - let internal = KbdInternal::new(xkb_config, repeat_rate, repeat_delay).map_err(|_| { + + // we create a new contex for each keyboard because libxkbcommon is actually NOT threadsafe + // so confining it inside the KbdInternal allows us to use Rusts mutability rules to make + // sure nothing goes wrong. + // + // FIXME: This is an issue with the xkbcommon-rs crate that does not reflect this + // non-threadsafety properly. + let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); + let keymap = xkb::Keymap::new_from_names( + &context, + &xkb_config.rules, + &xkb_config.model, + &xkb_config.layout, + &xkb_config.variant, + xkb_config.options, + xkb::KEYMAP_COMPILE_NO_FLAGS, + ) + .ok_or_else(|| { debug!(log, "Loading keymap failed"); Error::BadKeymap })?; + Self::new_inner(keymap, repeat_delay, repeat_rate, log) + } + + /// Create a keyboard handler from a keymap string + pub(crate) fn from_keymap_string( + string: String, + repeat_delay: i32, + repeat_rate: i32, + logger: &::slog::Logger, + ) -> Result { + let log = logger.new(o!("smithay_module" => "xkbcommon_handler")); + let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); + let keymap = xkb::Keymap::new_from_string( + &context, + string, + xkb::KEYMAP_FORMAT_TEXT_V1, + xkb::KEYMAP_COMPILE_NO_FLAGS, + ) + .ok_or_else(|| { + debug!(log, "Loading keymap failed"); + Error::BadKeymap + })?; + Self::new_inner(keymap, repeat_delay, repeat_rate, log) + } + + fn new_inner( + keymap: xkb::Keymap, + repeat_delay: i32, + repeat_rate: i32, + log: ::slog::Logger, + ) -> Result { + let internal = KbdInternal::new(keymap, repeat_rate, repeat_delay); info!(log, "Loaded Keymap"; "name" => internal.keymap.layouts().next()); diff --git a/src/input/mod.rs b/src/input/mod.rs index df55a8d6a4b0..62c7d0a29445 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -459,9 +459,52 @@ impl Seat { repeat_delay: i32, repeat_rate: i32, ) -> Result, KeyboardError> { - let mut inner = self.arc.inner.lock().unwrap(); let keyboard = self::keyboard::KeyboardHandle::new(xkb_config, repeat_delay, repeat_rate, &self.arc.log)?; + self.add_keyboard_inner(&keyboard)?; + Ok(keyboard) + } + + /// Adds the keyboard capability to this seat + /// + /// Like [`Seat::add_keyboard`] except it takes a keymap as a string + /// instead of a configuration. + /// + /// Can be used with `xkbcommon::xkb::Keymap::get_as_string` to copy from + /// an existing keymap. This can not directly take a `Keymap` since it + /// needs to create a `Keymap` with it's own xkb context. + /// + /// ``` no_run + /// let keyboard = seat + /// .add_keyboard_from_string( + /// keymap.get_as_string(xkb::KEYMAP_FORMAT_TEXT_V1), + /// 200, + /// 25, + /// ) + /// .expect("Failed to initialize the keyboard"); + /// ``` + /// + pub fn add_keyboard_from_keymap_string( + &mut self, + string: String, + repeat_delay: i32, + repeat_rate: i32, + ) -> Result, KeyboardError> { + let keyboard = self::keyboard::KeyboardHandle::from_keymap_string( + string, + repeat_delay, + repeat_rate, + &self.arc.log, + )?; + self.add_keyboard_inner(&keyboard)?; + Ok(keyboard) + } + + fn add_keyboard_inner( + &mut self, + keyboard: &self::keyboard::KeyboardHandle, + ) -> Result<(), KeyboardError> { + let mut inner = self.arc.inner.lock().unwrap(); if inner.keyboard.is_some() { // there is already a keyboard, remove it and notify the clients // of the change @@ -472,7 +515,7 @@ impl Seat { inner.keyboard = Some(keyboard.clone()); #[cfg(feature = "wayland_frontend")] inner.send_all_caps(); - Ok(keyboard) + Ok(()) } /// Access the keyboard of this seat if any