Refine Window::set_cursor_grab API
This commit renames `Window::set_cursor_grab` to `Window::set_cursor_grab_mode`. The new API now accepts enumeration to control the way cursor grab is performed. The value could be: `lock`, `confine`, or `none`. This commit also implements `Window::set_cursor_position` for Wayland, since it's tied to locked cursor. Implements API from #1677.
This commit is contained in:
parent
8ef9fe44c7
commit
9e6f666616
19 changed files with 359 additions and 131 deletions
|
|
@ -24,23 +24,23 @@ use sctk::shm::ShmHandler;
|
|||
/// Set of extra features that are supported by the compositor.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct WindowingFeatures {
|
||||
cursor_grab: bool,
|
||||
pointer_constraints: bool,
|
||||
xdg_activation: bool,
|
||||
}
|
||||
|
||||
impl WindowingFeatures {
|
||||
/// Create `WindowingFeatures` based on the presented interfaces.
|
||||
pub fn new(env: &Environment<WinitEnv>) -> Self {
|
||||
let cursor_grab = env.get_global::<ZwpPointerConstraintsV1>().is_some();
|
||||
let pointer_constraints = env.get_global::<ZwpPointerConstraintsV1>().is_some();
|
||||
let xdg_activation = env.get_global::<XdgActivationV1>().is_some();
|
||||
Self {
|
||||
cursor_grab,
|
||||
pointer_constraints,
|
||||
xdg_activation,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cursor_grab(&self) -> bool {
|
||||
self.cursor_grab
|
||||
pub fn pointer_constraints(&self) -> bool {
|
||||
self.pointer_constraints
|
||||
}
|
||||
|
||||
pub fn xdg_activation(&self) -> bool {
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ use std::rc::Rc;
|
|||
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::Attached;
|
||||
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_pointer_constraints_v1::{ZwpPointerConstraintsV1};
|
||||
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_pointer_constraints_v1::ZwpPointerConstraintsV1;
|
||||
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_confined_pointer_v1::ZwpConfinedPointerV1;
|
||||
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_locked_pointer_v1::ZwpLockedPointerV1;
|
||||
|
||||
use crate::event::{ModifiersState, TouchPhase};
|
||||
|
||||
|
|
@ -25,6 +26,7 @@ pub(super) struct PointerData {
|
|||
pub pointer_constraints: Option<Attached<ZwpPointerConstraintsV1>>,
|
||||
|
||||
pub confined_pointer: Rc<RefCell<Option<ZwpConfinedPointerV1>>>,
|
||||
pub locked_pointer: Rc<RefCell<Option<ZwpLockedPointerV1>>>,
|
||||
|
||||
/// Latest observed serial in pointer events.
|
||||
pub latest_serial: Rc<Cell<u32>>,
|
||||
|
|
@ -39,6 +41,7 @@ pub(super) struct PointerData {
|
|||
impl PointerData {
|
||||
pub fn new(
|
||||
confined_pointer: Rc<RefCell<Option<ZwpConfinedPointerV1>>>,
|
||||
locked_pointer: Rc<RefCell<Option<ZwpLockedPointerV1>>>,
|
||||
pointer_constraints: Option<Attached<ZwpPointerConstraintsV1>>,
|
||||
modifiers_state: Rc<RefCell<ModifiersState>>,
|
||||
) -> Self {
|
||||
|
|
@ -47,6 +50,7 @@ impl PointerData {
|
|||
latest_serial: Rc::new(Cell::new(0)),
|
||||
latest_enter_serial: Rc::new(Cell::new(0)),
|
||||
confined_pointer,
|
||||
locked_pointer,
|
||||
modifiers_state,
|
||||
pointer_constraints,
|
||||
axis_data: AxisData::new(),
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ pub(super) fn handle_pointer(
|
|||
let winit_pointer = WinitPointer {
|
||||
pointer,
|
||||
confined_pointer: Rc::downgrade(&pointer_data.confined_pointer),
|
||||
locked_pointer: Rc::downgrade(&pointer_data.locked_pointer),
|
||||
pointer_constraints: pointer_data.pointer_constraints.clone(),
|
||||
latest_serial: pointer_data.latest_serial.clone(),
|
||||
latest_enter_serial: pointer_data.latest_enter_serial.clone(),
|
||||
|
|
@ -104,6 +105,7 @@ pub(super) fn handle_pointer(
|
|||
let winit_pointer = WinitPointer {
|
||||
pointer,
|
||||
confined_pointer: Rc::downgrade(&pointer_data.confined_pointer),
|
||||
locked_pointer: Rc::downgrade(&pointer_data.locked_pointer),
|
||||
pointer_constraints: pointer_data.pointer_constraints.clone(),
|
||||
latest_serial: pointer_data.latest_serial.clone(),
|
||||
latest_enter_serial: pointer_data.latest_enter_serial.clone(),
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use sctk::reexports::protocols::unstable::relative_pointer::v1::client::zwp_rela
|
|||
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::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_locked_pointer_v1::ZwpLockedPointerV1;
|
||||
|
||||
use sctk::seat::pointer::{ThemeManager, ThemedPointer};
|
||||
use sctk::window::Window;
|
||||
|
|
@ -35,9 +36,13 @@ pub struct WinitPointer {
|
|||
/// Cursor to handle confine requests.
|
||||
confined_pointer: Weak<RefCell<Option<ZwpConfinedPointerV1>>>,
|
||||
|
||||
/// Cursor to handle locked requests.
|
||||
locked_pointer: Weak<RefCell<Option<ZwpLockedPointerV1>>>,
|
||||
|
||||
/// Latest observed serial in pointer events.
|
||||
/// used by Window::start_interactive_move()
|
||||
latest_serial: Rc<Cell<u32>>,
|
||||
|
||||
/// Latest observed serial in pointer enter events.
|
||||
/// used by Window::set_cursor()
|
||||
latest_enter_serial: Rc<Cell<u32>>,
|
||||
|
|
@ -157,6 +162,52 @@ impl WinitPointer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn lock(&self, surface: &WlSurface) {
|
||||
let pointer_constraints = match &self.pointer_constraints {
|
||||
Some(pointer_constraints) => pointer_constraints,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let locked_pointer = match self.locked_pointer.upgrade() {
|
||||
Some(locked_pointer) => locked_pointer,
|
||||
// A pointer is gone.
|
||||
None => return,
|
||||
};
|
||||
|
||||
*locked_pointer.borrow_mut() = Some(init_locked_pointer(
|
||||
pointer_constraints,
|
||||
surface,
|
||||
&*self.pointer,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn unlock(&self) {
|
||||
let locked_pointer = match self.locked_pointer.upgrade() {
|
||||
Some(locked_pointer) => locked_pointer,
|
||||
// A pointer is gone.
|
||||
None => return,
|
||||
};
|
||||
|
||||
let mut locked_pointer = locked_pointer.borrow_mut();
|
||||
|
||||
if let Some(locked_pointer) = locked_pointer.take() {
|
||||
locked_pointer.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_cursor_position(&self, surface_x: u32, surface_y: u32) {
|
||||
let locked_pointer = match self.locked_pointer.upgrade() {
|
||||
Some(locked_pointer) => locked_pointer,
|
||||
// A pointer is gone.
|
||||
None => return,
|
||||
};
|
||||
|
||||
let locked_pointer = locked_pointer.borrow_mut();
|
||||
if let Some(locked_pointer) = locked_pointer.as_ref() {
|
||||
locked_pointer.set_cursor_position_hint(surface_x.into(), surface_y.into());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drag_window(&self, window: &Window<WinitFrame>) {
|
||||
// WlPointer::setart_interactive_move() expects the last serial of *any*
|
||||
// pointer event (compare to set_cursor()).
|
||||
|
|
@ -174,6 +225,9 @@ pub(super) struct Pointers {
|
|||
|
||||
/// Confined pointer.
|
||||
confined_pointer: Rc<RefCell<Option<ZwpConfinedPointerV1>>>,
|
||||
|
||||
/// Locked pointer.
|
||||
locked_pointer: Rc<RefCell<Option<ZwpLockedPointerV1>>>,
|
||||
}
|
||||
|
||||
impl Pointers {
|
||||
|
|
@ -185,11 +239,15 @@ impl Pointers {
|
|||
modifiers_state: Rc<RefCell<ModifiersState>>,
|
||||
) -> Self {
|
||||
let confined_pointer = Rc::new(RefCell::new(None));
|
||||
let locked_pointer = Rc::new(RefCell::new(None));
|
||||
|
||||
let pointer_data = Rc::new(RefCell::new(PointerData::new(
|
||||
confined_pointer.clone(),
|
||||
locked_pointer.clone(),
|
||||
pointer_constraints.clone(),
|
||||
modifiers_state,
|
||||
)));
|
||||
|
||||
let pointer_seat = seat.detach();
|
||||
let pointer = theme_manager.theme_pointer_with_impl(
|
||||
seat,
|
||||
|
|
@ -216,6 +274,7 @@ impl Pointers {
|
|||
pointer,
|
||||
relative_pointer,
|
||||
confined_pointer,
|
||||
locked_pointer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -232,6 +291,11 @@ impl Drop for Pointers {
|
|||
confined_pointer.destroy();
|
||||
}
|
||||
|
||||
// Drop lock ponter.
|
||||
if let Some(locked_pointer) = self.locked_pointer.borrow_mut().take() {
|
||||
locked_pointer.destroy();
|
||||
}
|
||||
|
||||
// Drop the pointer itself in case it's possible.
|
||||
if self.pointer.as_ref().version() >= 3 {
|
||||
self.pointer.release();
|
||||
|
|
@ -264,3 +328,16 @@ pub(super) fn init_confined_pointer(
|
|||
|
||||
confined_pointer.detach()
|
||||
}
|
||||
|
||||
pub(super) fn init_locked_pointer(
|
||||
pointer_constraints: &Attached<ZwpPointerConstraintsV1>,
|
||||
surface: &WlSurface,
|
||||
pointer: &WlPointer,
|
||||
) -> ZwpLockedPointerV1 {
|
||||
let locked_pointer =
|
||||
pointer_constraints.lock_pointer(surface, pointer, None, Lifetime::Persistent);
|
||||
|
||||
locked_pointer.quick_assign(move |_, _, _| {});
|
||||
|
||||
locked_pointer.detach()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@ use crate::platform_impl::{
|
|||
MonitorHandle as PlatformMonitorHandle, OsError,
|
||||
PlatformSpecificWindowBuilderAttributes as PlatformAttributes,
|
||||
};
|
||||
use crate::window::{CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes};
|
||||
use crate::window::{
|
||||
CursorGrabMode, CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes,
|
||||
};
|
||||
|
||||
use super::env::WindowingFeatures;
|
||||
use super::event_loop::WinitState;
|
||||
|
|
@ -72,6 +74,9 @@ pub struct Window {
|
|||
|
||||
/// Whether the window is decorated.
|
||||
decorated: AtomicBool,
|
||||
|
||||
/// Grabbing mode.
|
||||
cursor_grab_mode: Mutex<CursorGrabMode>,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
|
|
@ -285,6 +290,7 @@ impl Window {
|
|||
windowing_features,
|
||||
resizeable: AtomicBool::new(attributes.resizable),
|
||||
decorated: AtomicBool::new(attributes.decorations),
|
||||
cursor_grab_mode: Mutex::new(CursorGrabMode::None),
|
||||
};
|
||||
|
||||
Ok(window)
|
||||
|
|
@ -474,12 +480,17 @@ impl Window {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
|
||||
if !self.windowing_features.cursor_grab() {
|
||||
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||
if !self.windowing_features.pointer_constraints() {
|
||||
if mode == CursorGrabMode::None {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
return Err(ExternalError::NotSupported(NotSupportedError::new()));
|
||||
}
|
||||
|
||||
self.send_request(WindowRequest::GrabCursor(grab));
|
||||
*self.cursor_grab_mode.lock().unwrap() = mode;
|
||||
self.send_request(WindowRequest::SetCursorGrabMode(mode));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -494,15 +505,19 @@ impl Window {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, _: Position) -> Result<(), ExternalError> {
|
||||
// XXX This is possible if the locked pointer is being used. We don't have any
|
||||
// API for that right now, but it could be added in
|
||||
// https://github.com/rust-windowing/winit/issues/1677.
|
||||
//
|
||||
// This function is essential for the locked pointer API.
|
||||
//
|
||||
// See pointer-constraints-unstable-v1.xml.
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> {
|
||||
// Positon can be set only for locked cursor.
|
||||
if *self.cursor_grab_mode.lock().unwrap() != CursorGrabMode::Locked {
|
||||
return Err(ExternalError::Os(os_error!(OsError::WaylandMisc(
|
||||
"cursor position can be set only for locked cursor."
|
||||
))));
|
||||
}
|
||||
|
||||
let scale_factor = self.scale_factor() as f64;
|
||||
let position = position.to_logical(scale_factor);
|
||||
self.send_request(WindowRequest::SetLockedCursorPosition(position));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use crate::platform_impl::wayland::event_loop::{EventSink, WinitState};
|
|||
use crate::platform_impl::wayland::seat::pointer::WinitPointer;
|
||||
use crate::platform_impl::wayland::seat::text_input::TextInputHandler;
|
||||
use crate::platform_impl::wayland::WindowId;
|
||||
use crate::window::{CursorIcon, Theme, UserAttentionType};
|
||||
use crate::window::{CursorGrabMode, CursorIcon, Theme, UserAttentionType};
|
||||
|
||||
use super::WinitFrame;
|
||||
|
||||
|
|
@ -40,8 +40,11 @@ pub enum WindowRequest {
|
|||
/// Change the cursor icon.
|
||||
NewCursorIcon(CursorIcon),
|
||||
|
||||
/// Grab cursor.
|
||||
GrabCursor(bool),
|
||||
/// Change cursor grabbing mode.
|
||||
SetCursorGrabMode(CursorGrabMode),
|
||||
|
||||
/// Set cursor position.
|
||||
SetLockedCursorPosition(LogicalPosition<u32>),
|
||||
|
||||
/// Drag window.
|
||||
DragWindow,
|
||||
|
|
@ -172,7 +175,7 @@ pub struct WindowHandle {
|
|||
cursor_visible: Cell<bool>,
|
||||
|
||||
/// Cursor confined to the surface.
|
||||
confined: Cell<bool>,
|
||||
cursor_grab_mode: Cell<CursorGrabMode>,
|
||||
|
||||
/// Pointers over the current surface.
|
||||
pointers: Vec<WinitPointer>,
|
||||
|
|
@ -208,7 +211,7 @@ impl WindowHandle {
|
|||
pending_window_requests,
|
||||
cursor_icon: Cell::new(CursorIcon::Default),
|
||||
is_resizable: Cell::new(true),
|
||||
confined: Cell::new(false),
|
||||
cursor_grab_mode: Cell::new(CursorGrabMode::None),
|
||||
cursor_visible: Cell::new(true),
|
||||
pointers: Vec::new(),
|
||||
text_inputs: Vec::new(),
|
||||
|
|
@ -219,24 +222,37 @@ impl WindowHandle {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_cursor_grab(&self, grab: bool) {
|
||||
pub fn set_cursor_grab(&self, mode: CursorGrabMode) {
|
||||
// The new requested state matches the current confine status, return.
|
||||
if self.confined.get() == grab {
|
||||
let old_mode = self.cursor_grab_mode.replace(mode);
|
||||
if old_mode == mode {
|
||||
return;
|
||||
}
|
||||
|
||||
self.confined.replace(grab);
|
||||
// Clear old pointer data.
|
||||
match old_mode {
|
||||
CursorGrabMode::None => (),
|
||||
CursorGrabMode::Confined => self.pointers.iter().for_each(|p| p.unconfine()),
|
||||
CursorGrabMode::Locked => self.pointers.iter().for_each(|p| p.unlock()),
|
||||
}
|
||||
|
||||
for pointer in self.pointers.iter() {
|
||||
if self.confined.get() {
|
||||
let surface = self.window.surface();
|
||||
pointer.confine(surface);
|
||||
} else {
|
||||
pointer.unconfine();
|
||||
let surface = self.window.surface();
|
||||
match mode {
|
||||
CursorGrabMode::Locked => self.pointers.iter().for_each(|p| p.lock(surface)),
|
||||
CursorGrabMode::Confined => self.pointers.iter().for_each(|p| p.confine(surface)),
|
||||
CursorGrabMode::None => {
|
||||
// Current lock/confine was already removed.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_locked_cursor_position(&self, position: LogicalPosition<u32>) {
|
||||
// XXX the cursor locking is ensured inside `Window`.
|
||||
self.pointers
|
||||
.iter()
|
||||
.for_each(|p| p.set_cursor_position(position.x, position.y));
|
||||
}
|
||||
|
||||
pub fn set_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
let xdg_activation = match self.xdg_activation.as_ref() {
|
||||
None => return,
|
||||
|
|
@ -284,10 +300,13 @@ impl WindowHandle {
|
|||
let position = self.pointers.iter().position(|p| *p == pointer);
|
||||
|
||||
if position.is_none() {
|
||||
if self.confined.get() {
|
||||
let surface = self.window.surface();
|
||||
pointer.confine(surface);
|
||||
let surface = self.window.surface();
|
||||
match self.cursor_grab_mode.get() {
|
||||
CursorGrabMode::None => (),
|
||||
CursorGrabMode::Locked => pointer.lock(surface),
|
||||
CursorGrabMode::Confined => pointer.confine(surface),
|
||||
}
|
||||
|
||||
self.pointers.push(pointer);
|
||||
}
|
||||
|
||||
|
|
@ -302,9 +321,11 @@ impl WindowHandle {
|
|||
if let Some(position) = position {
|
||||
let pointer = self.pointers.remove(position);
|
||||
|
||||
// Drop the confined pointer.
|
||||
if self.confined.get() {
|
||||
pointer.unconfine();
|
||||
// Drop the grabbing mode.
|
||||
match self.cursor_grab_mode.get() {
|
||||
CursorGrabMode::None => (),
|
||||
CursorGrabMode::Locked => pointer.unlock(),
|
||||
CursorGrabMode::Confined => pointer.unconfine(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -428,8 +449,11 @@ pub fn handle_window_requests(winit_state: &mut WinitState) {
|
|||
let event_sink = &mut winit_state.event_sink;
|
||||
window_handle.set_ime_allowed(allow, event_sink);
|
||||
}
|
||||
WindowRequest::GrabCursor(grab) => {
|
||||
window_handle.set_cursor_grab(grab);
|
||||
WindowRequest::SetCursorGrabMode(mode) => {
|
||||
window_handle.set_cursor_grab(mode);
|
||||
}
|
||||
WindowRequest::SetLockedCursorPosition(position) => {
|
||||
window_handle.set_locked_cursor_position(position);
|
||||
}
|
||||
WindowRequest::DragWindow => {
|
||||
window_handle.drag_window();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue