Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions src/input/keyboard/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -938,8 +938,8 @@ impl<D: SeatHandler + 'static> KeyboardHandle<D> {
///
/// The `filter` argument is expected to be a closure which will peek at the generated input
/// as interpreted by the keymap before it is forwarded to the focused client. If this closure
/// returns [`FilterResult::Forward`], the input will not be sent to the client. If it returns
/// [`FilterResult::Intercept`] a value can be passed to be returned by the whole function.
/// returns [`FilterResult::Forward`], the input will be sent to the client. If it returns
/// [`FilterResult::Intercept`], a value can be passed to be returned by the whole function.
/// This mechanism can be used to implement compositor-level key bindings for example.
///
/// The module [`keysyms`] exposes definitions of all possible keysyms to be compared against.
Expand Down Expand Up @@ -1090,6 +1090,9 @@ impl<D: SeatHandler + 'static> KeyboardHandle<D> {
}

/// Set the modifiers state.
///
/// Note that the round-trip from XKB state to serialized form and back to XKB state is lossy,
/// as documented in [`xkb::State::update_mask`].
pub fn set_modifier_state(&self, mods_state: ModifiersState) -> u32 {
let internal = &mut self.arc.internal.lock().unwrap();

Expand Down Expand Up @@ -1129,6 +1132,18 @@ impl<D: SeatHandler + 'static> KeyboardHandle<D> {
modifiers_changed
}

/// Advertises changed modifier state using [`KeyboardTarget::modifiers`].
///
/// Use this with [`KeyboardHandle::set_modifier_state`] when necessary.
pub fn advertise_modifier_state(&self, data: &mut D) {
let internal = &mut *self.arc.internal.lock().unwrap();

if let Some((focus, _)) = internal.focus.as_mut() {
let seat = self.get_seat(data);
focus.modifiers(&seat, data, internal.mods_state, SERIAL_COUNTER.next_serial());
};
}

/// Get the current led state
pub fn led_state(&self) -> LedState {
self.arc.internal.lock().unwrap().led_state
Expand Down
45 changes: 41 additions & 4 deletions src/input/keyboard/modifiers_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ use xkbcommon::xkb;
///
/// Each field of this struct represents a modifier and is `true` if this modifier is active.
///
/// For some modifiers, this means that the key is currently pressed, others are toggled
/// For some modifiers, this means that the key is currently pressed, others are toggled/locked
/// (like caps lock).
///
/// **Note:** The XKB state should usually be the single source of truth, and the
/// serialization is lossy and will not survive round trips. This is documented in
/// [`xkb::State::update_mask`].
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct ModifiersState {
/// The "control" key
Expand All @@ -30,12 +34,19 @@ pub struct ModifiersState {
/// The "ISO level 5 shift" key
pub iso_level5_shift: bool,

/// Serialized modifier state, as send e.g. by the wl_keyboard protocol
/// Cached serialized modifier state, e.g. for sending in `wl_keyboard.modifiers`.
///
/// Note that this may have outdated information compared to the other fields, and that
/// this is not updated in [`ModifiersState::serialize_back`].
pub serialized: SerializedMods,
}

impl ModifiersState {
/// Update the modifiers state from an xkb state
/// Updates the high-level modifiers state from an XKB state.
///
/// **Note:** The XKB state should usually be the single source of truth, and the
/// serialization is lossy and will not survive round trips. This is documented in
/// [`xkb::State::update_mask`].
pub fn update_with(&mut self, state: &xkb::State) {
self.ctrl = state.mod_name_is_active(&xkb::MOD_NAME_CTRL, xkb::STATE_MODS_EFFECTIVE);
self.alt = state.mod_name_is_active(&xkb::MOD_NAME_ALT, xkb::STATE_MODS_EFFECTIVE);
Expand All @@ -49,7 +60,33 @@ impl ModifiersState {
self.serialized = serialize_modifiers(state);
}

/// Serialize modifier state back to be sent to xkb.
/// Serializes the high-level modifiers state to be sent to XKB e.g. in
/// `wl_keyboard.modifiers`.
///
/// **Note:** The XKB state should usually be the single source of truth, and the
/// serialization is lossy and will not survive round trips. This is documented in
/// [`xkb::State::update_mask`].
///
/// Note that cached serialized state is stored in [`ModifiersState::serialized`], but it may
/// have outdated information. This function ignores that field. You should update the cached
/// serialized state after using this function, like so:
///
/// ```no_run
/// use smithay::input::keyboard::ModifiersState;
///
/// let mut mods_state: ModifiersState;
/// # mods_state = todo!();
/// # let xkb_state = todo!();
///
/// // Update the information
/// mods_state.ctrl = true;
///
/// // Serialize e.g. for sending in `wl_keyboard.modifiers`
/// let serialized = mods_state.serialize_back(&xkb_state);
///
/// // Update the cached serialized state
/// mods_state.serialized = serialized;
/// ```
pub fn serialize_back(&self, state: &xkb::State) -> SerializedMods {
let keymap = state.get_keymap();

Expand Down