add the keyboardgrab and rename GrabStartData

to PointerGrabStartData
This commit is contained in:
Christian Meissl 2022-01-08 22:58:23 +01:00
parent 8edcdf5cd0
commit 070dc78c11
7 changed files with 276 additions and 65 deletions

View File

@ -13,6 +13,7 @@
- `PointerButtonEvent::button` now returns an `Option<MouseButton>`. - `PointerButtonEvent::button` now returns an `Option<MouseButton>`.
- `MouseButton` is now non-exhaustive. - `MouseButton` is now non-exhaustive.
- Remove `Other` and add `Forward` and `Back` variants to `MouseButton`. Use the new `PointerButtonEvent::button_code` in place of `Other`. - Remove `Other` and add `Forward` and `Back` variants to `MouseButton`. Use the new `PointerButtonEvent::button_code` in place of `Other`.
- `GrabStartData` has been renamed to `PointerGrabStartData`
#### Backends #### Backends
@ -49,6 +50,7 @@
- Support for `xdg_wm_base` protocol version 3 - Support for `xdg_wm_base` protocol version 3
- Added the option to initialize the dmabuf global with a client filter - Added the option to initialize the dmabuf global with a client filter
- `wayland::output::Output` now has user data attached to it and more functions to query its properties - `wayland::output::Output` now has user data attached to it and more functions to query its properties
- Added a `KeyboardGrab` similar to the existing `PointerGrab`
#### Backends #### Backends

View File

@ -19,7 +19,7 @@ use smithay::{
compositor_init, is_sync_subsurface, with_states, with_surface_tree_upward, BufferAssignment, compositor_init, is_sync_subsurface, with_states, with_surface_tree_upward, BufferAssignment,
SurfaceAttributes, TraversalAction, SurfaceAttributes, TraversalAction,
}, },
seat::{AxisFrame, GrabStartData, PointerGrab, PointerInnerHandle, Seat}, seat::{AxisFrame, PointerGrab, PointerGrabStartData, PointerInnerHandle, Seat},
shell::{ shell::{
legacy::{wl_shell_init, ShellRequest, ShellState as WlShellState, ShellSurfaceKind}, legacy::{wl_shell_init, ShellRequest, ShellState as WlShellState, ShellSurfaceKind},
wlr_layer::{LayerShellRequest, LayerSurfaceAttributes}, wlr_layer::{LayerShellRequest, LayerSurfaceAttributes},
@ -39,7 +39,7 @@ use crate::{
}; };
struct MoveSurfaceGrab { struct MoveSurfaceGrab {
start_data: GrabStartData, start_data: PointerGrabStartData,
window_map: Rc<RefCell<WindowMap>>, window_map: Rc<RefCell<WindowMap>>,
toplevel: SurfaceKind, toplevel: SurfaceKind,
initial_window_location: Point<i32, Logical>, initial_window_location: Point<i32, Logical>,
@ -82,7 +82,7 @@ impl PointerGrab for MoveSurfaceGrab {
handle.axis(details) handle.axis(details)
} }
fn start_data(&self) -> &GrabStartData { fn start_data(&self) -> &PointerGrabStartData {
&self.start_data &self.start_data
} }
} }
@ -130,7 +130,7 @@ impl From<ResizeEdge> for xdg_toplevel::ResizeEdge {
} }
struct ResizeSurfaceGrab { struct ResizeSurfaceGrab {
start_data: GrabStartData, start_data: PointerGrabStartData,
toplevel: SurfaceKind, toplevel: SurfaceKind,
edges: ResizeEdge, edges: ResizeEdge,
initial_window_size: Size<i32, Logical>, initial_window_size: Size<i32, Logical>,
@ -280,7 +280,7 @@ impl PointerGrab for ResizeSurfaceGrab {
handle.axis(details) handle.axis(details)
} }
fn start_data(&self) -> &GrabStartData { fn start_data(&self) -> &PointerGrabStartData {
&self.start_data &self.start_data
} }
} }

View File

@ -8,7 +8,7 @@ use wayland_server::{
use crate::{ use crate::{
utils::{Logical, Point}, utils::{Logical, Point},
wayland::{ wayland::{
seat::{AxisFrame, GrabStartData, PointerGrab, PointerInnerHandle, Seat}, seat::{AxisFrame, PointerGrab, PointerGrabStartData, PointerInnerHandle, Seat},
Serial, Serial,
}, },
}; };
@ -16,7 +16,7 @@ use crate::{
use super::{with_source_metadata, DataDeviceData, SeatData}; use super::{with_source_metadata, DataDeviceData, SeatData};
pub(crate) struct DnDGrab { pub(crate) struct DnDGrab {
start_data: GrabStartData, start_data: PointerGrabStartData,
data_source: Option<wl_data_source::WlDataSource>, data_source: Option<wl_data_source::WlDataSource>,
current_focus: Option<wl_surface::WlSurface>, current_focus: Option<wl_surface::WlSurface>,
pending_offers: Vec<wl_data_offer::WlDataOffer>, pending_offers: Vec<wl_data_offer::WlDataOffer>,
@ -29,7 +29,7 @@ pub(crate) struct DnDGrab {
impl DnDGrab { impl DnDGrab {
pub(crate) fn new( pub(crate) fn new(
start_data: GrabStartData, start_data: PointerGrabStartData,
source: Option<wl_data_source::WlDataSource>, source: Option<wl_data_source::WlDataSource>,
origin: wl_surface::WlSurface, origin: wl_surface::WlSurface,
seat: Seat, seat: Seat,
@ -222,7 +222,7 @@ impl PointerGrab for DnDGrab {
handle.axis(details); handle.axis(details);
} }
fn start_data(&self) -> &GrabStartData { fn start_data(&self) -> &PointerGrabStartData {
&self.start_data &self.start_data
} }
} }

View File

@ -60,7 +60,7 @@ use slog::{debug, error, o};
use crate::wayland::{ use crate::wayland::{
compositor, compositor,
seat::{GrabStartData, Seat}, seat::{PointerGrabStartData, Seat},
Serial, Serial,
}; };
@ -335,7 +335,7 @@ pub fn set_data_device_selection(seat: &Seat, mime_types: Vec<String>) {
pub fn start_dnd<C>( pub fn start_dnd<C>(
seat: &Seat, seat: &Seat,
serial: Serial, serial: Serial,
start_data: GrabStartData, start_data: PointerGrabStartData,
metadata: SourceMetadata, metadata: SourceMetadata,
callback: C, callback: C,
) where ) where

View File

@ -8,7 +8,7 @@ use wayland_server::{
use crate::{ use crate::{
utils::{Logical, Point}, utils::{Logical, Point},
wayland::{ wayland::{
seat::{AxisFrame, GrabStartData, PointerGrab, PointerInnerHandle, Seat}, seat::{AxisFrame, PointerGrab, PointerGrabStartData, PointerInnerHandle, Seat},
Serial, Serial,
}, },
}; };
@ -42,7 +42,7 @@ pub enum ServerDndEvent {
} }
pub(crate) struct ServerDnDGrab<C: 'static> { pub(crate) struct ServerDnDGrab<C: 'static> {
start_data: GrabStartData, start_data: PointerGrabStartData,
metadata: super::SourceMetadata, metadata: super::SourceMetadata,
current_focus: Option<wl_surface::WlSurface>, current_focus: Option<wl_surface::WlSurface>,
pending_offers: Vec<wl_data_offer::WlDataOffer>, pending_offers: Vec<wl_data_offer::WlDataOffer>,
@ -53,7 +53,7 @@ pub(crate) struct ServerDnDGrab<C: 'static> {
impl<C: 'static> ServerDnDGrab<C> { impl<C: 'static> ServerDnDGrab<C> {
pub(crate) fn new( pub(crate) fn new(
start_data: GrabStartData, start_data: PointerGrabStartData,
metadata: super::SourceMetadata, metadata: super::SourceMetadata,
seat: Seat, seat: Seat,
callback: Rc<RefCell<C>>, callback: Rc<RefCell<C>>,
@ -222,7 +222,7 @@ where
handle.axis(details); handle.axis(details);
} }
fn start_data(&self) -> &GrabStartData { fn start_data(&self) -> &PointerGrabStartData {
&self.start_data &self.start_data
} }
} }

View File

@ -86,9 +86,16 @@ pub struct XkbConfig<'a> {
pub options: Option<String>, pub options: Option<String>,
} }
enum GrabStatus {
None,
Active(Serial, Box<dyn KeyboardGrab>),
Borrowed,
}
struct KbdInternal { struct KbdInternal {
known_kbds: Vec<WlKeyboard>, known_kbds: Vec<WlKeyboard>,
focus: Option<WlSurface>, focus: Option<WlSurface>,
pending_focus: Option<WlSurface>,
pressed_keys: Vec<u32>, pressed_keys: Vec<u32>,
mods_state: ModifiersState, mods_state: ModifiersState,
keymap: xkb::Keymap, keymap: xkb::Keymap,
@ -96,6 +103,7 @@ struct KbdInternal {
repeat_rate: i32, repeat_rate: i32,
repeat_delay: i32, repeat_delay: i32,
focus_hook: Box<dyn FnMut(Option<&WlSurface>)>, focus_hook: Box<dyn FnMut(Option<&WlSurface>)>,
grab: GrabStatus,
} }
// focus_hook does not implement debug, so we have to impl Debug manually // focus_hook does not implement debug, so we have to impl Debug manually
@ -147,6 +155,7 @@ impl KbdInternal {
Ok(KbdInternal { Ok(KbdInternal {
known_kbds: Vec::new(), known_kbds: Vec::new(),
focus: None, focus: None,
pending_focus: None,
pressed_keys: Vec::new(), pressed_keys: Vec::new(),
mods_state: ModifiersState::default(), mods_state: ModifiersState::default(),
keymap, keymap,
@ -154,6 +163,7 @@ impl KbdInternal {
repeat_rate, repeat_rate,
repeat_delay, repeat_delay,
focus_hook, focus_hook,
grab: GrabStatus::None,
}) })
} }
@ -215,6 +225,35 @@ impl KbdInternal {
} }
} }
} }
fn with_grab<F>(&mut self, f: F, logger: ::slog::Logger)
where
F: FnOnce(KeyboardInnerHandle<'_>, &mut dyn KeyboardGrab),
{
let mut grab = ::std::mem::replace(&mut self.grab, GrabStatus::Borrowed);
match grab {
GrabStatus::Borrowed => panic!("Accessed a keyboard grab from within a keyboard grab access."),
GrabStatus::Active(_, ref mut handler) => {
// If this grab is associated with a surface that is no longer alive, discard it
if let Some(ref surface) = handler.start_data().focus {
if !surface.as_ref().is_alive() {
self.grab = GrabStatus::None;
f(KeyboardInnerHandle { inner: self, logger }, &mut DefaultGrab);
return;
}
}
f(KeyboardInnerHandle { inner: self, logger }, &mut **handler);
}
GrabStatus::None => {
f(KeyboardInnerHandle { inner: self, logger }, &mut DefaultGrab);
}
}
if let GrabStatus::Borrowed = self.grab {
// the grab has not been ended nor replaced, put it back in place
self.grab = grab;
}
}
} }
/// Errors that can be encountered when creating a keyboard handler /// Errors that can be encountered when creating a keyboard handler
@ -320,6 +359,47 @@ pub enum FilterResult<T> {
Intercept(T), Intercept(T),
} }
/// Data about the event that started the grab.
#[derive(Debug, Clone)]
pub struct GrabStartData {
/// The focused surface, if any, at the start of the grab.
pub focus: Option<WlSurface>,
}
/// A trait to implement a keyboard grab
///
/// In some context, it is necessary to temporarily change the behavior of the keyboard. This is
/// typically known as a keyboard grab. A example would be, during a popup grab the keyboard focus
/// will not be changed and stay on the grabbed popup.
///
/// This trait is the interface to intercept regular keyboard events and change them as needed, its
/// interface mimics the [`KeyboardHandle`] interface.
///
/// If your logic decides that the grab should end, both [`KeyboardInnerHandle`] and [`KeyboardHandle`] have
/// a method to change it.
///
/// When your grab ends (either as you requested it or if it was forcefully cancelled by the server),
/// the struct implementing this trait will be dropped. As such you should put clean-up logic in the destructor,
/// rather than trying to guess when the grab will end.
pub trait KeyboardGrab {
/// An input was reported
fn input(
&mut self,
handle: &mut KeyboardInnerHandle<'_>,
keycode: u32,
key_state: WlKeyState,
modifiers: Option<(u32, u32, u32, u32)>,
serial: Serial,
time: u32,
);
/// A focus change was requested
fn set_focus(&mut self, handle: &mut KeyboardInnerHandle<'_>, focus: Option<&WlSurface>, serial: Serial);
/// The data about the event that started the grab.
fn start_data(&self) -> &GrabStartData;
}
/// An handle to a keyboard handler /// An handle to a keyboard handler
/// ///
/// It can be cloned and all clones manipulate the same internal state. /// It can be cloned and all clones manipulate the same internal state.
@ -337,6 +417,42 @@ pub struct KeyboardHandle {
} }
impl KeyboardHandle { impl KeyboardHandle {
/// Change the current grab on this keyboard to the provided grab
///
/// Overwrites any current grab.
pub fn set_grab<G: KeyboardGrab + 'static>(&self, grab: G, serial: Serial) {
self.arc.internal.borrow_mut().grab = GrabStatus::Active(serial, Box::new(grab));
}
/// Remove any current grab on this keyboard, resetting it to the default behavior
pub fn unset_grab(&self) {
self.arc.internal.borrow_mut().grab = GrabStatus::None;
}
/// Check if this keyboard is currently grabbed with this serial
pub fn has_grab(&self, serial: Serial) -> bool {
let guard = self.arc.internal.borrow_mut();
match guard.grab {
GrabStatus::Active(s, _) => s == serial,
_ => false,
}
}
/// Check if this keyboard is currently being grabbed
pub fn is_grabbed(&self) -> bool {
let guard = self.arc.internal.borrow_mut();
!matches!(guard.grab, GrabStatus::None)
}
/// Returns the start data for the grab, if any.
pub fn grab_start_data(&self) -> Option<GrabStartData> {
let guard = self.arc.internal.borrow();
match &guard.grab {
GrabStatus::Active(_, g) => Some(g.start_data().clone()),
_ => None,
}
}
/// Handle a keystroke /// Handle a keystroke
/// ///
/// All keystrokes from the input backend should be fed _in order_ to this method of the /// All keystrokes from the input backend should be fed _in order_ to this method of the
@ -392,14 +508,12 @@ impl KeyboardHandle {
KeyState::Pressed => WlKeyState::Pressed, KeyState::Pressed => WlKeyState::Pressed,
KeyState::Released => WlKeyState::Released, KeyState::Released => WlKeyState::Released,
}; };
guard.with_focused_kbds(|kbd, _| { guard.with_grab(
// key event must be sent before modifers event for libxkbcommon move |mut handle, grab| {
// to process them correctly grab.input(&mut handle, keycode, wl_state, modifiers, serial, time);
kbd.key(serial.into(), time, keycode, wl_state); },
if let Some((dep, la, lo, gr)) = modifiers { self.arc.logger.clone(),
kbd.modifiers(serial.into(), dep, la, lo, gr); );
}
});
if guard.focus.is_some() { if guard.focus.is_some() {
trace!(self.arc.logger, "Input forwarded to client"); trace!(self.arc.logger, "Input forwarded to client");
} else { } else {
@ -417,44 +531,13 @@ impl KeyboardHandle {
/// a [`wl_keyboard::Event::Enter`](wayland_server::protocol::wl_keyboard::Event::Enter) event will be sent. /// a [`wl_keyboard::Event::Enter`](wayland_server::protocol::wl_keyboard::Event::Enter) event will be sent.
pub fn set_focus(&self, focus: Option<&WlSurface>, serial: Serial) { pub fn set_focus(&self, focus: Option<&WlSurface>, serial: Serial) {
let mut guard = self.arc.internal.borrow_mut(); let mut guard = self.arc.internal.borrow_mut();
guard.pending_focus = focus.cloned();
let same = guard guard.with_grab(
.focus move |mut handle, grab| {
.as_ref() grab.set_focus(&mut handle, focus, serial);
.and_then(|f| focus.map(|s| s.as_ref().equals(f.as_ref()))) },
.unwrap_or(false); self.arc.logger.clone(),
);
if !same {
// unset old focus
guard.with_focused_kbds(|kbd, s| {
kbd.leave(serial.into(), s);
});
// set new focus
guard.focus = focus.cloned();
let (dep, la, lo, gr) = guard.serialize_modifiers();
let keys = guard.serialize_pressed_keys();
guard.with_focused_kbds(|kbd, surface| {
kbd.enter(serial.into(), surface, keys.clone());
// Modifiers must be send after enter event.
kbd.modifiers(serial.into(), dep, la, lo, gr);
});
{
let KbdInternal {
ref focus,
ref mut focus_hook,
..
} = *guard;
focus_hook(focus.as_ref());
}
if guard.focus.is_some() {
trace!(self.arc.logger, "Focus set to new surface");
} else {
trace!(self.arc.logger, "Focus unset");
}
} else {
trace!(self.arc.logger, "Focus unchanged");
}
} }
/// Check if given client currently has keyboard focus /// Check if given client currently has keyboard focus
@ -543,3 +626,129 @@ pub(crate) fn implement_keyboard(keyboard: Main<WlKeyboard>, handle: Option<&Key
keyboard.deref().clone() keyboard.deref().clone()
} }
/// This inner handle is accessed from inside a keyboard grab logic, and directly
/// sends event to the client
#[derive(Debug)]
pub struct KeyboardInnerHandle<'a> {
inner: &'a mut KbdInternal,
logger: ::slog::Logger,
}
impl<'a> KeyboardInnerHandle<'a> {
/// Change the current grab on this keyboard to the provided grab
///
/// Overwrites any current grab.
pub fn set_grab<G: KeyboardGrab + 'static>(&mut self, serial: Serial, grab: G) {
self.inner.grab = GrabStatus::Active(serial, Box::new(grab));
}
/// Remove any current grab on this keyboard, resetting it to the default behavior
///
/// This will also restore the focus of the underlying keyboard if restore_focus
/// is [`true`]
pub fn unset_grab(&mut self, serial: Serial, restore_focus: bool) {
self.inner.grab = GrabStatus::None;
// restore the focus
if restore_focus {
let focus = self.inner.pending_focus.clone();
self.set_focus(focus.as_ref(), serial);
}
}
/// Access the current focus of this keyboard
pub fn current_focus(&self) -> Option<&WlSurface> {
self.inner.focus.as_ref()
}
/// Send the input to the focused keyboards
pub fn input(
&mut self,
keycode: u32,
key_state: WlKeyState,
modifiers: Option<(u32, u32, u32, u32)>,
serial: Serial,
time: u32,
) {
self.inner.with_focused_kbds(|kbd, _| {
// key event must be sent before modifers event for libxkbcommon
// to process them correctly
kbd.key(serial.into(), time, keycode, key_state);
if let Some((dep, la, lo, gr)) = modifiers {
kbd.modifiers(serial.into(), dep, la, lo, gr);
}
});
}
/// Set the current focus of this keyboard
///
/// If the new focus is different from the previous one, any previous focus
/// will be sent a [`wl_keyboard::Event::Leave`](wayland_server::protocol::wl_keyboard::Event::Leave)
/// event, and if the new focus is not `None`,
/// a [`wl_keyboard::Event::Enter`](wayland_server::protocol::wl_keyboard::Event::Enter) event will be sent.
pub fn set_focus(&mut self, focus: Option<&WlSurface>, serial: Serial) {
let same = self
.inner
.focus
.as_ref()
.and_then(|f| focus.map(|s| s.as_ref().equals(f.as_ref())))
.unwrap_or(false);
if !same {
// unset old focus
self.inner.with_focused_kbds(|kbd, s| {
kbd.leave(serial.into(), s);
});
// set new focus
self.inner.focus = focus.cloned();
let (dep, la, lo, gr) = self.inner.serialize_modifiers();
let keys = self.inner.serialize_pressed_keys();
self.inner.with_focused_kbds(|kbd, surface| {
kbd.enter(serial.into(), surface, keys.clone());
// Modifiers must be send after enter event.
kbd.modifiers(serial.into(), dep, la, lo, gr);
});
{
let KbdInternal {
ref focus,
ref mut focus_hook,
..
} = *self.inner;
focus_hook(focus.as_ref());
}
if self.inner.focus.is_some() {
trace!(self.logger, "Focus set to new surface");
} else {
trace!(self.logger, "Focus unset");
}
} else {
trace!(self.logger, "Focus unchanged");
}
}
}
// The default grab, the behavior when no particular grab is in progress
struct DefaultGrab;
impl KeyboardGrab for DefaultGrab {
fn input(
&mut self,
handle: &mut KeyboardInnerHandle<'_>,
keycode: u32,
key_state: WlKeyState,
modifiers: Option<(u32, u32, u32, u32)>,
serial: Serial,
time: u32,
) {
handle.input(keycode, key_state, modifiers, serial, time)
}
fn set_focus(&mut self, handle: &mut KeyboardInnerHandle<'_>, focus: Option<&WlSurface>, serial: Serial) {
handle.set_focus(focus, serial)
}
fn start_data(&self) -> &GrabStartData {
unreachable!()
}
}

View File

@ -41,12 +41,12 @@ mod pointer;
pub use self::{ pub use self::{
keyboard::{ keyboard::{
keysyms, Error as KeyboardError, FilterResult, KeyboardHandle, Keysym, KeysymHandle, ModifiersState, keysyms, Error as KeyboardError, FilterResult, GrabStartData as KeyboardGrabStartData, KeyboardGrab,
XkbConfig, KeyboardHandle, KeyboardInnerHandle, Keysym, KeysymHandle, ModifiersState, XkbConfig,
}, },
pointer::{ pointer::{
AxisFrame, CursorImageAttributes, CursorImageStatus, GrabStartData, PointerGrab, PointerHandle, AxisFrame, CursorImageAttributes, CursorImageStatus, GrabStartData as PointerGrabStartData,
PointerInnerHandle, PointerGrab, PointerHandle, PointerInnerHandle,
}, },
}; };