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:
Kirill Chibisov 2022-06-13 09:43:14 +03:00 committed by GitHub
parent 8ef9fe44c7
commit 9e6f666616
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 359 additions and 131 deletions

View file

@ -22,7 +22,7 @@ use crate::{
MonitorHandle as PlatformMonitorHandle, OsError, PlatformSpecificWindowBuilderAttributes,
VideoMode as PlatformVideoMode,
},
window::{CursorIcon, Fullscreen, Icon, UserAttentionType, WindowAttributes},
window::{CursorGrabMode, CursorIcon, Fullscreen, Icon, UserAttentionType, WindowAttributes},
};
use super::{
@ -106,7 +106,7 @@ pub struct UnownedWindow {
root: ffi::Window, // never changes
screen_id: i32, // never changes
cursor: Mutex<CursorIcon>,
cursor_grabbed: Mutex<bool>,
cursor_grabbed_mode: Mutex<CursorGrabMode>,
cursor_visible: Mutex<bool>,
ime_sender: Mutex<ImeSender>,
pub shared_state: Mutex<SharedState>,
@ -276,7 +276,7 @@ impl UnownedWindow {
root,
screen_id,
cursor: Default::default(),
cursor_grabbed: Mutex::new(false),
cursor_grabbed_mode: Mutex::new(CursorGrabMode::None),
cursor_visible: Mutex::new(true),
ime_sender: Mutex::new(event_loop.ime_sender.clone()),
shared_state: SharedState::new(guessed_monitor, &window_attrs),
@ -1272,64 +1272,75 @@ impl UnownedWindow {
}
#[inline]
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
let mut grabbed_lock = self.cursor_grabbed.lock();
if grab == *grabbed_lock {
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
let mut grabbed_lock = self.cursor_grabbed_mode.lock();
if mode == *grabbed_lock {
return Ok(());
}
unsafe {
// We ungrab before grabbing to prevent passive grabs from causing `AlreadyGrabbed`.
// Therefore, this is common to both codepaths.
(self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime);
}
let result = if grab {
let result = unsafe {
(self.xconn.xlib.XGrabPointer)(
self.xconn.display,
self.xwindow,
ffi::True,
(ffi::ButtonPressMask
| ffi::ButtonReleaseMask
| ffi::EnterWindowMask
| ffi::LeaveWindowMask
| ffi::PointerMotionMask
| ffi::PointerMotionHintMask
| ffi::Button1MotionMask
| ffi::Button2MotionMask
| ffi::Button3MotionMask
| ffi::Button4MotionMask
| ffi::Button5MotionMask
| ffi::ButtonMotionMask
| ffi::KeymapStateMask) as c_uint,
ffi::GrabModeAsync,
ffi::GrabModeAsync,
self.xwindow,
0,
ffi::CurrentTime,
)
};
match result {
ffi::GrabSuccess => Ok(()),
ffi::AlreadyGrabbed => {
Err("Cursor could not be grabbed: already grabbed by another client")
}
ffi::GrabInvalidTime => Err("Cursor could not be grabbed: invalid time"),
ffi::GrabNotViewable => {
Err("Cursor could not be grabbed: grab location not viewable")
}
ffi::GrabFrozen => Err("Cursor could not be grabbed: frozen by another client"),
_ => unreachable!(),
}
.map_err(|err| ExternalError::Os(os_error!(OsError::XMisc(err))))
} else {
self.xconn
let result = match mode {
CursorGrabMode::None => self
.xconn
.flush_requests()
.map_err(|err| ExternalError::Os(os_error!(OsError::XError(err))))
.map_err(|err| ExternalError::Os(os_error!(OsError::XError(err)))),
CursorGrabMode::Confined => {
let result = unsafe {
(self.xconn.xlib.XGrabPointer)(
self.xconn.display,
self.xwindow,
ffi::True,
(ffi::ButtonPressMask
| ffi::ButtonReleaseMask
| ffi::EnterWindowMask
| ffi::LeaveWindowMask
| ffi::PointerMotionMask
| ffi::PointerMotionHintMask
| ffi::Button1MotionMask
| ffi::Button2MotionMask
| ffi::Button3MotionMask
| ffi::Button4MotionMask
| ffi::Button5MotionMask
| ffi::ButtonMotionMask
| ffi::KeymapStateMask) as c_uint,
ffi::GrabModeAsync,
ffi::GrabModeAsync,
self.xwindow,
0,
ffi::CurrentTime,
)
};
match result {
ffi::GrabSuccess => Ok(()),
ffi::AlreadyGrabbed => {
Err("Cursor could not be confined: already confined by another client")
}
ffi::GrabInvalidTime => Err("Cursor could not be confined: invalid time"),
ffi::GrabNotViewable => {
Err("Cursor could not be confined: confine location not viewable")
}
ffi::GrabFrozen => {
Err("Cursor could not be confined: frozen by another client")
}
_ => unreachable!(),
}
.map_err(|err| ExternalError::Os(os_error!(OsError::XMisc(err))))
}
CursorGrabMode::Locked => {
return Err(ExternalError::NotSupported(NotSupportedError::new()));
}
};
if result.is_ok() {
*grabbed_lock = grab;
*grabbed_lock = mode;
}
result
}
@ -1386,14 +1397,14 @@ impl UnownedWindow {
// we can't use `set_cursor_grab(false)` here because it doesn't run `XUngrabPointer`
// if the cursor isn't currently grabbed
let mut grabbed_lock = self.cursor_grabbed.lock();
let mut grabbed_lock = self.cursor_grabbed_mode.lock();
unsafe {
(self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime);
}
self.xconn
.flush_requests()
.map_err(|err| ExternalError::Os(os_error!(OsError::XError(err))))?;
*grabbed_lock = false;
*grabbed_lock = CursorGrabMode::None;
// we keep the lock until we are done
self.xconn