Merge pull request #380 from Smithay/feature/xcb_variants

This commit is contained in:
Victor Brekenfeld 2021-09-22 18:48:26 +02:00 committed by GitHub
commit 7f0687cfc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 104 additions and 39 deletions

View File

@ -8,6 +8,7 @@
- `XdgPositionerState` moved to `XdgPopupState` and added to `XdgRequest::NewPopup`
- `PopupSurface::send_configure` now checks the protocol version and returns an `Result`
- `KeyboardHandle::input` filter closure now receives a `KeysymHandle` instead of a `Keysym` and returns a `FilterResult`.
### Additions

View File

@ -13,7 +13,7 @@ use smithay::{
},
reexports::wayland_server::protocol::wl_pointer,
wayland::{
seat::{keysyms as xkb, AxisFrame, Keysym, ModifiersState},
seat::{keysyms as xkb, AxisFrame, FilterResult, Keysym, ModifiersState},
SERIAL_COUNTER as SCOUNTER,
},
};
@ -42,10 +42,11 @@ impl<Backend> AnvilState<Backend> {
let serial = SCOUNTER.next_serial();
let log = &self.log;
let time = Event::time(&evt);
let mut action = KeyAction::None;
let suppressed_keys = &mut self.suppressed_keys;
self.keyboard
.input(keycode, state, serial, time, |modifiers, keysym| {
.input(keycode, state, serial, time, |modifiers, handle| {
let keysym = handle.modified_sym();
debug!(log, "keysym";
"state" => format!("{:?}", state),
"mods" => format!("{:?}", modifiers),
@ -58,27 +59,26 @@ impl<Backend> AnvilState<Backend> {
// so that we can decide on a release if the key
// should be forwarded to the client or not.
if let KeyState::Pressed = state {
action = process_keyboard_shortcut(*modifiers, keysym);
let action = process_keyboard_shortcut(*modifiers, keysym);
// forward to client only if action == KeyAction::Forward
let forward = matches!(action, KeyAction::Forward);
if !forward {
if action.is_some() {
suppressed_keys.push(keysym);
}
forward
action
.map(FilterResult::Intercept)
.unwrap_or(FilterResult::Forward)
} else {
let suppressed = suppressed_keys.contains(&keysym);
if suppressed {
suppressed_keys.retain(|k| *k != keysym);
FilterResult::Intercept(KeyAction::None)
} else {
FilterResult::Forward
}
!suppressed
}
});
action
})
.unwrap_or(KeyAction::None)
}
fn on_pointer_button<B: InputBackend>(&mut self, evt: B::PointerButtonEvent) {
@ -155,7 +155,7 @@ impl AnvilState<WinitData> {
match event {
InputEvent::Keyboard { event, .. } => match self.keyboard_key_to_action::<B>(event) {
KeyAction::None | KeyAction::Forward => {}
KeyAction::None => {}
KeyAction::Quit => {
info!(self.log, "Quitting.");
self.running.store(false, Ordering::SeqCst);
@ -243,7 +243,7 @@ impl AnvilState<UdevData> {
pub fn process_input_event<B: InputBackend>(&mut self, event: InputEvent<B>) {
match event {
InputEvent::Keyboard { event, .. } => match self.keyboard_key_to_action::<B>(event) {
KeyAction::None | KeyAction::Forward => {}
KeyAction::None => {}
KeyAction::Quit => {
info!(self.log, "Quitting.");
self.running.store(false, Ordering::SeqCst);
@ -520,32 +520,32 @@ enum KeyAction {
Screen(usize),
ScaleUp,
ScaleDown,
/// Forward the key to the client
Forward,
/// Do nothing more
None,
}
fn process_keyboard_shortcut(modifiers: ModifiersState, keysym: Keysym) -> KeyAction {
fn process_keyboard_shortcut(modifiers: ModifiersState, keysym: Keysym) -> Option<KeyAction> {
if modifiers.ctrl && modifiers.alt && keysym == xkb::KEY_BackSpace
|| modifiers.logo && keysym == xkb::KEY_q
{
// ctrl+alt+backspace = quit
// logo + q = quit
KeyAction::Quit
Some(KeyAction::Quit)
} else if (xkb::KEY_XF86Switch_VT_1..=xkb::KEY_XF86Switch_VT_12).contains(&keysym) {
// VTSwicth
KeyAction::VtSwitch((keysym - xkb::KEY_XF86Switch_VT_1 + 1) as i32)
Some(KeyAction::VtSwitch(
(keysym - xkb::KEY_XF86Switch_VT_1 + 1) as i32,
))
} else if modifiers.logo && keysym == xkb::KEY_Return {
// run terminal
KeyAction::Run("weston-terminal".into())
Some(KeyAction::Run("weston-terminal".into()))
} else if modifiers.logo && keysym >= xkb::KEY_1 && keysym <= xkb::KEY_9 {
KeyAction::Screen((keysym - xkb::KEY_1) as usize)
Some(KeyAction::Screen((keysym - xkb::KEY_1) as usize))
} else if modifiers.logo && modifiers.shift && keysym == xkb::KEY_M {
KeyAction::ScaleDown
Some(KeyAction::ScaleDown)
} else if modifiers.logo && modifiers.shift && keysym == xkb::KEY_P {
KeyAction::ScaleUp
Some(KeyAction::ScaleUp)
} else {
KeyAction::Forward
None
}
}

View File

@ -282,6 +282,56 @@ struct KbdRc {
logger: ::slog::Logger,
}
/// Handle to the underlying keycode to allow for different conversions
pub struct KeysymHandle<'a> {
keycode: u32,
keymap: &'a xkb::Keymap,
state: &'a xkb::State,
}
impl<'a> fmt::Debug for KeysymHandle<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.keycode)
}
}
impl<'a> KeysymHandle<'a> {
/// Returns the sym for the underlying keycode with all modifications by the current keymap state applied.
///
/// This function is similar to [`KeysymHandle::modified_syms`], but is intended for cases where the user
/// does not want to or cannot handle multiple keysyms.
///
/// If the key does not have exactly one keysym, returns [`keysyms::KEY_NoSymbol`].
pub fn modified_sym(&'a self) -> Keysym {
self.state.key_get_one_sym(self.keycode)
}
/// Returns the syms for the underlying keycode with all modifications by the current keymap state applied.
pub fn modified_syms(&'a self) -> &'a [Keysym] {
self.state.key_get_syms(self.keycode)
}
/// Returns the syms for the underlying keycode without any modifications by the current keymap state applied.
pub fn raw_syms(&'a self) -> &'a [Keysym] {
self.keymap
.key_get_syms_by_level(self.keycode, self.state.key_get_layout(self.keycode), 0)
}
/// Returns the raw code in X keycode system (shifted by 8)
pub fn raw_code(&'a self) -> u32 {
self.keycode
}
}
/// Result for key input filtering (see [`KeyboardHandle::input`])
#[derive(Debug)]
pub enum FilterResult<T> {
/// Forward the given keycode to the client
Forward,
/// Do not forward and return value
Intercept(T),
}
/// An handle to a keyboard handler
///
/// It can be cloned and all clones manipulate the same internal state.
@ -306,32 +356,42 @@ impl KeyboardHandle {
///
/// 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 false, the input will not be sent to the client. This mechanism can be used to
/// implement compositor-level key bindings for example.
/// 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.
/// This mechanism can be used to implement compositor-level key bindings for example.
///
/// The module [`crate::wayland::seat::keysyms`] exposes definitions of all possible keysyms
/// to be compared against. This includes non-character keysyms, such as XF86 special keys.
pub fn input<F>(&self, keycode: u32, state: KeyState, serial: Serial, time: u32, filter: F)
pub fn input<T, F>(
&self,
keycode: u32,
state: KeyState,
serial: Serial,
time: u32,
filter: F,
) -> Option<T>
where
F: FnOnce(&ModifiersState, Keysym) -> bool,
F: FnOnce(&ModifiersState, KeysymHandle<'_>) -> FilterResult<T>,
{
trace!(self.arc.logger, "Handling keystroke"; "keycode" => keycode, "state" => format_args!("{:?}", state));
let mut guard = self.arc.internal.borrow_mut();
let mods_changed = guard.key_input(keycode, state);
let handle = KeysymHandle {
// Offset the keycode by 8, as the evdev XKB rules reflect X's
// broken keycode system, which starts at 8.
let sym = guard.state.key_get_one_sym(keycode + 8);
let mods_changed = guard.key_input(keycode, state);
keycode: keycode + 8,
state: &guard.state,
keymap: &guard.keymap,
};
trace!(self.arc.logger, "Calling input filter";
"mods_state" => format_args!("{:?}", guard.mods_state), "sym" => xkb::keysym_get_name(sym)
"mods_state" => format_args!("{:?}", guard.mods_state), "sym" => xkb::keysym_get_name(handle.modified_sym())
);
if !filter(&guard.mods_state, sym) {
if let FilterResult::Intercept(val) = filter(&guard.mods_state, handle) {
// the filter returned false, we do not forward to client
trace!(self.arc.logger, "Input was intercepted by filter");
return;
return Some(val);
}
// forward to client if no keybinding is triggered
@ -357,6 +417,8 @@ impl KeyboardHandle {
} else {
trace!(self.arc.logger, "No client currently focused");
}
None
}
/// Set the current focus of this keyboard

View File

@ -40,7 +40,9 @@ mod keyboard;
mod pointer;
pub use self::{
keyboard::{keysyms, Error as KeyboardError, KeyboardHandle, Keysym, ModifiersState, XkbConfig},
keyboard::{
keysyms, Error as KeyboardError, FilterResult, KeyboardHandle, Keysym, ModifiersState, XkbConfig,
},
pointer::{
AxisFrame, CursorImageAttributes, CursorImageStatus, GrabStartData, PointerGrab, PointerHandle,
PointerInnerHandle,