grab_cursor and hide_cursor (#571)

* Windows: Use new cursor state API

* X11: Use new cursor state API

* macOS: Use new cursor state API

* Android+iOS: Stubbed new cursor state API

* Emscripten: Use new cursor state API

* Prevent multiple inc/dec of display count on Windows

* Fixed missing imports (no idea where those went)

* Remove NoneCursor

* Improved documentation

* Fix Emscripten build

* Windows: Re-grab before and after fullscreen
This commit is contained in:
Francesca Frangipane 2018-06-18 12:32:18 -04:00 committed by GitHub
parent 042f5fe4b3
commit fb7528c239
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 326 additions and 313 deletions

View file

@ -11,17 +11,13 @@ use sctk::reexports::client::ConnectError;
use {
CreationError,
CursorState,
EventsLoopClosed,
Icon,
LogicalPosition,
LogicalSize,
MouseCursor,
PhysicalPosition,
PhysicalSize,
ControlFlow,
WindowAttributes,
};
use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize};
use window::MonitorId as RootMonitorId;
use self::x11::{XConnection, XError};
use self::x11::ffi::XVisualInfo;
@ -252,10 +248,18 @@ impl Window {
}
#[inline]
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
match self {
&Window::X(ref w) => w.set_cursor_state(state),
&Window::Wayland(ref w) => w.set_cursor_state(state)
&Window::X(ref window) => window.grab_cursor(grab),
&Window::Wayland(ref _window) => Err("Cursor grabbing is not yet possible on Wayland.".to_owned()),
}
}
#[inline]
pub fn hide_cursor(&self, hide: bool) {
match self {
&Window::X(ref window) => window.hide_cursor(hide),
&Window::Wayland(ref _window) => unimplemented!(),
}
}

View file

@ -1,7 +1,8 @@
use std::collections::VecDeque;
use std::sync::{Arc, Mutex, Weak};
use {CreationError, CursorState, MouseCursor, WindowAttributes, LogicalPosition, LogicalSize};
use {CreationError, MouseCursor, WindowAttributes};
use dpi::{LogicalPosition, LogicalSize};
use platform::MonitorId as PlatformMonitorId;
use window::MonitorId as RootMonitorId;
@ -239,17 +240,6 @@ impl Window {
// TODO
}
#[inline]
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
use CursorState::{Grab, Hide, Normal};
// TODO : not yet possible on wayland to grab cursor
match state {
Grab => Err("Cursor cannot be grabbed on wayland yet.".to_string()),
Hide => Err("Cursor cannot be hidden on wayland yet.".to_string()),
Normal => Ok(()),
}
}
#[inline]
pub fn hidpi_factor(&self) -> i32 {
self.monitors.lock().unwrap().compute_hidpi_factor()

View file

@ -7,8 +7,9 @@ use std::sync::Arc;
use libc;
use parking_lot::Mutex;
use {CursorState, Icon, LogicalPosition, LogicalSize, MouseCursor, WindowAttributes};
use {Icon, MouseCursor, WindowAttributes};
use CreationError::{self, OsError};
use dpi::{LogicalPosition, LogicalSize};
use platform::MonitorId as PlatformMonitorId;
use platform::PlatformSpecificWindowBuilderAttributes;
use platform::x11::MonitorId as X11MonitorId;
@ -61,7 +62,8 @@ pub struct UnownedWindow {
root: ffi::Window, // never changes
screen_id: i32, // never changes
cursor: Mutex<MouseCursor>,
cursor_state: Mutex<CursorState>,
cursor_grabbed: Mutex<bool>,
cursor_hidden: Mutex<bool>,
ime_sender: Mutex<ImeSender>,
pub multitouch: bool, // never changes
pub shared_state: Mutex<SharedState>,
@ -160,7 +162,8 @@ impl UnownedWindow {
root,
screen_id,
cursor: Default::default(),
cursor_state: Default::default(),
cursor_grabbed: Default::default(),
cursor_hidden: Default::default(),
ime_sender: Mutex::new(event_loop.ime_sender.clone()),
multitouch: window_attrs.multitouch,
shared_state: SharedState::new(),
@ -976,9 +979,6 @@ impl UnownedWindow {
MouseCursor::ZoomIn => load(b"zoom-in\0"),
MouseCursor::ZoomOut => load(b"zoom-out\0"),
MouseCursor::NoneCursor => self.create_empty_cursor()
.expect("Failed to create empty cursor"),
}
}
@ -995,7 +995,7 @@ impl UnownedWindow {
#[inline]
pub fn set_cursor(&self, cursor: MouseCursor) {
*self.cursor.lock() = cursor;
if *self.cursor_state.lock() != CursorState::Hide {
if !*self.cursor_hidden.lock() {
self.update_cursor(self.get_cursor(cursor));
}
}
@ -1039,67 +1039,73 @@ impl UnownedWindow {
}
#[inline]
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
use CursorState::*;
let mut cursor_state_lock = self.cursor_state.lock();
match (state, *cursor_state_lock) {
(Normal, Normal) | (Hide, Hide) | (Grab, Grab) => return Ok(()),
_ => {},
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
let mut grabbed_lock = self.cursor_grabbed.lock();
if grab == *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 *cursor_state_lock {
Grab => {
unsafe {
(self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime);
self.xconn.flush_requests().expect("Failed to call XUngrabPointer");
}
},
Normal => {},
Hide => self.update_cursor(self.get_cursor(*self.cursor.lock())),
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| err.to_owned())
} else {
self.xconn.flush_requests()
.map_err(|err| format!("Failed to call `XUngrabPointer`: {:?}", err))
};
if result.is_ok() {
*grabbed_lock = grab;
}
result
}
match state {
Normal => {
*cursor_state_lock = state;
Ok(())
},
Hide => {
*cursor_state_lock = state;
self.update_cursor(
self.create_empty_cursor().expect("Failed to create empty cursor")
);
Ok(())
},
Grab => {
unsafe {
// Ungrab before grabbing to prevent passive grabs
// from causing AlreadyGrabbed
(self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime);
match (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
) {
ffi::GrabSuccess => {
*cursor_state_lock = state;
Ok(())
},
ffi::AlreadyGrabbed | ffi::GrabInvalidTime |
ffi::GrabNotViewable | ffi::GrabFrozen
=> Err("cursor could not be grabbed".to_string()),
_ => unreachable!(),
}
}
},
}
#[inline]
pub fn hide_cursor(&self, hide: bool) {
let mut hidden_lock = self.cursor_hidden.lock();
if hide == *hidden_lock {return; }
let cursor = if hide {
self.create_empty_cursor().expect("Failed to create empty cursor")
} else {
self.get_cursor(*self.cursor.lock())
};
*hidden_lock = hide;
drop(hidden_lock);
self.update_cursor(cursor);
}
#[inline]