diff --git a/CHANGELOG.md b/CHANGELOG.md index 5be8107c0b..7a32a84551 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - On X11, return dummy monitor data to avoid panicking when no monitors exist. - On X11, prevent stealing input focus when creating a new window. Only steal input focus when entering fullscreen mode. +- On Wayland, add support for set_cursor_visible and set_cursor_grab. - On Wayland, fixed DeviceEvents for relative mouse movement is not always produced # 0.20.0 Alpha 3 (2019-08-14) diff --git a/src/platform_impl/linux/wayland/event_loop.rs b/src/platform_impl/linux/wayland/event_loop.rs index 272fc832f3..0d8a24d6b8 100644 --- a/src/platform_impl/linux/wayland/event_loop.rs +++ b/src/platform_impl/linux/wayland/event_loop.rs @@ -7,11 +7,19 @@ use std::{ time::Instant, }; +use smithay_client_toolkit::reexports::protocols::unstable::pointer_constraints::v1::client::{ + zwp_locked_pointer_v1::ZwpLockedPointerV1, zwp_pointer_constraints_v1::ZwpPointerConstraintsV1, +}; use smithay_client_toolkit::reexports::protocols::unstable::relative_pointer::v1::client::{ zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1, zwp_relative_pointer_v1::ZwpRelativePointerV1, }; +use smithay_client_toolkit::pointer::{AutoPointer, AutoThemer}; +use smithay_client_toolkit::reexports::client::protocol::{ + wl_compositor::WlCompositor, wl_shm::WlShm, wl_surface::WlSurface, +}; + use crate::{ dpi::{PhysicalPosition, PhysicalSize}, event::ModifiersState, @@ -69,6 +77,79 @@ impl WindowEventsSink { } } +pub struct CursorManager { + pointer_constraints_proxy: Rc>>, + auto_themer: Option, + pointers: Vec, + locked_pointers: Vec, + cursor_visible: Rc>, +} + +impl CursorManager { + fn new(constraints: Rc>>) -> CursorManager { + CursorManager { + pointer_constraints_proxy: constraints, + auto_themer: None, + pointers: Vec::new(), + locked_pointers: Vec::new(), + cursor_visible: Rc::new(RefCell::new(true)), + } + } + + fn register_pointer(&mut self, pointer: wl_pointer::WlPointer) { + let auto_themer = self + .auto_themer + .as_ref() + .expect("AutoThemer not initialized. Server did not advertise shm or compositor?"); + self.pointers.push(auto_themer.theme_pointer(pointer)); + } + + fn set_auto_themer(&mut self, auto_themer: AutoThemer) { + self.auto_themer = Some(auto_themer); + } + + fn set_cursor_visible(&mut self, visible: bool) { + if !visible { + for pointer in self.pointers.iter() { + (**pointer).set_cursor(0, None, 0, 0); + } + } else { + for pointer in self.pointers.iter() { + pointer.set_cursor("left_ptr", None).unwrap(); + } + } + (*self.cursor_visible.try_borrow_mut().unwrap()) = visible; + } + + fn grab_pointer(&mut self, surface: Option<&WlSurface>) { + for lp in self.locked_pointers.drain(..) { + lp.destroy(); + } + + if let Some(surface) = surface { + for pointer in self.pointers.iter() { + let locked_pointer = self + .pointer_constraints_proxy + .try_borrow() + .unwrap() + .as_ref() + .and_then(|pointer_constraints| { + super::pointer::implement_locked_pointer( + surface, + &**pointer, + pointer_constraints, + ) + .ok() + }); + + if let Some(locked_pointer) = locked_pointer { + self.locked_pointers.push(locked_pointer); + } + } + } + } +} + pub struct EventLoop { // The loop inner_loop: ::calloop::EventLoop<()>, @@ -79,6 +160,8 @@ pub struct EventLoop { // our sink, shared with some handlers, buffering the events sink: Arc>>, pending_user_events: Rc>>, + // Utility for grabbing the cursor and changing visibility + cursor_manager: Rc>, _user_source: ::calloop::Source<::calloop::channel::Channel>, user_sender: ::calloop::channel::Sender, _kbd_source: ::calloop::Source< @@ -146,14 +229,24 @@ impl EventLoop { }) .unwrap(); + let pointer_constraints_proxy = Rc::new(RefCell::new(None)); + let mut seat_manager = SeatManager { sink: sink.clone(), relative_pointer_manager_proxy: Rc::new(RefCell::new(None)), + pointer_constraints_proxy: pointer_constraints_proxy.clone(), store: store.clone(), seats: seats.clone(), kbd_sender, + cursor_manager: Rc::new(RefCell::new(CursorManager::new(pointer_constraints_proxy))), }; + let cursor_manager = seat_manager.cursor_manager.clone(); + let cursor_manager2 = cursor_manager.clone(); + + let shm_cell = Rc::new(RefCell::new(None)); + let compositor_cell = Rc::new(RefCell::new(None)); + let env = Environment::from_display_with_cb( &display, &mut event_queue, @@ -175,6 +268,39 @@ impl EventLoop { .try_borrow_mut() .unwrap() = Some(relative_pointer_manager_proxy); } + if interface == "zwp_pointer_constraints_v1" { + let pointer_constraints_proxy = registry + .bind(version, id, move |pointer_constraints| { + pointer_constraints.implement_closure(|_, _| (), ()) + }) + .unwrap(); + + *seat_manager.pointer_constraints_proxy.borrow_mut() = + Some(pointer_constraints_proxy); + } + if interface == "wl_shm" { + let shm: WlShm = registry + .bind(version, id, move |shm| shm.implement_closure(|_, _| (), ())) + .unwrap(); + + (*shm_cell.borrow_mut()) = Some(shm); + } + if interface == "wl_compositor" { + let compositor: WlCompositor = registry + .bind(version, id, move |compositor| { + compositor.implement_closure(|_, _| (), ()) + }) + .unwrap(); + (*compositor_cell.borrow_mut()) = Some(compositor); + } + + if compositor_cell.borrow().is_some() && shm_cell.borrow().is_some() { + let compositor = compositor_cell.borrow_mut().take().unwrap(); + let shm = shm_cell.borrow_mut().take().unwrap(); + let auto_themer = AutoThemer::init(None, compositor, &shm); + cursor_manager2.borrow_mut().set_auto_themer(auto_themer); + } + if interface == "wl_seat" { seat_manager.add_seat(id, version, registry) } @@ -213,6 +339,7 @@ impl EventLoop { pending_user_events, display: display.clone(), outputs: env.outputs.clone(), + cursor_manager, _user_source: user_source, user_sender, _kbd_source: kbd_source, @@ -452,7 +579,17 @@ impl EventLoop { } // process pending resize/refresh window_target.store.lock().unwrap().for_each( - |newsize, size, new_dpi, refresh, frame_refresh, closed, wid, frame| { + |newsize, + size, + new_dpi, + refresh, + frame_refresh, + closed, + cursor_visible, + cursor_grab, + surface, + wid, + frame| { if let Some(frame) = frame { if let Some((w, h)) = newsize { frame.resize(w, h); @@ -482,6 +619,17 @@ impl EventLoop { if closed { sink.send_window_event(crate::event::WindowEvent::CloseRequested, wid); } + if let Some(grab) = cursor_grab { + self.cursor_manager.borrow_mut().grab_pointer(if grab { + Some(surface) + } else { + None + }); + } + + if let Some(visible) = cursor_visible { + self.cursor_manager.borrow_mut().set_cursor_visible(visible); + } }, ) } @@ -497,6 +645,8 @@ struct SeatManager { seats: Arc>>, kbd_sender: ::calloop::channel::Sender<(crate::event::WindowEvent, super::WindowId)>, relative_pointer_manager_proxy: Rc>>, + pointer_constraints_proxy: Rc>>, + cursor_manager: Rc>, } impl SeatManager { @@ -513,6 +663,7 @@ impl SeatManager { touch: None, kbd_sender: self.kbd_sender.clone(), modifiers_tracker: Arc::new(Mutex::new(ModifiersState::default())), + cursor_manager: self.cursor_manager.clone(), }; let seat = registry .bind(min(version, 5), id, move |seat| { @@ -544,6 +695,7 @@ struct SeatData { keyboard: Option, touch: Option, modifiers_tracker: Arc>, + cursor_manager: Rc>, } impl SeatData { @@ -558,8 +710,13 @@ impl SeatData { self.sink.clone(), self.store.clone(), self.modifiers_tracker.clone(), + self.cursor_manager.borrow().cursor_visible.clone(), )); + self.cursor_manager + .borrow_mut() + .register_pointer(self.pointer.as_ref().unwrap().clone()); + self.relative_pointer = self .relative_pointer_manager_proxy .try_borrow() diff --git a/src/platform_impl/linux/wayland/pointer.rs b/src/platform_impl/linux/wayland/pointer.rs index 7ffcdd72df..1a0cff9f66 100644 --- a/src/platform_impl/linux/wayland/pointer.rs +++ b/src/platform_impl/linux/wayland/pointer.rs @@ -1,3 +1,5 @@ +use std::cell::RefCell; +use std::rc::Rc; use std::sync::{Arc, Mutex}; use crate::event::{ @@ -17,11 +19,19 @@ use smithay_client_toolkit::reexports::protocols::unstable::relative_pointer::v1 zwp_relative_pointer_v1::ZwpRelativePointerV1, }; +use smithay_client_toolkit::reexports::protocols::unstable::pointer_constraints::v1::client::{ + zwp_locked_pointer_v1::ZwpLockedPointerV1, zwp_pointer_constraints_v1::Lifetime, + zwp_pointer_constraints_v1::ZwpPointerConstraintsV1, +}; + +use smithay_client_toolkit::reexports::client::protocol::wl_surface::WlSurface; + pub fn implement_pointer( seat: &wl_seat::WlSeat, sink: Arc>>, store: Arc>, modifiers_tracker: Arc>, + cursor_visible: Rc>, ) -> WlPointer { seat.get_pointer(|pointer| { let mut mouse_focus = None; @@ -62,6 +72,10 @@ pub fn implement_pointer( wid, ); } + + if *cursor_visible.borrow() == false { + pointer.set_cursor(0, None, 0, 0); + } } PtrEvent::Leave { surface, .. } => { mouse_focus = None; @@ -241,3 +255,13 @@ pub fn implement_relative_pointer( ) }) } + +pub fn implement_locked_pointer( + surface: &WlSurface, + pointer: &WlPointer, + constraints: &ZwpPointerConstraintsV1, +) -> Result { + constraints.lock_pointer(surface, pointer, None, Lifetime::Persistent.to_raw(), |c| { + c.implement_closure(|_, _| (), ()) + }) +} diff --git a/src/platform_impl/linux/wayland/window.rs b/src/platform_impl/linux/wayland/window.rs index ddd4b4b9f0..9fdde832e3 100644 --- a/src/platform_impl/linux/wayland/window.rs +++ b/src/platform_impl/linux/wayland/window.rs @@ -38,6 +38,8 @@ pub struct Window { need_frame_refresh: Arc>, need_refresh: Arc>, fullscreen: Arc>, + cursor_grab_changed: Arc>>, + cursor_visible_changed: Arc>>, } impl Window { @@ -140,6 +142,8 @@ impl Window { let need_frame_refresh = Arc::new(Mutex::new(true)); let frame = Arc::new(Mutex::new(frame)); let need_refresh = Arc::new(Mutex::new(true)); + let cursor_grab_changed = Arc::new(Mutex::new(None)); + let cursor_visible_changed = Arc::new(Mutex::new(None)); evlp.store.lock().unwrap().windows.push(InternalWindow { closed: false, @@ -148,6 +152,8 @@ impl Window { need_refresh: need_refresh.clone(), fullscreen: fullscreen.clone(), need_frame_refresh: need_frame_refresh.clone(), + cursor_grab_changed: cursor_grab_changed.clone(), + cursor_visible_changed: cursor_visible_changed.clone(), surface: surface.clone(), kill_switch: kill_switch.clone(), frame: Arc::downgrade(&frame), @@ -166,6 +172,8 @@ impl Window { need_frame_refresh, need_refresh, fullscreen, + cursor_grab_changed, + cursor_visible_changed, }) } @@ -297,13 +305,14 @@ impl Window { } #[inline] - pub fn set_cursor_visible(&self, _visible: bool) { - // TODO: This isn't possible on Wayland yet + pub fn set_cursor_visible(&self, visible: bool) { + *self.cursor_visible_changed.lock().unwrap() = Some(visible); } #[inline] - pub fn set_cursor_grab(&self, _grab: bool) -> Result<(), ExternalError> { - Err(ExternalError::NotSupported(NotSupportedError::new())) + pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { + *self.cursor_grab_changed.lock().unwrap() = Some(grab); + Ok(()) } #[inline] @@ -362,6 +371,8 @@ struct InternalWindow { need_refresh: Arc>, fullscreen: Arc>, need_frame_refresh: Arc>, + cursor_grab_changed: Arc>>, + cursor_visible_changed: Arc>>, closed: bool, kill_switch: Arc>, frame: Weak>>, @@ -429,6 +440,9 @@ impl WindowStore { bool, bool, bool, + Option, + Option, + &wl_surface::WlSurface, WindowId, Option<&mut SWindow>, ), @@ -443,6 +457,9 @@ impl WindowStore { ::std::mem::replace(&mut *window.need_refresh.lock().unwrap(), false), ::std::mem::replace(&mut *window.need_frame_refresh.lock().unwrap(), false), window.closed, + window.cursor_visible_changed.lock().unwrap().take(), + window.cursor_grab_changed.lock().unwrap().take(), + &window.surface, make_wid(&window.surface), opt_mutex_lock.as_mut().map(|m| &mut **m), ); diff --git a/src/window.rs b/src/window.rs index 1eaeec477f..248c8317d3 100644 --- a/src/window.rs +++ b/src/window.rs @@ -655,6 +655,8 @@ impl Window { /// /// - **macOS:** This presently merely locks the cursor in a fixed location, which looks visually /// awkward. + /// - **Wayland:** This presently merely locks the cursor in a fixed location, which looks visually + /// awkward. /// - **Android:** Has no effect. /// - **iOS:** Always returns an Err. #[inline] @@ -670,6 +672,7 @@ impl Window { /// /// - **Windows:** The cursor is only hidden within the confines of the window. /// - **X11:** The cursor is only hidden within the confines of the window. + /// - **Wayland:** The cursor is only hidden within the confines of the window. /// - **macOS:** The cursor is hidden as long as the window has input focus, even if the cursor is /// outside of the window. /// - **iOS:** Has no effect.