//! All pointer related handling. use std::cell::{Cell, RefCell}; use std::rc::{Rc, Weak}; use sctk::reexports::client::protocol::wl_pointer::WlPointer; use sctk::reexports::client::protocol::wl_seat::WlSeat; use sctk::reexports::client::protocol::wl_surface::WlSurface; use sctk::reexports::client::Attached; use sctk::reexports::protocols::unstable::relative_pointer::v1::client::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1; use sctk::reexports::protocols::unstable::relative_pointer::v1::client::zwp_relative_pointer_v1::ZwpRelativePointerV1; use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_pointer_constraints_v1::{ZwpPointerConstraintsV1, Lifetime}; use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_confined_pointer_v1::ZwpConfinedPointerV1; use sctk::seat::pointer::{ThemeManager, ThemedPointer}; use sctk::window::Window; use crate::event::ModifiersState; use crate::platform_impl::wayland::event_loop::WinitState; use crate::platform_impl::wayland::window::WinitFrame; use crate::window::CursorIcon; mod data; mod handlers; use data::PointerData; /// A proxy to Wayland pointer, which serves requests from a `WindowHandle`. pub struct WinitPointer { pointer: ThemedPointer, /// Create confined pointers. pointer_constraints: Option>, /// Cursor to handle confine requests. confined_pointer: Weak>>, /// Latest observed serial in pointer events. /// used by Window::start_interactive_move() latest_serial: Rc>, /// Latest observed serial in pointer enter events. /// used by Window::set_cursor() latest_enter_serial: Rc>, /// Seat. seat: WlSeat, } impl PartialEq for WinitPointer { fn eq(&self, other: &Self) -> bool { *self.pointer == *other.pointer } } impl Eq for WinitPointer {} impl WinitPointer { /// Set the cursor icon. /// /// Providing `None` will hide the cursor. pub fn set_cursor(&self, cursor_icon: Option) { let cursor_icon = match cursor_icon { Some(cursor_icon) => cursor_icon, None => { // Hide the cursor. // WlPointer::set_cursor() expects the serial of the last *enter* // event (compare to to start_interactive_move()). (*self.pointer).set_cursor(self.latest_enter_serial.get(), None, 0, 0); return; } }; let cursors: &[&str] = match cursor_icon { CursorIcon::Alias => &["link"], CursorIcon::Arrow => &["arrow"], CursorIcon::Cell => &["plus"], CursorIcon::Copy => &["copy"], CursorIcon::Crosshair => &["crosshair"], CursorIcon::Default => &["left_ptr"], CursorIcon::Hand => &["hand2", "hand1"], CursorIcon::Help => &["question_arrow"], CursorIcon::Move => &["move"], CursorIcon::Grab => &["openhand", "grab"], CursorIcon::Grabbing => &["closedhand", "grabbing"], CursorIcon::Progress => &["progress"], CursorIcon::AllScroll => &["all-scroll"], CursorIcon::ContextMenu => &["context-menu"], CursorIcon::NoDrop => &["no-drop", "circle"], 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", "size_fdiag"], CursorIcon::NeswResize => &["fd_double_arrow", "size_bdiag"], CursorIcon::ColResize => &["split_h", "h_double_arrow"], CursorIcon::RowResize => &["split_v", "v_double_arrow"], CursorIcon::Text => &["text", "xterm"], CursorIcon::VerticalText => &["vertical-text"], CursorIcon::Wait => &["watch"], CursorIcon::ZoomIn => &["zoom-in"], CursorIcon::ZoomOut => &["zoom-out"], }; let serial = Some(self.latest_enter_serial.get()); for cursor in cursors { if self.pointer.set_cursor(cursor, serial).is_ok() { return; } } warn!("Failed to set cursor to {:?}", cursor_icon); } /// Confine the pointer to a surface. pub fn confine(&self, surface: &WlSurface) { let pointer_constraints = match &self.pointer_constraints { Some(pointer_constraints) => pointer_constraints, None => return, }; let confined_pointer = match self.confined_pointer.upgrade() { Some(confined_pointer) => confined_pointer, // A pointer is gone. None => return, }; *confined_pointer.borrow_mut() = Some(init_confined_pointer( pointer_constraints, surface, &*self.pointer, )); } /// Tries to unconfine the pointer if the current pointer is confined. pub fn unconfine(&self) { let confined_pointer = match self.confined_pointer.upgrade() { Some(confined_pointer) => confined_pointer, // A pointer is gone. None => return, }; let mut confined_pointer = confined_pointer.borrow_mut(); if let Some(confined_pointer) = confined_pointer.take() { confined_pointer.destroy(); } } pub fn drag_window(&self, window: &Window) { // WlPointer::setart_interactive_move() expects the last serial of *any* // pointer event (compare to set_cursor()). window.start_interactive_move(&self.seat, self.latest_serial.get()); } } /// A pointer wrapper for easy releasing and managing pointers. pub(super) struct Pointers { /// A pointer itself. pointer: ThemedPointer, /// A relative pointer handler. relative_pointer: Option, /// Confined pointer. confined_pointer: Rc>>, } impl Pointers { pub(super) fn new( seat: &Attached, theme_manager: &ThemeManager, relative_pointer_manager: &Option>, pointer_constraints: &Option>, modifiers_state: Rc>, ) -> Self { let confined_pointer = Rc::new(RefCell::new(None)); let pointer_data = Rc::new(RefCell::new(PointerData::new( confined_pointer.clone(), pointer_constraints.clone(), modifiers_state, ))); let pointer_seat = seat.detach(); let pointer = theme_manager.theme_pointer_with_impl( seat, move |event, pointer, mut dispatch_data| { let winit_state = dispatch_data.get::().unwrap(); handlers::handle_pointer( pointer, event, &pointer_data, winit_state, pointer_seat.clone(), ); }, ); // Setup relative_pointer if it's available. let relative_pointer = relative_pointer_manager .as_ref() .map(|relative_pointer_manager| { init_relative_pointer(relative_pointer_manager, &*pointer) }); Self { pointer, relative_pointer, confined_pointer, } } } impl Drop for Pointers { fn drop(&mut self) { // Drop relative pointer. if let Some(relative_pointer) = self.relative_pointer.take() { relative_pointer.destroy(); } // Drop confined pointer. if let Some(confined_pointer) = self.confined_pointer.borrow_mut().take() { confined_pointer.destroy(); } // Drop the pointer itself in case it's possible. if self.pointer.as_ref().version() >= 3 { self.pointer.release(); } } } pub(super) fn init_relative_pointer( relative_pointer_manager: &ZwpRelativePointerManagerV1, pointer: &WlPointer, ) -> ZwpRelativePointerV1 { let relative_pointer = relative_pointer_manager.get_relative_pointer(pointer); relative_pointer.quick_assign(move |_, event, mut dispatch_data| { let winit_state = dispatch_data.get::().unwrap(); handlers::handle_relative_pointer(event, winit_state); }); relative_pointer.detach() } pub(super) fn init_confined_pointer( pointer_constraints: &Attached, surface: &WlSurface, pointer: &WlPointer, ) -> ZwpConfinedPointerV1 { let confined_pointer = pointer_constraints.confine_pointer(surface, pointer, None, Lifetime::Persistent); confined_pointer.quick_assign(move |_, _, _| {}); confined_pointer.detach() }