wayland.seat: Pointer grabbing logic

This commit is contained in:
Victor Berger 2018-10-04 17:40:03 +02:00 committed by Victor Berger
parent 1301fb6e62
commit 139d0f3992
4 changed files with 426 additions and 105 deletions

View File

@ -17,7 +17,7 @@ use smithay::{
self, Event, InputBackend, InputHandler, KeyState, KeyboardKeyEvent, PointerAxisEvent,
PointerButtonEvent, PointerMotionAbsoluteEvent, PointerMotionEvent,
},
wayland::seat::{keysyms as xkb, KeyboardHandle, Keysym, ModifiersState, PointerHandle},
wayland::seat::{keysyms as xkb, AxisFrame, KeyboardHandle, Keysym, ModifiersState, PointerHandle},
wayland_server::protocol::wl_pointer,
};
@ -171,11 +171,7 @@ impl<B: InputBackend> InputHandler<B> for AnvilInputHandler {
.window_map
.borrow()
.get_surface_under((location.0, location.1));
self.pointer.motion(
under.as_ref().map(|&(ref s, (x, y))| (s, x, y)),
serial,
evt.time(),
);
self.pointer.motion(*location, under, serial, evt.time());
}
fn on_pointer_move_absolute(&mut self, _: &input::Seat, evt: B::PointerMotionAbsoluteEvent) {
@ -200,11 +196,7 @@ impl<B: InputBackend> InputHandler<B> for AnvilInputHandler {
*self.pointer_location.borrow_mut() = (x, y);
let serial = self.next_serial();
let under = self.window_map.borrow().get_surface_under((x as f64, y as f64));
self.pointer.motion(
under.as_ref().map(|&(ref s, (x, y))| (s, x, y)),
serial,
evt.time(),
);
self.pointer.motion((x, y), under, serial, evt.time());
}
fn on_pointer_button(&mut self, _: &input::Seat, evt: B::PointerButtonEvent) {
@ -217,13 +209,15 @@ impl<B: InputBackend> InputHandler<B> for AnvilInputHandler {
};
let state = match evt.state() {
input::MouseButtonState::Pressed => {
// change the keyboard focus
// change the keyboard focus unless the pointer is grabbed
if !self.pointer.is_grabbed() {
let under = self
.window_map
.borrow_mut()
.get_surface_and_bring_to_top(*self.pointer_location.borrow());
self.keyboard
.set_focus(under.as_ref().map(|&(ref s, _)| s), serial);
}
wl_pointer::ButtonState::Pressed
}
input::MouseButtonState::Released => wl_pointer::ButtonState::Released,
@ -247,25 +241,24 @@ impl<B: InputBackend> InputHandler<B> for AnvilInputHandler {
let vertical_amount_discrete = evt.amount_discrete(&input::Axis::Vertical);
{
let mut event = self.pointer.axis();
event.source(source);
let mut frame = AxisFrame::new(evt.time()).source(source);
if horizontal_amount != 0.0 {
event.value(wl_pointer::Axis::HorizontalScroll, horizontal_amount, evt.time());
frame = frame.value(wl_pointer::Axis::HorizontalScroll, horizontal_amount);
if let Some(discrete) = horizontal_amount_discrete {
event.discrete(wl_pointer::Axis::HorizontalScroll, discrete as i32);
frame = frame.discrete(wl_pointer::Axis::HorizontalScroll, discrete as i32);
}
} else if source == wl_pointer::AxisSource::Finger {
event.stop(wl_pointer::Axis::HorizontalScroll, evt.time());
frame = frame.stop(wl_pointer::Axis::HorizontalScroll);
}
if vertical_amount != 0.0 {
event.value(wl_pointer::Axis::VerticalScroll, vertical_amount, evt.time());
frame = frame.value(wl_pointer::Axis::VerticalScroll, vertical_amount);
if let Some(discrete) = vertical_amount_discrete {
event.discrete(wl_pointer::Axis::VerticalScroll, discrete as i32);
frame = frame.discrete(wl_pointer::Axis::VerticalScroll, discrete as i32);
}
} else if source == wl_pointer::AxisSource::Finger {
event.stop(wl_pointer::Axis::VerticalScroll, evt.time());
frame = frame.stop(wl_pointer::Axis::VerticalScroll);
}
event.done();
self.pointer.axis(frame);
}
}

View File

@ -49,7 +49,7 @@ where
SD: 'static,
D: 'static,
{
// Find the topmost surface under this point if any and the location of this point in the surface
// Find the topmost surface under this point if any and the location of this surface
fn matching<F>(
&self,
point: (f64, f64),
@ -81,10 +81,7 @@ where
height: h,
};
if my_rect.contains((point.0 as i32, point.1 as i32)) {
found = Some((
wl_surface.clone(),
(point.0 - my_rect.x as f64, point.1 - my_rect.y as f64),
));
found = Some((wl_surface.clone(), (my_rect.x as f64, my_rect.y as f64)));
TraversalAction::Break
} else {
TraversalAction::DoChildren((x, y))

View File

@ -61,8 +61,9 @@ mod pointer;
pub use self::{
keyboard::{keysyms, Error as KeyboardError, KeyboardHandle, Keysym, ModifiersState, XkbConfig},
pointer::{PointerAxisHandle, PointerHandle},
pointer::{AxisFrame, PointerGrab, PointerHandle, PointerInnerHandle},
};
use wayland_server::{protocol::wl_seat, Display, Global, NewResource, Resource};
struct Inner {

View File

@ -1,4 +1,4 @@
use std::sync::{Arc, Mutex, MutexGuard};
use std::sync::{Arc, Mutex};
use wayland_server::{
protocol::{
wl_pointer::{Axis, AxisSource, ButtonState, Event, Request, WlPointer},
@ -9,9 +9,17 @@ use wayland_server::{
// TODO: handle pointer surface role
enum GrabStatus {
None,
Active(u32, Box<PointerGrab>),
Borrowed,
}
struct PointerInternal {
known_pointers: Vec<Resource<WlPointer>>,
focus: Option<Resource<WlSurface>>,
focus: Option<(Resource<WlSurface>, (f64, f64))>,
location: (f64, f64),
grab: GrabStatus,
}
impl PointerInternal {
@ -19,6 +27,8 @@ impl PointerInternal {
PointerInternal {
known_pointers: Vec::new(),
focus: None,
location: (0.0, 0.0),
grab: GrabStatus::None,
}
}
@ -26,7 +36,7 @@ impl PointerInternal {
where
F: FnMut(&Resource<WlPointer>, &Resource<WlSurface>),
{
if let Some(ref focus) = self.focus {
if let Some((ref focus, _)) = self.focus {
for ptr in &self.known_pointers {
if ptr.same_client_as(focus) {
f(ptr, focus)
@ -34,15 +44,39 @@ impl PointerInternal {
}
}
}
fn with_grab<F>(&mut self, f: F)
where
F: FnOnce(PointerInnerHandle, &mut PointerGrab),
{
let mut grab = ::std::mem::replace(&mut self.grab, GrabStatus::Borrowed);
match grab {
GrabStatus::Borrowed => panic!("Accessed a pointer grab from within a pointer grab access."),
GrabStatus::Active(_, ref mut handler) => {
f(PointerInnerHandle { inner: self }, &mut **handler);
}
GrabStatus::None => {
f(PointerInnerHandle { inner: self }, &mut DefaultGrab);
}
}
if let GrabStatus::Borrowed = self.grab {
// the grab has not been ended nor replaced, put it back in place
self.grab = grab;
}
}
}
/// An handle to a keyboard handler
/// An handle to a pointer handler
///
/// It can be cloned and all clones manipulate the same internal state. Clones
/// can also be sent across threads.
///
/// This handle gives you access to an interface to send pointer events to your
/// clients.
///
/// When sending events using this handle, they will be intercepted by a pointer
/// grab if any is active. See the `PointerGrab` trait for details.
#[derive(Clone)]
pub struct PointerHandle {
inner: Arc<Mutex<PointerInternal>>,
@ -54,29 +88,177 @@ impl PointerHandle {
guard.known_pointers.push(pointer);
}
/// Change the current grab on this pointer to the provided grab
///
/// Overwrites any current grab.
pub fn set_grab<G: PointerGrab + 'static>(&self, grab: G, serial: u32) {
self.inner.lock().unwrap().grab = GrabStatus::Active(serial, Box::new(grab));
}
/// Remove any current grab on this pointer, reseting it to the default behavior
pub fn unset_grab(&self) {
self.inner.lock().unwrap().grab = GrabStatus::None;
}
/// Check if this pointer is currently grabbed with this serial
pub fn has_grab(&self, serial: u32) -> bool {
let guard = self.inner.lock().unwrap();
match guard.grab {
GrabStatus::Active(s, _) => s == serial,
_ => false,
}
}
/// Check if this pointer is currently being grabbed
pub fn is_grabbed(&self) -> bool {
let guard = self.inner.lock().unwrap();
match guard.grab {
GrabStatus::None => false,
_ => true,
}
}
/// Notify that the pointer moved
///
/// You provide the new location of the pointer, in the form of:
///
/// - `None` if the pointer is not on top of a client surface
/// - `Some(surface, x, y)` if the pointer is focusing surface `surface`,
/// at location `(x, y)` relative to this surface
/// - The coordinates of the pointer in the global compositor space
/// - The surface on top of which the cursor is, and the coordinates of its
/// origin in the global compositor space (or `None` of the pointer is not
/// on top of a client surface).
///
/// This will internally take care of notifying the appropriate client objects
/// of enter/motion/leave events.
pub fn motion(&self, location: Option<(&Resource<WlSurface>, f64, f64)>, serial: u32, time: u32) {
let mut guard = self.inner.lock().unwrap();
pub fn motion(
&self,
location: (f64, f64),
focus: Option<(Resource<WlSurface>, (f64, f64))>,
serial: u32,
time: u32,
) {
self.inner.lock().unwrap().with_grab(move |mut handle, grab| {
grab.motion(&mut handle, location, focus, serial, time);
});
}
/// Notify that a button was pressed
///
/// This will internally send the appropriate button event to the client
/// objects matching with the currently focused surface.
pub fn button(&self, button: u32, state: ButtonState, serial: u32, time: u32) {
self.inner.lock().unwrap().with_grab(|mut handle, grab| {
grab.button(&mut handle, button, state, serial, time);
});
}
/// Start an axis frame
///
/// A single frame will group multiple scroll events as if they happended in the same instance.
/// Dropping the returned `PointerAxisHandle` will group the events together.
pub fn axis(&self, details: AxisFrame) {
self.inner.lock().unwrap().with_grab(|mut handle, grab| {
grab.axis(&mut handle, details);
});
}
}
/// A trait to implement a pointer grab
///
/// In some context, it is necessary to temporarily change the behavior of the pointer. This is
/// typically known as a pointer grab. A typicall example would be, during a drag'n'drop operation,
/// the underlying surfaces will no longer receive classic pointer event, but rather special events.
///
/// This trait is the interface to intercept regular pointer events and change them as needed, its
/// interface mimicks the `PointerHandle` interface.
///
/// If your logic decides that the grab should end, both `PointerInnerHandle` and `PointerHandle` 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 PointerGrab: Send + Sync {
/// A motion was reported
fn motion(
&mut self,
handle: &mut PointerInnerHandle,
location: (f64, f64),
focus: Option<(Resource<WlSurface>, (f64, f64))>,
serial: u32,
time: u32,
);
/// A button press was reported
fn button(
&mut self,
handle: &mut PointerInnerHandle,
button: u32,
state: ButtonState,
serial: u32,
time: u32,
);
/// An axis scroll was reported
fn axis(&mut self, handle: &mut PointerInnerHandle, details: AxisFrame);
}
/// This inner handle is accessed from inside a pointer grab logic, and directly
/// sends event to the client
pub struct PointerInnerHandle<'a> {
inner: &'a mut PointerInternal,
}
impl<'a> PointerInnerHandle<'a> {
/// Change the current grab on this pointer to the provided grab
///
/// Overwrites any current grab.
pub fn set_grab<G: PointerGrab + 'static>(&mut self, serial: u32, grab: G) {
self.inner.grab = GrabStatus::Active(serial, Box::new(grab));
}
/// Remove any current grab on this pointer, reseting it to the default behavior
pub fn unset_grab(&mut self) {
self.inner.grab = GrabStatus::None;
}
/// Access the current focus of this pointer
pub fn current_focus(&self) -> Option<&(Resource<WlSurface>, (f64, f64))> {
self.inner.focus.as_ref()
}
/// Access the current location of this pointer in the global space
pub fn current_location(&self) -> (f64, f64) {
self.inner.location
}
/// Notify that the pointer moved
///
/// You provide the new location of the pointer, in the form of:
///
/// - The coordinates of the pointer in the global compositor space
/// - The surface on top of which the cursor is, and the coordinates of its
/// origin in the global compositor space (or `None` of the pointer is not
/// on top of a client surface).
///
/// This will internally take care of notifying the appropriate client objects
/// of enter/motion/leave events.
pub fn motion(
&mut self,
(x, y): (f64, f64),
focus: Option<(Resource<WlSurface>, (f64, f64))>,
serial: u32,
time: u32,
) {
// do we leave a surface ?
let mut leave = true;
if let Some(ref focus) = guard.focus {
if let Some((surface, _, _)) = location {
if focus.equals(surface) {
self.inner.location = (x, y);
if let Some((ref current_focus, _)) = self.inner.focus {
if let Some((ref surface, _)) = focus {
if current_focus.equals(surface) {
leave = false;
}
}
}
if leave {
guard.with_focused_pointers(|pointer, surface| {
self.inner.with_focused_pointers(|pointer, surface| {
pointer.send(Event::Leave {
serial,
surface: surface.clone(),
@ -85,19 +267,22 @@ impl PointerHandle {
pointer.send(Event::Frame);
}
});
guard.focus = None;
self.inner.focus = None;
}
// do we enter one ?
if let Some((surface, x, y)) = location {
if guard.focus.is_none() {
guard.focus = Some(surface.clone());
guard.with_focused_pointers(|pointer, surface| {
if let Some((surface, (sx, sy))) = focus {
let entered = self.inner.focus.is_none();
// in all cases, update the focus, the coordinates of the surface
// might have changed
self.inner.focus = Some((surface.clone(), (sx, sy)));
if entered {
self.inner.with_focused_pointers(|pointer, surface| {
pointer.send(Event::Enter {
serial,
surface: surface.clone(),
surface_x: x,
surface_y: y,
surface_x: x - sx,
surface_y: y - sy,
});
if pointer.version() >= 5 {
pointer.send(Event::Frame);
@ -105,11 +290,11 @@ impl PointerHandle {
})
} else {
// we were on top of a surface and remained on it
guard.with_focused_pointers(|pointer, _| {
self.inner.with_focused_pointers(|pointer, _| {
pointer.send(Event::Motion {
time,
surface_x: x,
surface_y: y,
surface_x: x - sx,
surface_y: y - sy,
});
if pointer.version() >= 5 {
pointer.send(Event::Frame);
@ -124,8 +309,7 @@ impl PointerHandle {
/// This will internally send the appropriate button event to the client
/// objects matching with the currently focused surface.
pub fn button(&self, button: u32, state: ButtonState, serial: u32, time: u32) {
let guard = self.inner.lock().unwrap();
guard.with_focused_pointers(|pointer, _| {
self.inner.with_focused_pointers(|pointer, _| {
pointer.send(Event::Button {
serial,
time,
@ -138,14 +322,62 @@ impl PointerHandle {
})
}
/// Start an axis frame
/// Notify that an axis was scrolled
///
/// A single frame will group multiple scroll events as if they happended in the same instance.
/// Dropping the returned `PointerAxisHandle` will group the events together.
pub fn axis(&self) -> PointerAxisHandle {
PointerAxisHandle {
inner: self.inner.lock().unwrap(),
/// This will internally send the appropriate axis events to the client
/// objects matching with the currently focused surface.
pub fn axis(&mut self, details: AxisFrame) {
self.inner.with_focused_pointers(|pointer, _| {
// axis
if details.axis.0 != 0.0 {
pointer.send(Event::Axis {
time: details.time,
axis: Axis::HorizontalScroll,
value: details.axis.0,
});
}
if details.axis.1 != 0.0 {
pointer.send(Event::Axis {
time: details.time,
axis: Axis::VerticalScroll,
value: details.axis.1,
});
}
if pointer.version() >= 5 {
// axis source
if let Some(source) = details.source {
pointer.send(Event::AxisSource { axis_source: source });
}
// axis discrete
if details.discrete.0 != 0 {
pointer.send(Event::AxisDiscrete {
axis: Axis::HorizontalScroll,
discrete: details.discrete.0,
});
}
if details.discrete.1 != 0 {
pointer.send(Event::AxisDiscrete {
axis: Axis::VerticalScroll,
discrete: details.discrete.1,
});
}
// stop
if details.stop.0 {
pointer.send(Event::AxisStop {
time: details.time,
axis: Axis::HorizontalScroll,
});
}
if details.stop.1 {
pointer.send(Event::AxisStop {
time: details.time,
axis: Axis::VerticalScroll,
});
}
// frame
pointer.send(Event::Frame);
}
});
}
}
@ -154,17 +386,32 @@ impl PointerHandle {
/// Can be used with the builder pattern, e.g.:
///
/// ```ignore
/// pointer.axis()
/// AxisFrame::new()
/// .source(AxisSource::Wheel)
/// .discrete(Axis::Vertical, 6)
/// .value(Axis::Vertical, 30, time)
/// .stop(Axis::Vertical);
/// ```
pub struct PointerAxisHandle<'a> {
inner: MutexGuard<'a, PointerInternal>,
#[derive(Copy, Clone, Debug)]
pub struct AxisFrame {
source: Option<AxisSource>,
time: u32,
axis: (f64, f64),
discrete: (i32, i32),
stop: (bool, bool),
}
impl<'a> PointerAxisHandle<'a> {
impl AxisFrame {
pub fn new(time: u32) -> Self {
AxisFrame {
source: None,
time,
axis: (0.0, 0.0),
discrete: (0, 0),
stop: (false, false),
}
}
/// Specify the source of the axis events
///
/// This event is optional, if no source is known, you can ignore this call.
@ -172,12 +419,8 @@ impl<'a> PointerAxisHandle<'a> {
///
/// Using the `AxisSource::Finger` requires a stop event to be send,
/// when the user lifts off the finger (not necessarily in the same frame).
pub fn source(&mut self, source: AxisSource) -> &mut Self {
self.inner.with_focused_pointers(|pointer, _| {
if pointer.version() >= 5 {
pointer.send(Event::AxisSource { axis_source: source });
}
});
pub fn source(mut self, source: AxisSource) -> Self {
self.source = Some(source);
self
}
@ -186,24 +429,29 @@ impl<'a> PointerAxisHandle<'a> {
/// This event is optional and gives the client additional information about
/// the nature of the axis event. E.g. a scroll wheel might issue separate steps,
/// while a touchpad may never issue this event as it has no steps.
pub fn discrete(&mut self, axis: Axis, steps: i32) -> &mut Self {
self.inner.with_focused_pointers(|pointer, _| {
if pointer.version() >= 5 {
pointer.send(Event::AxisDiscrete {
axis,
discrete: steps,
});
pub fn discrete(mut self, axis: Axis, steps: i32) -> Self {
match axis {
Axis::HorizontalScroll => {
self.discrete.0 = steps;
}
});
Axis::VerticalScroll => {
self.discrete.1 = steps;
}
};
self
}
/// The actual scroll value. This event is the only required one, but can also
/// be send multiple times. The values off one frame will be accumulated by the client.
pub fn value(&mut self, axis: Axis, value: f64, time: u32) -> &mut Self {
self.inner.with_focused_pointers(|pointer, _| {
pointer.send(Event::Axis { time, axis, value });
});
pub fn value(mut self, axis: Axis, value: f64) -> Self {
match axis {
Axis::HorizontalScroll => {
self.axis.0 = value;
}
Axis::VerticalScroll => {
self.axis.1 = value;
}
};
self
}
@ -211,27 +459,17 @@ impl<'a> PointerAxisHandle<'a> {
///
/// This event is required for sources of the `AxisSource::Finger` type
/// and otherwise optional.
pub fn stop(&mut self, axis: Axis, time: u32) -> &mut Self {
self.inner.with_focused_pointers(|pointer, _| {
if pointer.version() >= 5 {
pointer.send(Event::AxisStop { time, axis });
pub fn stop(mut self, axis: Axis) -> Self {
match axis {
Axis::HorizontalScroll => {
self.stop.0 = true;
}
});
Axis::VerticalScroll => {
self.stop.1 = true;
}
};
self
}
/// Finish this event
///
/// This will group all axis calls together.
/// Note: They are already submitted to the client, obmitting this call just
/// leaves room for additional events.
pub fn done(&mut self) {
self.inner.with_focused_pointers(|pointer, _| {
if pointer.version() >= 5 {
pointer.send(Event::Frame);
}
})
}
}
pub(crate) fn create_pointer_handler() -> PointerHandle {
@ -272,3 +510,95 @@ pub(crate) fn implement_pointer(
(),
)
}
/*
* Grabs definition
*/
// The default grab, the behavior when no particular grab is in progress
struct DefaultGrab;
impl PointerGrab for DefaultGrab {
fn motion(
&mut self,
handle: &mut PointerInnerHandle,
location: (f64, f64),
focus: Option<(Resource<WlSurface>, (f64, f64))>,
serial: u32,
time: u32,
) {
handle.motion(location, focus, serial, time);
}
fn button(
&mut self,
handle: &mut PointerInnerHandle,
button: u32,
state: ButtonState,
serial: u32,
time: u32,
) {
handle.button(button, state, serial, time);
let current_focus = handle.current_focus().cloned();
handle.set_grab(
serial,
ClickGrab {
buttons: vec![button],
current_focus,
pending_focus: None,
},
);
}
fn axis(&mut self, handle: &mut PointerInnerHandle, details: AxisFrame) {
handle.axis(details);
}
}
// A click grab, basic grab started when an user clicks a surface
// to maintain it focused until the user releases the click.
//
// In case the user maintains several simultaneous clicks, release
// the grab once all are released.
struct ClickGrab {
buttons: Vec<u32>,
current_focus: Option<(Resource<WlSurface>, (f64, f64))>,
pending_focus: Option<(Resource<WlSurface>, (f64, f64))>,
}
impl PointerGrab for ClickGrab {
fn motion(
&mut self,
handle: &mut PointerInnerHandle,
location: (f64, f64),
focus: Option<(Resource<WlSurface>, (f64, f64))>,
serial: u32,
time: u32,
) {
// buffer the future focus, but maintain the current one
self.pending_focus = focus;
handle.motion(location, self.current_focus.clone(), serial, time);
}
fn button(
&mut self,
handle: &mut PointerInnerHandle,
button: u32,
state: ButtonState,
serial: u32,
time: u32,
) {
match state {
ButtonState::Pressed => self.buttons.push(button),
ButtonState::Released => self.buttons.retain(|b| *b != button),
}
handle.button(button, state, serial, time);
if self.buttons.is_empty() {
// no more buttons are pressed, release the grab
handle.unset_grab();
// restore the focus
let location = handle.current_location();
handle.motion(location, self.pending_focus.clone(), serial, time);
}
}
fn axis(&mut self, handle: &mut PointerInnerHandle, details: AxisFrame) {
handle.axis(details);
}
}