mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2025-01-11 13:31:29 +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`.
|
- `AvailableMonitorsIter` now implements `Debug`.
|
||||||
- Fixed quirk on macOS where certain keys would generate characters at twice the normal rate when held down.
|
- 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`.
|
- 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)
|
# Version 0.15.1 (2018-06-13)
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ fn main() {
|
||||||
let window = winit::WindowBuilder::new().build(&events_loop).unwrap();
|
let window = winit::WindowBuilder::new().build(&events_loop).unwrap();
|
||||||
window.set_title("A fantastic window!");
|
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;
|
let mut cursor_idx = 0;
|
||||||
|
|
||||||
events_loop.run_forever(|event| {
|
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.
|
/// Cursor showing that something cannot be done.
|
||||||
NotAllowed,
|
NotAllowed,
|
||||||
ContextMenu,
|
ContextMenu,
|
||||||
NoneCursor,
|
|
||||||
Cell,
|
Cell,
|
||||||
VerticalText,
|
VerticalText,
|
||||||
Alias,
|
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.
|
/// Attributes to use when creating a window.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct WindowAttributes {
|
pub struct WindowAttributes {
|
||||||
|
|
|
@ -12,7 +12,6 @@ use std::sync::mpsc::{Receiver, channel};
|
||||||
|
|
||||||
use {
|
use {
|
||||||
CreationError,
|
CreationError,
|
||||||
CursorState,
|
|
||||||
Event,
|
Event,
|
||||||
LogicalPosition,
|
LogicalPosition,
|
||||||
LogicalSize,
|
LogicalSize,
|
||||||
|
@ -337,9 +336,13 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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
|
// N/A
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -150,7 +150,8 @@ impl EventsLoop {
|
||||||
pub struct WindowId(usize);
|
pub struct WindowId(usize);
|
||||||
|
|
||||||
pub struct Window2 {
|
pub struct Window2 {
|
||||||
cursor_state: Mutex<::CursorState>,
|
cursor_grabbed: Mutex<bool>,
|
||||||
|
cursor_hidden: Mutex<bool>,
|
||||||
is_fullscreen: bool,
|
is_fullscreen: bool,
|
||||||
events: Box<Mutex<VecDeque<::Event>>>,
|
events: Box<Mutex<VecDeque<::Event>>>,
|
||||||
}
|
}
|
||||||
|
@ -374,7 +375,8 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
let w = Window2 {
|
let w = Window2 {
|
||||||
cursor_state: Default::default(),
|
cursor_grabbed: Default::default(),
|
||||||
|
cursor_hidden: Default::default(),
|
||||||
events: Default::default(),
|
events: Default::default(),
|
||||||
is_fullscreen: attribs.fullscreen.is_some(),
|
is_fullscreen: attribs.fullscreen.is_some(),
|
||||||
};
|
};
|
||||||
|
@ -498,48 +500,57 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn show(&self) {}
|
pub fn show(&self) {
|
||||||
#[inline]
|
// N/A
|
||||||
pub fn hide(&self) {}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor(&self, _cursor: ::MouseCursor) {}
|
pub fn hide(&self) {
|
||||||
|
// N/A
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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 {
|
unsafe {
|
||||||
use ::CursorState::*;
|
if grab {
|
||||||
|
em_try(ffi::emscripten_set_pointerlockchange_callback(
|
||||||
let mut old_state = self.window.cursor_state.lock().unwrap();
|
ptr::null(),
|
||||||
if state == *old_state {
|
0 as *mut c_void,
|
||||||
return Ok(());
|
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)))?,
|
|
||||||
}
|
}
|
||||||
|
*grabbed_lock = grab;
|
||||||
// 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;
|
|
||||||
|
|
||||||
Ok(())
|
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]
|
#[inline]
|
||||||
|
@ -610,7 +621,8 @@ impl Drop for Window {
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// Return back to normal cursor state
|
// 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
|
// Exit fullscreen if on
|
||||||
if self.window.is_fullscreen {
|
if self.window.is_fullscreen {
|
||||||
|
|
|
@ -69,7 +69,6 @@ use objc::runtime::{BOOL, Class, Object, Sel, YES};
|
||||||
|
|
||||||
use {
|
use {
|
||||||
CreationError,
|
CreationError,
|
||||||
CursorState,
|
|
||||||
Event,
|
Event,
|
||||||
LogicalPosition,
|
LogicalPosition,
|
||||||
LogicalSize,
|
LogicalSize,
|
||||||
|
@ -394,9 +393,13 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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
|
// N/A
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -11,17 +11,13 @@ use sctk::reexports::client::ConnectError;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
CreationError,
|
CreationError,
|
||||||
CursorState,
|
|
||||||
EventsLoopClosed,
|
EventsLoopClosed,
|
||||||
Icon,
|
Icon,
|
||||||
LogicalPosition,
|
|
||||||
LogicalSize,
|
|
||||||
MouseCursor,
|
MouseCursor,
|
||||||
PhysicalPosition,
|
|
||||||
PhysicalSize,
|
|
||||||
ControlFlow,
|
ControlFlow,
|
||||||
WindowAttributes,
|
WindowAttributes,
|
||||||
};
|
};
|
||||||
|
use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize};
|
||||||
use window::MonitorId as RootMonitorId;
|
use window::MonitorId as RootMonitorId;
|
||||||
use self::x11::{XConnection, XError};
|
use self::x11::{XConnection, XError};
|
||||||
use self::x11::ffi::XVisualInfo;
|
use self::x11::ffi::XVisualInfo;
|
||||||
|
@ -252,10 +248,18 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
|
||||||
match self {
|
match self {
|
||||||
&Window::X(ref w) => w.set_cursor_state(state),
|
&Window::X(ref window) => window.grab_cursor(grab),
|
||||||
&Window::Wayland(ref w) => w.set_cursor_state(state)
|
&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::collections::VecDeque;
|
||||||
use std::sync::{Arc, Mutex, Weak};
|
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 platform::MonitorId as PlatformMonitorId;
|
||||||
use window::MonitorId as RootMonitorId;
|
use window::MonitorId as RootMonitorId;
|
||||||
|
|
||||||
|
@ -239,17 +240,6 @@ impl Window {
|
||||||
// TODO
|
// 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]
|
#[inline]
|
||||||
pub fn hidpi_factor(&self) -> i32 {
|
pub fn hidpi_factor(&self) -> i32 {
|
||||||
self.monitors.lock().unwrap().compute_hidpi_factor()
|
self.monitors.lock().unwrap().compute_hidpi_factor()
|
||||||
|
|
|
@ -7,8 +7,9 @@ use std::sync::Arc;
|
||||||
use libc;
|
use libc;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
use {CursorState, Icon, LogicalPosition, LogicalSize, MouseCursor, WindowAttributes};
|
use {Icon, MouseCursor, WindowAttributes};
|
||||||
use CreationError::{self, OsError};
|
use CreationError::{self, OsError};
|
||||||
|
use dpi::{LogicalPosition, LogicalSize};
|
||||||
use platform::MonitorId as PlatformMonitorId;
|
use platform::MonitorId as PlatformMonitorId;
|
||||||
use platform::PlatformSpecificWindowBuilderAttributes;
|
use platform::PlatformSpecificWindowBuilderAttributes;
|
||||||
use platform::x11::MonitorId as X11MonitorId;
|
use platform::x11::MonitorId as X11MonitorId;
|
||||||
|
@ -61,7 +62,8 @@ pub struct UnownedWindow {
|
||||||
root: ffi::Window, // never changes
|
root: ffi::Window, // never changes
|
||||||
screen_id: i32, // never changes
|
screen_id: i32, // never changes
|
||||||
cursor: Mutex<MouseCursor>,
|
cursor: Mutex<MouseCursor>,
|
||||||
cursor_state: Mutex<CursorState>,
|
cursor_grabbed: Mutex<bool>,
|
||||||
|
cursor_hidden: Mutex<bool>,
|
||||||
ime_sender: Mutex<ImeSender>,
|
ime_sender: Mutex<ImeSender>,
|
||||||
pub multitouch: bool, // never changes
|
pub multitouch: bool, // never changes
|
||||||
pub shared_state: Mutex<SharedState>,
|
pub shared_state: Mutex<SharedState>,
|
||||||
|
@ -160,7 +162,8 @@ impl UnownedWindow {
|
||||||
root,
|
root,
|
||||||
screen_id,
|
screen_id,
|
||||||
cursor: Default::default(),
|
cursor: Default::default(),
|
||||||
cursor_state: Default::default(),
|
cursor_grabbed: Default::default(),
|
||||||
|
cursor_hidden: Default::default(),
|
||||||
ime_sender: Mutex::new(event_loop.ime_sender.clone()),
|
ime_sender: Mutex::new(event_loop.ime_sender.clone()),
|
||||||
multitouch: window_attrs.multitouch,
|
multitouch: window_attrs.multitouch,
|
||||||
shared_state: SharedState::new(),
|
shared_state: SharedState::new(),
|
||||||
|
@ -976,9 +979,6 @@ impl UnownedWindow {
|
||||||
|
|
||||||
MouseCursor::ZoomIn => load(b"zoom-in\0"),
|
MouseCursor::ZoomIn => load(b"zoom-in\0"),
|
||||||
MouseCursor::ZoomOut => load(b"zoom-out\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]
|
#[inline]
|
||||||
pub fn set_cursor(&self, cursor: MouseCursor) {
|
pub fn set_cursor(&self, cursor: MouseCursor) {
|
||||||
*self.cursor.lock() = cursor;
|
*self.cursor.lock() = cursor;
|
||||||
if *self.cursor_state.lock() != CursorState::Hide {
|
if !*self.cursor_hidden.lock() {
|
||||||
self.update_cursor(self.get_cursor(cursor));
|
self.update_cursor(self.get_cursor(cursor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1039,67 +1039,73 @@ impl UnownedWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
|
||||||
use CursorState::*;
|
let mut grabbed_lock = self.cursor_grabbed.lock();
|
||||||
|
if grab == *grabbed_lock { return Ok(()); }
|
||||||
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 => {
|
|
||||||
unsafe {
|
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.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 {
|
match result {
|
||||||
Normal => {
|
ffi::GrabSuccess => Ok(()),
|
||||||
*cursor_state_lock = state;
|
ffi::AlreadyGrabbed => Err("Cursor could not be grabbed: already grabbed by another client"),
|
||||||
Ok(())
|
ffi::GrabInvalidTime => Err("Cursor could not be grabbed: invalid time"),
|
||||||
},
|
ffi::GrabNotViewable => Err("Cursor could not be grabbed: grab location not viewable"),
|
||||||
Hide => {
|
ffi::GrabFrozen => Err("Cursor could not be grabbed: frozen by another client"),
|
||||||
*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!(),
|
_ => 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]
|
#[inline]
|
||||||
|
|
|
@ -27,7 +27,6 @@ use objc::declare::ClassDecl;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
CreationError,
|
CreationError,
|
||||||
CursorState,
|
|
||||||
Event,
|
Event,
|
||||||
LogicalPosition,
|
LogicalPosition,
|
||||||
LogicalSize,
|
LogicalSize,
|
||||||
|
@ -527,6 +526,7 @@ pub struct Window2 {
|
||||||
pub window: IdRef,
|
pub window: IdRef,
|
||||||
pub delegate: WindowDelegate,
|
pub delegate: WindowDelegate,
|
||||||
pub input_context: IdRef,
|
pub input_context: IdRef,
|
||||||
|
cursor_hidden: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for Window2 {}
|
unsafe impl Send for Window2 {}
|
||||||
|
@ -674,6 +674,7 @@ impl Window2 {
|
||||||
window: window,
|
window: window,
|
||||||
delegate: WindowDelegate::new(delegate_state),
|
delegate: WindowDelegate::new(delegate_state),
|
||||||
input_context,
|
input_context,
|
||||||
|
cursor_hidden: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set fullscreen mode after we setup everything
|
// Set fullscreen mode after we setup everything
|
||||||
|
@ -980,7 +981,7 @@ impl Window2 {
|
||||||
MouseCursor::SeResize | MouseCursor::SwResize |
|
MouseCursor::SeResize | MouseCursor::SwResize |
|
||||||
MouseCursor::NwseResize | MouseCursor::NeswResize |
|
MouseCursor::NwseResize | MouseCursor::NeswResize |
|
||||||
|
|
||||||
MouseCursor::Cell | MouseCursor::NoneCursor |
|
MouseCursor::Cell |
|
||||||
MouseCursor::Wait | MouseCursor::Progress | MouseCursor::Help |
|
MouseCursor::Wait | MouseCursor::Progress | MouseCursor::Help |
|
||||||
MouseCursor::Move | MouseCursor::AllScroll | MouseCursor::ZoomIn |
|
MouseCursor::Move | MouseCursor::AllScroll | MouseCursor::ZoomIn |
|
||||||
MouseCursor::ZoomOut => "arrowCursor",
|
MouseCursor::ZoomOut => "arrowCursor",
|
||||||
|
@ -994,25 +995,25 @@ impl Window2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
#[inline]
|
||||||
let cls = Class::get("NSCursor").unwrap();
|
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
|
||||||
|
// TODO: Do this for real https://stackoverflow.com/a/40922095/5435443
|
||||||
// TODO: Check for errors.
|
CGDisplay::associate_mouse_and_mouse_cursor_position(!grab)
|
||||||
match state {
|
.map_err(|status| format!("Failed to grab cursor: `CGError` {:?}", status))
|
||||||
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 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 {
|
use {
|
||||||
ControlFlow,
|
ControlFlow,
|
||||||
CursorState,
|
|
||||||
Event,
|
Event,
|
||||||
EventsLoopClosed,
|
EventsLoopClosed,
|
||||||
KeyboardInput,
|
KeyboardInput,
|
||||||
|
@ -83,8 +82,8 @@ pub struct SavedWindowInfo {
|
||||||
pub struct WindowState {
|
pub struct WindowState {
|
||||||
/// Cursor to set at the next `WM_SETCURSOR` event received.
|
/// Cursor to set at the next `WM_SETCURSOR` event received.
|
||||||
pub cursor: Cursor,
|
pub cursor: Cursor,
|
||||||
/// Cursor state to set at the next `WM_SETCURSOR` event received.
|
pub cursor_grabbed: bool,
|
||||||
pub cursor_state: CursorState,
|
pub cursor_hidden: bool,
|
||||||
/// Used by `WM_GETMINMAXINFO`.
|
/// Used by `WM_GETMINMAXINFO`.
|
||||||
pub max_size: Option<PhysicalSize>,
|
pub max_size: Option<PhysicalSize>,
|
||||||
pub min_size: Option<PhysicalSize>,
|
pub min_size: Option<PhysicalSize>,
|
||||||
|
|
|
@ -17,7 +17,6 @@ use winapi::um::winnt::{LONG, LPCWSTR};
|
||||||
|
|
||||||
use {
|
use {
|
||||||
CreationError,
|
CreationError,
|
||||||
CursorState,
|
|
||||||
Icon,
|
Icon,
|
||||||
LogicalPosition,
|
LogicalPosition,
|
||||||
LogicalSize,
|
LogicalSize,
|
||||||
|
@ -26,9 +25,9 @@ use {
|
||||||
PhysicalSize,
|
PhysicalSize,
|
||||||
WindowAttributes,
|
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::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::icon::{self, IconType, WinIcon};
|
||||||
use platform::platform::raw_input::register_all_mice_and_keyboards_for_raw_input;
|
use platform::platform::raw_input::register_all_mice_and_keyboards_for_raw_input;
|
||||||
use platform::platform::util;
|
use platform::platform::util;
|
||||||
|
@ -336,7 +335,6 @@ impl Window {
|
||||||
MouseCursor::Wait => winuser::IDC_WAIT,
|
MouseCursor::Wait => winuser::IDC_WAIT,
|
||||||
MouseCursor::Progress => winuser::IDC_APPSTARTING,
|
MouseCursor::Progress => winuser::IDC_APPSTARTING,
|
||||||
MouseCursor::Help => winuser::IDC_HELP,
|
MouseCursor::Help => winuser::IDC_HELP,
|
||||||
MouseCursor::NoneCursor => ptr::null(),
|
|
||||||
_ => winuser::IDC_ARROW, // use arrow for the missing cases.
|
_ => winuser::IDC_ARROW, // use arrow for the missing cases.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -363,36 +361,13 @@ impl Window {
|
||||||
Ok(util::rect_eq(&client_rect, &clip_rect))
|
Ok(util::rect_eq(&client_rect, &clip_rect))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change_cursor_state(
|
pub(crate) unsafe fn grab_cursor_inner(window: &WindowWrapper, grab: bool) -> Result<(), String> {
|
||||||
window: &WindowWrapper,
|
if grab {
|
||||||
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 {
|
|
||||||
let mut rect = mem::uninitialized();
|
let mut rect = mem::uninitialized();
|
||||||
if winuser::GetClientRect(window.0, &mut rect) == 0 {
|
if winuser::GetClientRect(window.0, &mut rect) == 0 {
|
||||||
return Err("`GetClientRect` failed".to_owned());
|
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 {
|
if winuser::ClientToScreen(window.0, &mut rect.left as *mut _ as LPPOINT) == 0 {
|
||||||
return Err("`ClientToScreen` (left, top) failed".to_owned());
|
return Err("`ClientToScreen` (left, top) failed".to_owned());
|
||||||
}
|
}
|
||||||
|
@ -402,44 +377,62 @@ impl Window {
|
||||||
if winuser::ClipCursor(&rect) == 0 {
|
if winuser::ClipCursor(&rect) == 0 {
|
||||||
return Err("`ClipCursor` failed".to_owned());
|
return Err("`ClipCursor` failed".to_owned());
|
||||||
}
|
}
|
||||||
if current_state != CursorState::Hide {
|
} else {
|
||||||
winuser::ShowCursor(FALSE);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
(CursorState::Grab, CursorState::Normal) => unsafe {
|
|
||||||
if winuser::ClipCursor(ptr::null()) == 0 {
|
if winuser::ClipCursor(ptr::null()) == 0 {
|
||||||
return Err("`ClipCursor` failed".to_owned());
|
return Err("`ClipCursor` failed".to_owned());
|
||||||
}
|
}
|
||||||
winuser::ShowCursor(TRUE);
|
}
|
||||||
},
|
Ok(())
|
||||||
};
|
|
||||||
Ok(state)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
#[inline]
|
||||||
let is_grabbed = unsafe { self.cursor_is_grabbed() }?;
|
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
|
||||||
let (tx, rx) = channel();
|
let currently_grabbed = unsafe { self.cursor_is_grabbed() }?;
|
||||||
let window = self.window.clone();
|
|
||||||
let window_state = Arc::clone(&self.window_state);
|
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 |_| {
|
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||||
let mut window_state_lock = window_state.lock().unwrap();
|
let result = unsafe { Self::grab_cursor_inner(&window, grab) };
|
||||||
// We should probably also check if the cursor is hidden,
|
if result.is_ok() {
|
||||||
// but `GetCursorInfo` isn't in winapi-rs yet, and it doesn't seem to matter as much.
|
window_state.lock().unwrap().cursor_grabbed = grab;
|
||||||
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 _ = tx.send(result);
|
let _ = tx.send(result);
|
||||||
});
|
});
|
||||||
rx.recv().unwrap()
|
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]
|
#[inline]
|
||||||
pub fn get_hidpi_factor(&self) -> f64 {
|
pub fn get_hidpi_factor(&self) -> f64 {
|
||||||
get_window_scale_factor(self.window.0, self.window.1)
|
get_window_scale_factor(self.window.0, self.window.1)
|
||||||
|
@ -518,26 +511,28 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn restore_saved_window(&self) {
|
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
|
// 'saved_window_info' can be None if the window has never been
|
||||||
// in fullscreen mode before this method gets called.
|
// 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;
|
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
|
// 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
|
// here are ugly, but if SetWindowPos() doesn't redraw, the taskbar won't be
|
||||||
// repainted. Better-looking methods welcome.
|
// repainted. Better-looking methods welcome.
|
||||||
{
|
|
||||||
let saved_window_info = window_state.saved_window_info.as_mut().unwrap();
|
|
||||||
saved_window_info.is_fullscreen = false;
|
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 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 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 maximized = self.maximized.get();
|
||||||
let resizable = self.resizable.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.
|
// 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.
|
// `ShowWindow` resizes the window, so it must be called from the main thread.
|
||||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||||
|
let _ = Self::grab_cursor_inner(&window, false);
|
||||||
|
|
||||||
if resizable {
|
if resizable {
|
||||||
style |= winuser::WS_SIZEBOX as LONG;
|
style |= winuser::WS_SIZEBOX as LONG;
|
||||||
} else {
|
} else {
|
||||||
|
@ -577,6 +574,9 @@ impl Window {
|
||||||
);
|
);
|
||||||
|
|
||||||
mark_fullscreen(window.0, false);
|
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 (x, y): (i32, i32) = inner.get_position().into();
|
||||||
let (width, height): (u32, u32) = inner.get_dimensions().into();
|
let (width, height): (u32, u32) = inner.get_dimensions().into();
|
||||||
let window = self.window.clone();
|
let window = self.window.clone();
|
||||||
|
let window_state = Arc::clone(&self.window_state);
|
||||||
|
|
||||||
let (style, ex_style) = self.set_fullscreen_style();
|
let (style, ex_style) = self.set_fullscreen_style();
|
||||||
|
|
||||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||||
|
let _ = Self::grab_cursor_inner(&window, false);
|
||||||
|
|
||||||
winuser::SetWindowLongW(
|
winuser::SetWindowLongW(
|
||||||
window.0,
|
window.0,
|
||||||
winuser::GWL_STYLE,
|
winuser::GWL_STYLE,
|
||||||
|
@ -622,6 +625,9 @@ impl Window {
|
||||||
);
|
);
|
||||||
|
|
||||||
mark_fullscreen(window.0, true);
|
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 => {
|
&None => {
|
||||||
|
@ -980,7 +986,8 @@ unsafe fn init(
|
||||||
.map(|logical_size| PhysicalSize::from_logical(logical_size, dpi_factor));
|
.map(|logical_size| PhysicalSize::from_logical(logical_size, dpi_factor));
|
||||||
let mut window_state = events_loop::WindowState {
|
let mut window_state = events_loop::WindowState {
|
||||||
cursor: Cursor(winuser::IDC_ARROW), // use arrow by default
|
cursor: Cursor(winuser::IDC_ARROW), // use arrow by default
|
||||||
cursor_state: CursorState::Normal,
|
cursor_grabbed: false,
|
||||||
|
cursor_hidden: false,
|
||||||
max_size,
|
max_size,
|
||||||
min_size,
|
min_size,
|
||||||
mouse_in_window: false,
|
mouse_in_window: false,
|
||||||
|
|
|
@ -2,7 +2,6 @@ use std::collections::vec_deque::IntoIter as VecDequeIter;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
CreationError,
|
CreationError,
|
||||||
CursorState,
|
|
||||||
EventsLoop,
|
EventsLoop,
|
||||||
Icon,
|
Icon,
|
||||||
LogicalPosition,
|
LogicalPosition,
|
||||||
|
@ -327,12 +326,31 @@ impl Window {
|
||||||
self.window.set_cursor_position(position)
|
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]
|
#[inline]
|
||||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
|
||||||
self.window.set_cursor_state(state)
|
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
|
/// Sets the window to maximized or back
|
||||||
|
|
Loading…
Reference in a new issue