diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a32a845..282a8dd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ 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 +- On Wayland, add support for set_cursor_icon. # 0.20.0 Alpha 3 (2019-08-14) diff --git a/FEATURES.md b/FEATURES.md index 9610b7ba..d634eaf5 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -190,8 +190,8 @@ Legend: |----------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | |Mouse events |✔️ |▢[#63] |✔️ |✔️ |**N/A**|**N/A**|❓ | |Mouse set location |✔️ |✔️ |✔️ |❓ |**N/A**|**N/A**|❓ | -|Cursor grab |✔️ |▢[#165] |▢[#242] |❌[#306] |**N/A**|**N/A**|❓ | -|Cursor icon |✔️ |✔️ |✔️ |❌[#306] |**N/A**|**N/A**|❓ | +|Cursor grab |✔️ |▢[#165] |▢[#242] |✔️ |**N/A**|**N/A**|❓ | +|Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|❓ | |Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |❓ | |Touch pressure |✔️ |❌ |❌ |❌ |❌ |✔️ |❓ | |Multitouch |✔️ |❌ |✔️ |✔️ |❓ |✔️ |❓ | diff --git a/src/platform_impl/linux/wayland/event_loop.rs b/src/platform_impl/linux/wayland/event_loop.rs index 0d8a24d6..cbe08799 100644 --- a/src/platform_impl/linux/wayland/event_loop.rs +++ b/src/platform_impl/linux/wayland/event_loop.rs @@ -29,6 +29,7 @@ use crate::{ sticky_exit_callback, MonitorHandle as PlatformMonitorHandle, VideoMode as PlatformVideoMode, }, + window::CursorIcon, }; use super::{window::WindowStore, DeviceId, WindowId}; @@ -78,21 +79,23 @@ impl WindowEventsSink { } pub struct CursorManager { - pointer_constraints_proxy: Rc>>, + pointer_constraints_proxy: Arc>>, auto_themer: Option, pointers: Vec, locked_pointers: Vec, - cursor_visible: Rc>, + cursor_visible: bool, + current_cursor: CursorIcon, } impl CursorManager { - fn new(constraints: Rc>>) -> CursorManager { + fn new(constraints: Arc>>) -> CursorManager { CursorManager { pointer_constraints_proxy: constraints, auto_themer: None, pointers: Vec::new(), locked_pointers: Vec::new(), - cursor_visible: Rc::new(RefCell::new(true)), + cursor_visible: true, + current_cursor: CursorIcon::default(), } } @@ -108,29 +111,96 @@ impl CursorManager { self.auto_themer = Some(auto_themer); } - fn set_cursor_visible(&mut self, visible: bool) { + pub 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.set_cursor_icon_impl(self.current_cursor); } - (*self.cursor_visible.try_borrow_mut().unwrap()) = visible; + self.cursor_visible = visible; } - fn grab_pointer(&mut self, surface: Option<&WlSurface>) { - for lp in self.locked_pointers.drain(..) { - lp.destroy(); + /// A helper function to restore cursor styles on PtrEvent::Enter. + pub fn reload_cursor_style(&mut self) { + if !self.cursor_visible { + self.set_cursor_visible(false); + } else { + self.set_cursor_icon_impl(self.current_cursor); + } + } + + pub fn set_cursor_icon(&mut self, cursor: CursorIcon) { + if self.cursor_visible && cursor != self.current_cursor { + self.current_cursor = cursor; + + self.set_cursor_icon_impl(cursor); + } + } + + fn set_cursor_icon_impl(&mut self, cursor: CursorIcon) { + let cursor = match cursor { + CursorIcon::Alias => "link", + CursorIcon::Arrow => "arrow", + CursorIcon::Cell => "plus", + CursorIcon::Copy => "copy", + CursorIcon::Crosshair => "crosshair", + CursorIcon::Default => "left_ptr", + CursorIcon::Hand => "hand", + CursorIcon::Help => "question_arrow", + CursorIcon::Move => "move", + CursorIcon::Grab => "grab", + CursorIcon::Grabbing => "grabbing", + CursorIcon::Progress => "progress", + CursorIcon::AllScroll => "all-scroll", + CursorIcon::ContextMenu => "context-menu", + + CursorIcon::NoDrop => "no-drop", + CursorIcon::NotAllowed => "crossed_circle", + + // Resize cursors + CursorIcon::EResize => "right_side", + CursorIcon::NResize => "top_side", + CursorIcon::NeResize => "top_right_corner", + CursorIcon::NwResize => "top_left_corner", + CursorIcon::SResize => "bottom_side", + CursorIcon::SeResize => "bottom_right_corner", + CursorIcon::SwResize => "bottom_left_corner", + CursorIcon::WResize => "left_side", + CursorIcon::EwResize => "h_double_arrow", + CursorIcon::NsResize => "v_double_arrow", + CursorIcon::NwseResize => "bd_double_arrow", + CursorIcon::NeswResize => "fd_double_arrow", + CursorIcon::ColResize => "h_double_arrow", + CursorIcon::RowResize => "v_double_arrow", + + CursorIcon::Text => "text", + CursorIcon::VerticalText => "vertical-text", + + CursorIcon::Wait => "watch", + + CursorIcon::ZoomIn => "zoom-in", + CursorIcon::ZoomOut => "zoom-out", + }; + + for pointer in self.pointers.iter() { + // Ignore erros, since we don't want to fail hard in case we can't find a proper cursor + // in a given theme. + let _ = pointer.set_cursor(cursor, None); + } + } + + pub fn grab_pointer(&mut self, surface: Option<&WlSurface>) { + for locked_pointer in self.locked_pointers.drain(..) { + locked_pointer.destroy(); } if let Some(surface) = surface { for pointer in self.pointers.iter() { let locked_pointer = self .pointer_constraints_proxy - .try_borrow() + .try_lock() .unwrap() .as_ref() .and_then(|pointer_constraints| { @@ -155,13 +225,12 @@ pub struct EventLoop { inner_loop: ::calloop::EventLoop<()>, // The wayland display pub display: Arc, - // the output manager + // The output manager pub outputs: OutputMgr, - // our sink, shared with some handlers, buffering the events + // 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< @@ -174,17 +243,19 @@ pub struct EventLoop { // // We should only try and wake up the `EventLoop` if it still exists, so we hold Weak ptrs. pub struct EventLoopProxy { - user_sender: ::calloop::channel::Sender, + user_sender: calloop::channel::Sender, } pub struct EventLoopWindowTarget { - // the event queue + // The event queue pub evq: RefCell<::calloop::Source>, // The window store pub store: Arc>, - // the env + // The cursor manager + pub cursor_manager: Arc>, + // The env pub env: Environment, - // a cleanup switch to prune dead windows + // A cleanup switch to prune dead windows pub cleanup_needed: Arc>, // The wayland display pub display: Arc, @@ -229,7 +300,7 @@ impl EventLoop { }) .unwrap(); - let pointer_constraints_proxy = Rc::new(RefCell::new(None)); + let pointer_constraints_proxy = Arc::new(Mutex::new(None)); let mut seat_manager = SeatManager { sink: sink.clone(), @@ -238,11 +309,11 @@ impl EventLoop { store: store.clone(), seats: seats.clone(), kbd_sender, - cursor_manager: Rc::new(RefCell::new(CursorManager::new(pointer_constraints_proxy))), + cursor_manager: Arc::new(Mutex::new(CursorManager::new(pointer_constraints_proxy))), }; let cursor_manager = seat_manager.cursor_manager.clone(); - let cursor_manager2 = cursor_manager.clone(); + let cursor_manager_clone = cursor_manager.clone(); let shm_cell = Rc::new(RefCell::new(None)); let compositor_cell = Rc::new(RefCell::new(None)); @@ -275,7 +346,7 @@ impl EventLoop { }) .unwrap(); - *seat_manager.pointer_constraints_proxy.borrow_mut() = + *seat_manager.pointer_constraints_proxy.lock().unwrap() = Some(pointer_constraints_proxy); } if interface == "wl_shm" { @@ -298,7 +369,10 @@ impl EventLoop { 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); + cursor_manager_clone + .lock() + .unwrap() + .set_auto_themer(auto_themer); } if interface == "wl_seat" { @@ -333,13 +407,13 @@ impl EventLoop { }) .unwrap(); + let cursor_manager_clone = cursor_manager.clone(); Ok(EventLoop { inner_loop, sink, pending_user_events, display: display.clone(), outputs: env.outputs.clone(), - cursor_manager, _user_source: user_source, user_sender, _kbd_source: kbd_source, @@ -348,6 +422,7 @@ impl EventLoop { evq: RefCell::new(source), store, env, + cursor_manager: cursor_manager_clone, cleanup_needed: Arc::new(Mutex::new(false)), seats, display, @@ -369,7 +444,7 @@ impl EventLoop { F: 'static + FnMut(crate::event::Event, &RootELW, &mut ControlFlow), { self.run_return(callback); - ::std::process::exit(0); + std::process::exit(0); } pub fn run_return(&mut self, mut callback: F) @@ -579,17 +654,7 @@ impl EventLoop { } // process pending resize/refresh window_target.store.lock().unwrap().for_each( - |newsize, - size, - new_dpi, - refresh, - frame_refresh, - closed, - cursor_visible, - cursor_grab, - surface, - wid, - frame| { + |newsize, size, new_dpi, refresh, frame_refresh, closed, wid, frame| { if let Some(frame) = frame { if let Some((w, h)) = newsize { frame.resize(w, h); @@ -619,17 +684,6 @@ 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); - } }, ) } @@ -645,8 +699,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>, + pointer_constraints_proxy: Arc>>, + cursor_manager: Arc>, } impl SeatManager { @@ -695,7 +749,7 @@ struct SeatData { keyboard: Option, touch: Option, modifiers_tracker: Arc>, - cursor_manager: Rc>, + cursor_manager: Arc>, } impl SeatData { @@ -710,11 +764,12 @@ impl SeatData { self.sink.clone(), self.store.clone(), self.modifiers_tracker.clone(), - self.cursor_manager.borrow().cursor_visible.clone(), + self.cursor_manager.clone(), )); self.cursor_manager - .borrow_mut() + .lock() + .unwrap() .register_pointer(self.pointer.as_ref().unwrap().clone()); self.relative_pointer = self diff --git a/src/platform_impl/linux/wayland/pointer.rs b/src/platform_impl/linux/wayland/pointer.rs index 1a0cff9f..cad299e4 100644 --- a/src/platform_impl/linux/wayland/pointer.rs +++ b/src/platform_impl/linux/wayland/pointer.rs @@ -1,5 +1,3 @@ -use std::cell::RefCell; -use std::rc::Rc; use std::sync::{Arc, Mutex}; use crate::event::{ @@ -7,7 +5,11 @@ use crate::event::{ WindowEvent, }; -use super::{event_loop::WindowEventsSink, window::WindowStore, DeviceId}; +use super::{ + event_loop::{CursorManager, WindowEventsSink}, + window::WindowStore, + DeviceId, +}; use smithay_client_toolkit::reexports::client::protocol::{ wl_pointer::{self, Event as PtrEvent, WlPointer}, @@ -31,7 +33,7 @@ pub fn implement_pointer( sink: Arc>>, store: Arc>, modifiers_tracker: Arc>, - cursor_visible: Rc>, + cursor_manager: Arc>, ) -> WlPointer { seat.get_pointer(|pointer| { let mut mouse_focus = None; @@ -43,6 +45,7 @@ pub fn implement_pointer( move |evt, pointer| { let mut sink = sink.lock().unwrap(); let store = store.lock().unwrap(); + let mut cursor_manager = cursor_manager.lock().unwrap(); match evt { PtrEvent::Enter { surface, @@ -73,9 +76,7 @@ pub fn implement_pointer( ); } - if *cursor_visible.borrow() == false { - pointer.set_cursor(0, None, 0, 0); - } + cursor_manager.reload_cursor_style(); } PtrEvent::Leave { surface, .. } => { mouse_focus = None; diff --git a/src/platform_impl/linux/wayland/window.rs b/src/platform_impl/linux/wayland/window.rs index 9fdde832..f94b8177 100644 --- a/src/platform_impl/linux/wayland/window.rs +++ b/src/platform_impl/linux/wayland/window.rs @@ -1,6 +1,7 @@ use raw_window_handle::unix::WaylandHandle; use std::{ collections::VecDeque, + mem::replace, sync::{Arc, Mutex, Weak}, }; @@ -26,11 +27,12 @@ use smithay_client_toolkit::{ window::{ConceptFrame, Event as WEvent, State as WState, Theme, Window as SWindow}, }; -use super::{make_wid, EventLoopWindowTarget, MonitorHandle, WindowId}; +use super::{event_loop::CursorManager, make_wid, EventLoopWindowTarget, MonitorHandle, WindowId}; pub struct Window { surface: wl_surface::WlSurface, frame: Arc>>, + cursor_manager: Arc>, outputs: OutputMgr, // Access to info for all monitors size: Arc>, kill_switch: (Arc>, Arc>), @@ -38,8 +40,6 @@ pub struct Window { need_frame_refresh: Arc>, need_refresh: Arc>, fullscreen: Arc>, - cursor_grab_changed: Arc>>, - cursor_visible_changed: Arc>>, } impl Window { @@ -54,6 +54,7 @@ impl Window { let fullscreen = Arc::new(Mutex::new(false)); let window_store = evlp.store.clone(); + let cursor_manager = evlp.cursor_manager.clone(); let surface = evlp.env.create_surface(move |dpi, surface| { window_store.lock().unwrap().dpi_change(&surface, dpi); surface.set_buffer_scale(dpi); @@ -142,8 +143,6 @@ 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, @@ -152,8 +151,6 @@ 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), @@ -171,9 +168,8 @@ impl Window { kill_switch: (kill_switch, evlp.cleanup_needed.clone()), need_frame_refresh, need_refresh, + cursor_manager, fullscreen, - cursor_grab_changed, - cursor_visible_changed, }) } @@ -300,18 +296,25 @@ impl Window { } #[inline] - pub fn set_cursor_icon(&self, _cursor: CursorIcon) { - // TODO + pub fn set_cursor_icon(&self, cursor: CursorIcon) { + let mut cursor_manager = self.cursor_manager.lock().unwrap(); + cursor_manager.set_cursor_icon(cursor); } #[inline] pub fn set_cursor_visible(&self, visible: bool) { - *self.cursor_visible_changed.lock().unwrap() = Some(visible); + let mut cursor_manager = self.cursor_manager.lock().unwrap(); + cursor_manager.set_cursor_visible(visible); } #[inline] pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { - *self.cursor_grab_changed.lock().unwrap() = Some(grab); + let mut cursor_manager = self.cursor_manager.lock().unwrap(); + if grab { + cursor_manager.grab_pointer(Some(&self.surface)); + } else { + cursor_manager.grab_pointer(None); + } Ok(()) } @@ -371,8 +374,6 @@ 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>>, @@ -440,9 +441,6 @@ impl WindowStore { bool, bool, bool, - Option, - Option, - &wl_surface::WlSurface, WindowId, Option<&mut SWindow>, ), @@ -454,12 +452,9 @@ impl WindowStore { window.newsize.take(), &mut *(window.size.lock().unwrap()), window.new_dpi, - ::std::mem::replace(&mut *window.need_refresh.lock().unwrap(), false), - ::std::mem::replace(&mut *window.need_frame_refresh.lock().unwrap(), false), + replace(&mut *window.need_refresh.lock().unwrap(), false), + 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), );