mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-23 22:01:31 +11:00
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:
parent
042f5fe4b3
commit
fb7528c239
|
@ -16,6 +16,7 @@
|
|||
- `AvailableMonitorsIter` now implements `Debug`.
|
||||
- Fixed quirk on macOS where certain keys would generate characters at twice the normal rate when held down.
|
||||
- On X11, all event loops now share the same `XConnection`.
|
||||
- **Breaking:** `Window::set_cursor_state` and `CursorState` enum removed in favor of the more composable `Window::grab_cursor` and `Window::hide_cursor`. As a result, grabbing the cursor no longer automatically hides it; you must call both methods to retain the old behavior on Windows and macOS. `Cursor::NoneCursor` has been removed, as it's no longer useful.
|
||||
|
||||
# Version 0.15.1 (2018-06-13)
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ fn main() {
|
|||
let window = winit::WindowBuilder::new().build(&events_loop).unwrap();
|
||||
window.set_title("A fantastic window!");
|
||||
|
||||
let cursors = [MouseCursor::Default, MouseCursor::Crosshair, MouseCursor::Hand, MouseCursor::Arrow, MouseCursor::Move, MouseCursor::Text, MouseCursor::Wait, MouseCursor::Help, MouseCursor::Progress, MouseCursor::NotAllowed, MouseCursor::ContextMenu, MouseCursor::NoneCursor, MouseCursor::Cell, MouseCursor::VerticalText, MouseCursor::Alias, MouseCursor::Copy, MouseCursor::NoDrop, MouseCursor::Grab, MouseCursor::Grabbing, MouseCursor::AllScroll, MouseCursor::ZoomIn, MouseCursor::ZoomOut, MouseCursor::EResize, MouseCursor::NResize, MouseCursor::NeResize, MouseCursor::NwResize, MouseCursor::SResize, MouseCursor::SeResize, MouseCursor::SwResize, MouseCursor::WResize, MouseCursor::EwResize, MouseCursor::NsResize, MouseCursor::NeswResize, MouseCursor::NwseResize, MouseCursor::ColResize, MouseCursor::RowResize];
|
||||
let cursors = [MouseCursor::Default, MouseCursor::Crosshair, MouseCursor::Hand, MouseCursor::Arrow, MouseCursor::Move, MouseCursor::Text, MouseCursor::Wait, MouseCursor::Help, MouseCursor::Progress, MouseCursor::NotAllowed, MouseCursor::ContextMenu, MouseCursor::Cell, MouseCursor::VerticalText, MouseCursor::Alias, MouseCursor::Copy, MouseCursor::NoDrop, MouseCursor::Grab, MouseCursor::Grabbing, MouseCursor::AllScroll, MouseCursor::ZoomIn, MouseCursor::ZoomOut, MouseCursor::EResize, MouseCursor::NResize, MouseCursor::NeResize, MouseCursor::NwResize, MouseCursor::SResize, MouseCursor::SeResize, MouseCursor::SwResize, MouseCursor::WResize, MouseCursor::EwResize, MouseCursor::NsResize, MouseCursor::NeswResize, MouseCursor::NwseResize, MouseCursor::ColResize, MouseCursor::RowResize];
|
||||
let mut cursor_idx = 0;
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
|
|
38
examples/cursor_grab.rs
Normal file
38
examples/cursor_grab.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
extern crate winit;
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
let window = winit::WindowBuilder::new()
|
||||
.with_title("Super Cursor Grab'n'Hide Simulator 9000")
|
||||
.build(&events_loop)
|
||||
.unwrap();
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
if let winit::Event::WindowEvent { event, .. } = event {
|
||||
use winit::WindowEvent::*;
|
||||
match event {
|
||||
CloseRequested => return winit::ControlFlow::Break,
|
||||
KeyboardInput {
|
||||
input: winit::KeyboardInput {
|
||||
state: winit::ElementState::Released,
|
||||
virtual_keycode: Some(key),
|
||||
modifiers,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
use winit::VirtualKeyCode::*;
|
||||
match key {
|
||||
Escape => return winit::ControlFlow::Break,
|
||||
G => window.grab_cursor(!modifiers.shift).unwrap(),
|
||||
H => window.hide_cursor(!modifiers.shift),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
winit::ControlFlow::Continue
|
||||
});
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
extern crate winit;
|
||||
|
||||
use winit::{ControlFlow, WindowEvent, ElementState, KeyboardInput};
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
let window = winit::WindowBuilder::new().build(&events_loop).unwrap();
|
||||
window.set_title("winit - Cursor grabbing test");
|
||||
|
||||
let mut grabbed = false;
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
println!("{:?}", event);
|
||||
|
||||
match event {
|
||||
winit::Event::WindowEvent { event, .. } => {
|
||||
match event {
|
||||
WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. } => {
|
||||
if grabbed {
|
||||
grabbed = false;
|
||||
window.set_cursor_state(winit::CursorState::Normal)
|
||||
.ok().expect("could not ungrab mouse cursor");
|
||||
} else {
|
||||
grabbed = true;
|
||||
window.set_cursor_state(winit::CursorState::Grab)
|
||||
.ok().expect("could not grab mouse cursor");
|
||||
}
|
||||
},
|
||||
|
||||
WindowEvent::CloseRequested => return ControlFlow::Break,
|
||||
|
||||
a @ WindowEvent::CursorMoved { .. } => {
|
||||
println!("{:?}", a);
|
||||
},
|
||||
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
ControlFlow::Continue
|
||||
});
|
||||
}
|
24
src/lib.rs
24
src/lib.rs
|
@ -355,7 +355,6 @@ pub enum MouseCursor {
|
|||
/// Cursor showing that something cannot be done.
|
||||
NotAllowed,
|
||||
ContextMenu,
|
||||
NoneCursor,
|
||||
Cell,
|
||||
VerticalText,
|
||||
Alias,
|
||||
|
@ -391,29 +390,6 @@ impl Default for MouseCursor {
|
|||
}
|
||||
}
|
||||
|
||||
/// Describes how winit handles the cursor.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum CursorState {
|
||||
/// Normal cursor behavior.
|
||||
Normal,
|
||||
|
||||
/// The cursor will be invisible when over the window.
|
||||
Hide,
|
||||
|
||||
/// Grabs the mouse cursor. The cursor's motion will be confined to this
|
||||
/// window and the window has exclusive access to further events regarding
|
||||
/// the cursor.
|
||||
///
|
||||
/// This is useful for first-person cameras for example.
|
||||
Grab,
|
||||
}
|
||||
|
||||
impl Default for CursorState {
|
||||
fn default() -> Self {
|
||||
CursorState::Normal
|
||||
}
|
||||
}
|
||||
|
||||
/// Attributes to use when creating a window.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WindowAttributes {
|
||||
|
|
|
@ -12,7 +12,6 @@ use std::sync::mpsc::{Receiver, channel};
|
|||
|
||||
use {
|
||||
CreationError,
|
||||
CursorState,
|
||||
Event,
|
||||
LogicalPosition,
|
||||
LogicalSize,
|
||||
|
@ -337,9 +336,13 @@ impl Window {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, _state: CursorState) -> Result<(), String> {
|
||||
pub fn grab_cursor(&self, _grab: bool) -> Result<(), String> {
|
||||
Err("Cursor grabbing is not possible on Android.".to_owned())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hide_cursor(&self, _hide: bool) {
|
||||
// N/A
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -150,7 +150,8 @@ impl EventsLoop {
|
|||
pub struct WindowId(usize);
|
||||
|
||||
pub struct Window2 {
|
||||
cursor_state: Mutex<::CursorState>,
|
||||
cursor_grabbed: Mutex<bool>,
|
||||
cursor_hidden: Mutex<bool>,
|
||||
is_fullscreen: bool,
|
||||
events: Box<Mutex<VecDeque<::Event>>>,
|
||||
}
|
||||
|
@ -374,7 +375,8 @@ impl Window {
|
|||
}
|
||||
|
||||
let w = Window2 {
|
||||
cursor_state: Default::default(),
|
||||
cursor_grabbed: Default::default(),
|
||||
cursor_hidden: Default::default(),
|
||||
events: Default::default(),
|
||||
is_fullscreen: attribs.fullscreen.is_some(),
|
||||
};
|
||||
|
@ -498,48 +500,57 @@ impl Window {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show(&self) {}
|
||||
#[inline]
|
||||
pub fn hide(&self) {}
|
||||
pub fn show(&self) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, _cursor: ::MouseCursor) {}
|
||||
pub fn hide(&self) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, state: ::CursorState) -> Result<(), String> {
|
||||
pub fn set_cursor(&self, _cursor: ::MouseCursor) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
|
||||
let mut grabbed_lock = self.window.cursor_grabbed.lock().unwrap();
|
||||
if grab == *grabbed_lock { return Ok(()); }
|
||||
unsafe {
|
||||
use ::CursorState::*;
|
||||
|
||||
let mut old_state = self.window.cursor_state.lock().unwrap();
|
||||
if state == *old_state {
|
||||
return Ok(());
|
||||
if grab {
|
||||
em_try(ffi::emscripten_set_pointerlockchange_callback(
|
||||
ptr::null(),
|
||||
0 as *mut c_void,
|
||||
ffi::EM_FALSE,
|
||||
Some(pointerlockchange_callback),
|
||||
))?;
|
||||
em_try(ffi::emscripten_request_pointerlock(ptr::null(), ffi::EM_TRUE))?;
|
||||
} else {
|
||||
em_try(ffi::emscripten_set_pointerlockchange_callback(
|
||||
ptr::null(),
|
||||
0 as *mut c_void,
|
||||
ffi::EM_FALSE,
|
||||
None,
|
||||
))?;
|
||||
em_try(ffi::emscripten_exit_pointerlock())?;
|
||||
}
|
||||
|
||||
// Set or unset grab callback
|
||||
match state {
|
||||
Hide | Normal => em_try(ffi::emscripten_set_pointerlockchange_callback(ptr::null(), 0 as *mut c_void, ffi::EM_FALSE, None))?,
|
||||
Grab => em_try(ffi::emscripten_set_pointerlockchange_callback(ptr::null(), 0 as *mut c_void, ffi::EM_FALSE, Some(pointerlockchange_callback)))?,
|
||||
}
|
||||
|
||||
// Go back to normal cursor state
|
||||
match *old_state {
|
||||
Hide => show_mouse(),
|
||||
Grab => em_try(ffi::emscripten_exit_pointerlock())?,
|
||||
Normal => (),
|
||||
}
|
||||
|
||||
// Set cursor from normal cursor state
|
||||
match state {
|
||||
Hide => ffi::emscripten_hide_mouse(),
|
||||
Grab => em_try(ffi::emscripten_request_pointerlock(ptr::null(), ffi::EM_TRUE))?,
|
||||
Normal => (),
|
||||
}
|
||||
|
||||
// Update
|
||||
*old_state = state;
|
||||
|
||||
*grabbed_lock = grab;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hide_cursor(&self, hide: bool) {
|
||||
let mut hidden_lock = self.window.cursor_hidden.lock().unwrap();
|
||||
if hide == *hidden_lock { return; }
|
||||
if hide {
|
||||
unsafe { ffi::emscripten_hide_mouse() };
|
||||
} else {
|
||||
show_mouse();
|
||||
}
|
||||
*hidden_lock = hide;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -610,7 +621,8 @@ impl Drop for Window {
|
|||
|
||||
unsafe {
|
||||
// Return back to normal cursor state
|
||||
let _ = self.set_cursor_state(::CursorState::Normal);
|
||||
self.hide_cursor(false);
|
||||
self.grab_cursor(false);
|
||||
|
||||
// Exit fullscreen if on
|
||||
if self.window.is_fullscreen {
|
||||
|
|
|
@ -69,7 +69,6 @@ use objc::runtime::{BOOL, Class, Object, Sel, YES};
|
|||
|
||||
use {
|
||||
CreationError,
|
||||
CursorState,
|
||||
Event,
|
||||
LogicalPosition,
|
||||
LogicalSize,
|
||||
|
@ -394,9 +393,13 @@ impl Window {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, _cursor_state: CursorState) -> Result<(), String> {
|
||||
pub fn grab_cursor(&self, _grab: bool) -> Result<(), String> {
|
||||
Err("Cursor grabbing is not possible on iOS.".to_owned())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hide_cursor(&self, _hide: bool) {
|
||||
// N/A
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -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!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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(()),
|
||||
_ => {},
|
||||
}
|
||||
|
||||
match *cursor_state_lock {
|
||||
Grab => {
|
||||
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);
|
||||
self.xconn.flush_requests().expect("Failed to call XUngrabPointer");
|
||||
}
|
||||
},
|
||||
Normal => {},
|
||||
Hide => self.update_cursor(self.get_cursor(*self.cursor.lock())),
|
||||
}
|
||||
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 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()),
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
#[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]
|
||||
|
|
|
@ -27,7 +27,6 @@ use objc::declare::ClassDecl;
|
|||
|
||||
use {
|
||||
CreationError,
|
||||
CursorState,
|
||||
Event,
|
||||
LogicalPosition,
|
||||
LogicalSize,
|
||||
|
@ -527,6 +526,7 @@ pub struct Window2 {
|
|||
pub window: IdRef,
|
||||
pub delegate: WindowDelegate,
|
||||
pub input_context: IdRef,
|
||||
cursor_hidden: Cell<bool>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Window2 {}
|
||||
|
@ -674,6 +674,7 @@ impl Window2 {
|
|||
window: window,
|
||||
delegate: WindowDelegate::new(delegate_state),
|
||||
input_context,
|
||||
cursor_hidden: Default::default(),
|
||||
};
|
||||
|
||||
// Set fullscreen mode after we setup everything
|
||||
|
@ -980,7 +981,7 @@ impl Window2 {
|
|||
MouseCursor::SeResize | MouseCursor::SwResize |
|
||||
MouseCursor::NwseResize | MouseCursor::NeswResize |
|
||||
|
||||
MouseCursor::Cell | MouseCursor::NoneCursor |
|
||||
MouseCursor::Cell |
|
||||
MouseCursor::Wait | MouseCursor::Progress | MouseCursor::Help |
|
||||
MouseCursor::Move | MouseCursor::AllScroll | MouseCursor::ZoomIn |
|
||||
MouseCursor::ZoomOut => "arrowCursor",
|
||||
|
@ -994,25 +995,25 @@ impl Window2 {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
let cls = Class::get("NSCursor").unwrap();
|
||||
|
||||
// TODO: Check for errors.
|
||||
match state {
|
||||
CursorState::Normal => {
|
||||
let _: () = unsafe { msg_send![cls, unhide] };
|
||||
let _ = CGDisplay::associate_mouse_and_mouse_cursor_position(true);
|
||||
Ok(())
|
||||
},
|
||||
CursorState::Hide => {
|
||||
let _: () = unsafe { msg_send![cls, hide] };
|
||||
Ok(())
|
||||
},
|
||||
CursorState::Grab => {
|
||||
let _: () = unsafe { msg_send![cls, hide] };
|
||||
let _ = CGDisplay::associate_mouse_and_mouse_cursor_position(false);
|
||||
Ok(())
|
||||
#[inline]
|
||||
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
|
||||
// TODO: Do this for real https://stackoverflow.com/a/40922095/5435443
|
||||
CGDisplay::associate_mouse_and_mouse_cursor_position(!grab)
|
||||
.map_err(|status| format!("Failed to grab cursor: `CGError` {:?}", status))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hide_cursor(&self, hide: bool) {
|
||||
let cursor_class = Class::get("NSCursor").unwrap();
|
||||
// macOS uses a "hide counter" like Windows does, so we avoid incrementing it more than once.
|
||||
// (otherwise, `hide_cursor(false)` would need to be called n times!)
|
||||
if hide != self.cursor_hidden.get() {
|
||||
if hide {
|
||||
let _: () = unsafe { msg_send![cursor_class, hide] };
|
||||
} else {
|
||||
let _: () = unsafe { msg_send![cursor_class, unhide] };
|
||||
}
|
||||
self.cursor_hidden.replace(hide);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,6 @@ use winapi::um::winnt::{LONG, LPCSTR, SHORT};
|
|||
|
||||
use {
|
||||
ControlFlow,
|
||||
CursorState,
|
||||
Event,
|
||||
EventsLoopClosed,
|
||||
KeyboardInput,
|
||||
|
@ -83,8 +82,8 @@ pub struct SavedWindowInfo {
|
|||
pub struct WindowState {
|
||||
/// Cursor to set at the next `WM_SETCURSOR` event received.
|
||||
pub cursor: Cursor,
|
||||
/// Cursor state to set at the next `WM_SETCURSOR` event received.
|
||||
pub cursor_state: CursorState,
|
||||
pub cursor_grabbed: bool,
|
||||
pub cursor_hidden: bool,
|
||||
/// Used by `WM_GETMINMAXINFO`.
|
||||
pub max_size: Option<PhysicalSize>,
|
||||
pub min_size: Option<PhysicalSize>,
|
||||
|
|
|
@ -17,7 +17,6 @@ use winapi::um::winnt::{LONG, LPCWSTR};
|
|||
|
||||
use {
|
||||
CreationError,
|
||||
CursorState,
|
||||
Icon,
|
||||
LogicalPosition,
|
||||
LogicalSize,
|
||||
|
@ -26,9 +25,9 @@ use {
|
|||
PhysicalSize,
|
||||
WindowAttributes,
|
||||
};
|
||||
use platform::platform::{Cursor, EventsLoop, PlatformSpecificWindowBuilderAttributes, WindowId};
|
||||
use platform::platform::{Cursor, PlatformSpecificWindowBuilderAttributes, WindowId};
|
||||
use platform::platform::dpi::{BASE_DPI, dpi_to_scale_factor, get_window_dpi, get_window_scale_factor};
|
||||
use platform::platform::events_loop::{self, DESTROY_MSG_ID, INITIAL_DPI_MSG_ID};
|
||||
use platform::platform::events_loop::{self, DESTROY_MSG_ID, EventsLoop, INITIAL_DPI_MSG_ID};
|
||||
use platform::platform::icon::{self, IconType, WinIcon};
|
||||
use platform::platform::raw_input::register_all_mice_and_keyboards_for_raw_input;
|
||||
use platform::platform::util;
|
||||
|
@ -336,7 +335,6 @@ impl Window {
|
|||
MouseCursor::Wait => winuser::IDC_WAIT,
|
||||
MouseCursor::Progress => winuser::IDC_APPSTARTING,
|
||||
MouseCursor::Help => winuser::IDC_HELP,
|
||||
MouseCursor::NoneCursor => ptr::null(),
|
||||
_ => winuser::IDC_ARROW, // use arrow for the missing cases.
|
||||
};
|
||||
|
||||
|
@ -363,36 +361,13 @@ impl Window {
|
|||
Ok(util::rect_eq(&client_rect, &clip_rect))
|
||||
}
|
||||
|
||||
fn change_cursor_state(
|
||||
window: &WindowWrapper,
|
||||
current_state: CursorState,
|
||||
state: CursorState,
|
||||
) -> Result<CursorState, String> {
|
||||
match (current_state, state) {
|
||||
(CursorState::Normal, CursorState::Normal)
|
||||
| (CursorState::Hide, CursorState::Hide)
|
||||
| (CursorState::Grab, CursorState::Grab) => (), // no-op
|
||||
|
||||
(CursorState::Normal, CursorState::Hide) => unsafe {
|
||||
winuser::ShowCursor(FALSE);
|
||||
},
|
||||
|
||||
(CursorState::Grab, CursorState::Hide) => unsafe {
|
||||
if winuser::ClipCursor(ptr::null()) == 0 {
|
||||
return Err("`ClipCursor` failed".to_owned());
|
||||
}
|
||||
},
|
||||
|
||||
(CursorState::Hide, CursorState::Normal) => unsafe {
|
||||
winuser::ShowCursor(TRUE);
|
||||
},
|
||||
|
||||
(CursorState::Normal, CursorState::Grab)
|
||||
| (CursorState::Hide, CursorState::Grab) => unsafe {
|
||||
pub(crate) unsafe fn grab_cursor_inner(window: &WindowWrapper, grab: bool) -> Result<(), String> {
|
||||
if grab {
|
||||
let mut rect = mem::uninitialized();
|
||||
if winuser::GetClientRect(window.0, &mut rect) == 0 {
|
||||
return Err("`GetClientRect` failed".to_owned());
|
||||
}
|
||||
// A `POINT` is two `LONG`s (x, y), and the `RECT` field after `left` is `top`.
|
||||
if winuser::ClientToScreen(window.0, &mut rect.left as *mut _ as LPPOINT) == 0 {
|
||||
return Err("`ClientToScreen` (left, top) failed".to_owned());
|
||||
}
|
||||
|
@ -402,44 +377,62 @@ impl Window {
|
|||
if winuser::ClipCursor(&rect) == 0 {
|
||||
return Err("`ClipCursor` failed".to_owned());
|
||||
}
|
||||
if current_state != CursorState::Hide {
|
||||
winuser::ShowCursor(FALSE);
|
||||
}
|
||||
},
|
||||
|
||||
(CursorState::Grab, CursorState::Normal) => unsafe {
|
||||
} else {
|
||||
if winuser::ClipCursor(ptr::null()) == 0 {
|
||||
return Err("`ClipCursor` failed".to_owned());
|
||||
}
|
||||
winuser::ShowCursor(TRUE);
|
||||
},
|
||||
};
|
||||
Ok(state)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
let is_grabbed = unsafe { self.cursor_is_grabbed() }?;
|
||||
let (tx, rx) = channel();
|
||||
let window = self.window.clone();
|
||||
#[inline]
|
||||
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
|
||||
let currently_grabbed = unsafe { self.cursor_is_grabbed() }?;
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
{
|
||||
let window_state_lock = window_state.lock().unwrap();
|
||||
if currently_grabbed == grab
|
||||
&& grab == window_state_lock.cursor_grabbed {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
let window = self.window.clone();
|
||||
let (tx, rx) = channel();
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
let mut window_state_lock = window_state.lock().unwrap();
|
||||
// We should probably also check if the cursor is hidden,
|
||||
// but `GetCursorInfo` isn't in winapi-rs yet, and it doesn't seem to matter as much.
|
||||
let current_state = match window_state_lock.cursor_state {
|
||||
CursorState::Normal if is_grabbed => CursorState::Grab,
|
||||
CursorState::Grab if !is_grabbed => CursorState::Normal,
|
||||
current_state => current_state,
|
||||
};
|
||||
let result = Self::change_cursor_state(&window, current_state, state)
|
||||
.map(|_| {
|
||||
window_state_lock.cursor_state = state;
|
||||
});
|
||||
let result = unsafe { Self::grab_cursor_inner(&window, grab) };
|
||||
if result.is_ok() {
|
||||
window_state.lock().unwrap().cursor_grabbed = grab;
|
||||
}
|
||||
let _ = tx.send(result);
|
||||
});
|
||||
rx.recv().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn hide_cursor_inner(hide: bool) {
|
||||
if hide {
|
||||
winuser::ShowCursor(FALSE);
|
||||
} else {
|
||||
winuser::ShowCursor(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hide_cursor(&self, hide: bool) {
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
{
|
||||
let window_state_lock = window_state.lock().unwrap();
|
||||
// We don't want to increment/decrement the display count more than once!
|
||||
if hide == window_state_lock.cursor_hidden { return; }
|
||||
}
|
||||
let (tx, rx) = channel();
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
unsafe { Self::hide_cursor_inner(hide) };
|
||||
window_state.lock().unwrap().cursor_hidden = hide;
|
||||
let _ = tx.send(());
|
||||
});
|
||||
rx.recv().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f64 {
|
||||
get_window_scale_factor(self.window.0, self.window.1)
|
||||
|
@ -518,26 +511,28 @@ impl Window {
|
|||
}
|
||||
|
||||
unsafe fn restore_saved_window(&self) {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
let (rect, mut style, ex_style) = {
|
||||
let mut window_state_lock = self.window_state.lock().unwrap();
|
||||
|
||||
// 'saved_window_info' can be None if the window has never been
|
||||
// in fullscreen mode before this method gets called.
|
||||
if window_state.saved_window_info.is_none() {
|
||||
if window_state_lock.saved_window_info.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let saved_window_info = window_state_lock.saved_window_info.as_mut().unwrap();
|
||||
|
||||
// Reset original window style and size. The multiple window size/moves
|
||||
// here are ugly, but if SetWindowPos() doesn't redraw, the taskbar won't be
|
||||
// repainted. Better-looking methods welcome.
|
||||
{
|
||||
let saved_window_info = window_state.saved_window_info.as_mut().unwrap();
|
||||
saved_window_info.is_fullscreen = false;
|
||||
}
|
||||
let saved_window_info = window_state.saved_window_info.as_ref().unwrap();
|
||||
|
||||
let rect = saved_window_info.rect.clone();
|
||||
let (style, ex_style) = (saved_window_info.style, saved_window_info.ex_style);
|
||||
(rect, style, ex_style)
|
||||
};
|
||||
let window = self.window.clone();
|
||||
let (mut style, ex_style) = (saved_window_info.style, saved_window_info.ex_style);
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
|
||||
let maximized = self.maximized.get();
|
||||
let resizable = self.resizable.get();
|
||||
|
@ -545,6 +540,8 @@ impl Window {
|
|||
// We're restoring the window to its size and position from before being fullscreened.
|
||||
// `ShowWindow` resizes the window, so it must be called from the main thread.
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
let _ = Self::grab_cursor_inner(&window, false);
|
||||
|
||||
if resizable {
|
||||
style |= winuser::WS_SIZEBOX as LONG;
|
||||
} else {
|
||||
|
@ -577,6 +574,9 @@ impl Window {
|
|||
);
|
||||
|
||||
mark_fullscreen(window.0, false);
|
||||
|
||||
let window_state_lock = window_state.lock().unwrap();
|
||||
let _ = Self::grab_cursor_inner(&window, window_state_lock.cursor_grabbed);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -588,10 +588,13 @@ impl Window {
|
|||
let (x, y): (i32, i32) = inner.get_position().into();
|
||||
let (width, height): (u32, u32) = inner.get_dimensions().into();
|
||||
let window = self.window.clone();
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
|
||||
let (style, ex_style) = self.set_fullscreen_style();
|
||||
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
let _ = Self::grab_cursor_inner(&window, false);
|
||||
|
||||
winuser::SetWindowLongW(
|
||||
window.0,
|
||||
winuser::GWL_STYLE,
|
||||
|
@ -622,6 +625,9 @@ impl Window {
|
|||
);
|
||||
|
||||
mark_fullscreen(window.0, true);
|
||||
|
||||
let window_state_lock = window_state.lock().unwrap();
|
||||
let _ = Self::grab_cursor_inner(&window, window_state_lock.cursor_grabbed);
|
||||
});
|
||||
}
|
||||
&None => {
|
||||
|
@ -980,7 +986,8 @@ unsafe fn init(
|
|||
.map(|logical_size| PhysicalSize::from_logical(logical_size, dpi_factor));
|
||||
let mut window_state = events_loop::WindowState {
|
||||
cursor: Cursor(winuser::IDC_ARROW), // use arrow by default
|
||||
cursor_state: CursorState::Normal,
|
||||
cursor_grabbed: false,
|
||||
cursor_hidden: false,
|
||||
max_size,
|
||||
min_size,
|
||||
mouse_in_window: false,
|
||||
|
|
|
@ -2,7 +2,6 @@ use std::collections::vec_deque::IntoIter as VecDequeIter;
|
|||
|
||||
use {
|
||||
CreationError,
|
||||
CursorState,
|
||||
EventsLoop,
|
||||
Icon,
|
||||
LogicalPosition,
|
||||
|
@ -327,12 +326,31 @@ impl Window {
|
|||
self.window.set_cursor_position(position)
|
||||
}
|
||||
|
||||
/// Sets how winit handles the cursor. See the documentation of `CursorState` for details.
|
||||
/// Grabs the cursor, preventing it from leaving the window.
|
||||
///
|
||||
/// Has no effect on Android.
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// On macOS, this presently merely locks the cursor in a fixed location, which looks visually awkward.
|
||||
///
|
||||
/// This has no effect on Android or iOS.
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
self.window.set_cursor_state(state)
|
||||
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
|
||||
self.window.grab_cursor(grab)
|
||||
}
|
||||
|
||||
/// Hides the cursor, making it invisible but still usable.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// On Windows and X11, the cursor is only hidden within the confines of the window.
|
||||
///
|
||||
/// On macOS, the cursor is hidden as long as the window has input focus, even if the cursor is outside of the
|
||||
/// window.
|
||||
///
|
||||
/// This has no effect on Android or iOS.
|
||||
#[inline]
|
||||
pub fn hide_cursor(&self, hide: bool) {
|
||||
self.window.hide_cursor(hide)
|
||||
}
|
||||
|
||||
/// Sets the window to maximized or back
|
||||
|
|
Loading…
Reference in a new issue