Skip to content

Commit

Permalink
grab_cursor and hide_cursor (#571)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
francesca64 authored Jun 18, 2018
1 parent 042f5fe commit fb7528c
Show file tree
Hide file tree
Showing 15 changed files with 323 additions and 310 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion examples/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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| {
Expand Down
38 changes: 38 additions & 0 deletions examples/cursor_grab.rs
Original file line number Diff line number Diff line change
@@ -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
});
}
45 changes: 0 additions & 45 deletions examples/grabbing.rs

This file was deleted.

24 changes: 0 additions & 24 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,6 @@ pub enum MouseCursor {
/// Cursor showing that something cannot be done.
NotAllowed,
ContextMenu,
NoneCursor,
Cell,
VerticalText,
Alias,
Expand Down Expand Up @@ -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 {
Expand Down
9 changes: 6 additions & 3 deletions src/platform/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use std::sync::mpsc::{Receiver, channel};

use {
CreationError,
CursorState,
Event,
LogicalPosition,
LogicalSize,
Expand Down Expand Up @@ -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]
Expand Down
84 changes: 48 additions & 36 deletions src/platform/emscripten/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>>>,
}
Expand Down Expand Up @@ -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(),
};
Expand Down Expand Up @@ -498,48 +500,57 @@ impl Window {
}

#[inline]
pub fn show(&self) {}
pub fn show(&self) {
// N/A
}

#[inline]
pub fn hide(&self) {}
pub fn hide(&self) {
// N/A
}

#[inline]
pub fn set_cursor(&self, _cursor: ::MouseCursor) {}
pub fn set_cursor(&self, _cursor: ::MouseCursor) {
// N/A
}

#[inline]
pub fn set_cursor_state(&self, state: ::CursorState) -> Result<(), String> {
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(());
}

// 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 => (),
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())?;
}
}
*grabbed_lock = grab;
Ok(())
}

// Update
*old_state = state;

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]
Expand Down Expand Up @@ -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 {
Expand Down
9 changes: 6 additions & 3 deletions src/platform/ios/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ use objc::runtime::{BOOL, Class, Object, Sel, YES};

use {
CreationError,
CursorState,
Event,
LogicalPosition,
LogicalSize,
Expand Down Expand Up @@ -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]
Expand Down
20 changes: 12 additions & 8 deletions src/platform/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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!(),
}
}

Expand Down
14 changes: 2 additions & 12 deletions src/platform/linux/wayland/window.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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()
Expand Down
Loading

0 comments on commit fb7528c

Please sign in to comment.