From 139d0f3992d15262e03f50c8c175d514b99f3a54 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Thu, 4 Oct 2018 17:40:03 +0200 Subject: [PATCH] wayland.seat: Pointer grabbing logic --- anvil/src/input_handler.rs | 47 ++-- anvil/src/window_map.rs | 7 +- src/wayland/seat/mod.rs | 3 +- src/wayland/seat/pointer.rs | 474 ++++++++++++++++++++++++++++++------ 4 files changed, 426 insertions(+), 105 deletions(-) diff --git a/anvil/src/input_handler.rs b/anvil/src/input_handler.rs index 217c5c6..07b71e4 100644 --- a/anvil/src/input_handler.rs +++ b/anvil/src/input_handler.rs @@ -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 InputHandler 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 InputHandler 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 InputHandler for AnvilInputHandler { }; let state = match evt.state() { input::MouseButtonState::Pressed => { - // change the keyboard focus - 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); + // 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 InputHandler 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); } } diff --git a/anvil/src/window_map.rs b/anvil/src/window_map.rs index 2290837..dc12e10 100644 --- a/anvil/src/window_map.rs +++ b/anvil/src/window_map.rs @@ -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( &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)) diff --git a/src/wayland/seat/mod.rs b/src/wayland/seat/mod.rs index 68d2860..c2a391d 100644 --- a/src/wayland/seat/mod.rs +++ b/src/wayland/seat/mod.rs @@ -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 { diff --git a/src/wayland/seat/pointer.rs b/src/wayland/seat/pointer.rs index fbccada..07610f3 100644 --- a/src/wayland/seat/pointer.rs +++ b/src/wayland/seat/pointer.rs @@ -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), + Borrowed, +} + struct PointerInternal { known_pointers: Vec>, - focus: Option>, + focus: Option<(Resource, (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, &Resource), { - 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(&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>, @@ -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(&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, f64, f64)>, serial: u32, time: u32) { - let mut guard = self.inner.lock().unwrap(); + pub fn motion( + &self, + location: (f64, f64), + focus: Option<(Resource, (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, (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(&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, (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, (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, + 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, (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, + current_focus: Option<(Resource, (f64, f64))>, + pending_focus: Option<(Resource, (f64, f64))>, +} + +impl PointerGrab for ClickGrab { + fn motion( + &mut self, + handle: &mut PointerInnerHandle, + location: (f64, f64), + focus: Option<(Resource, (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); + } +}