mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-23 22:01:31 +11:00
Refine Window::set_cursor_grab
API
This commit renames `Window::set_cursor_grab` to `Window::set_cursor_grab_mode`. The new API now accepts enumeration to control the way cursor grab is performed. The value could be: `lock`, `confine`, or `none`. This commit also implements `Window::set_cursor_position` for Wayland, since it's tied to locked cursor. Implements API from #1677.
This commit is contained in:
parent
8ef9fe44c7
commit
9e6f666616
|
@ -58,6 +58,8 @@ And please only add new entries to the top of this list, right below the `# Unre
|
||||||
- **Breaking:** On X11, device events are now ignored for unfocused windows by default, use `EventLoopWindowTarget::set_device_event_filter` to set the filter level.
|
- **Breaking:** On X11, device events are now ignored for unfocused windows by default, use `EventLoopWindowTarget::set_device_event_filter` to set the filter level.
|
||||||
- Implemented `Default` on `EventLoop<()>`.
|
- Implemented `Default` on `EventLoop<()>`.
|
||||||
- Implemented `Eq` for `Fullscreen`, `Theme`, and `UserAttentionType`.
|
- Implemented `Eq` for `Fullscreen`, `Theme`, and `UserAttentionType`.
|
||||||
|
- **Breaking:** `Window::set_cursor_grab` now accepts `CursorGrabMode` to control grabbing behavior.
|
||||||
|
- On Wayland, add support for `Window::set_cursor_position`.
|
||||||
|
|
||||||
# 0.26.1 (2022-01-05)
|
# 0.26.1 (2022-01-05)
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,8 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||||
### Input Handling
|
### Input Handling
|
||||||
- **Mouse events**: Generating mouse events associated with pointer motion, click, and scrolling events.
|
- **Mouse events**: Generating mouse events associated with pointer motion, click, and scrolling events.
|
||||||
- **Mouse set location**: Forcibly changing the location of the pointer.
|
- **Mouse set location**: Forcibly changing the location of the pointer.
|
||||||
- **Cursor grab**: Locking the cursor so it cannot exit the client area of a window.
|
- **Cursor locking**: Locking the cursor inside the window so it cannot move.
|
||||||
|
- **Cursor confining**: Confining the cursor to the window bounds so it cannot leave them.
|
||||||
- **Cursor icon**: Changing the cursor icon, or hiding the cursor.
|
- **Cursor icon**: Changing the cursor icon, or hiding the cursor.
|
||||||
- **Cursor hittest**: Handle or ignore mouse events for a window.
|
- **Cursor hittest**: Handle or ignore mouse events for a window.
|
||||||
- **Touch events**: Single-touch events.
|
- **Touch events**: Single-touch events.
|
||||||
|
@ -197,8 +198,9 @@ Legend:
|
||||||
|Feature |Windows |MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |
|
|Feature |Windows |MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |
|
||||||
|----------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
|
|----------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
|
||||||
|Mouse events |✔️ |▢[#63] |✔️ |✔️ |**N/A**|**N/A**|✔️ |
|
|Mouse events |✔️ |▢[#63] |✔️ |✔️ |**N/A**|**N/A**|✔️ |
|
||||||
|Mouse set location |✔️ |✔️ |✔️ |❓ |**N/A**|**N/A**|**N/A**|
|
|Mouse set location |✔️ |✔️ |✔️ |✔️(when locked) |**N/A**|**N/A**|**N/A**|
|
||||||
|Cursor grab |✔️ |▢[#165] |▢[#242] |✔️ |**N/A**|**N/A**|✔️ |
|
|Cursor locking |❌ |✔️ |❌ |✔️ |**N/A**|**N/A**|✔️ |
|
||||||
|
|Cursor confining |✔️ |❌ |✔️ |✔️ |**N/A**|**N/A**|❌ |
|
||||||
|Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |
|
|Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |
|
||||||
|Cursor hittest |✔️ |✔️ |❌ |✔️ |**N/A**|**N/A**|❌ |
|
|Cursor hittest |✔️ |✔️ |❌ |✔️ |**N/A**|**N/A**|❌ |
|
||||||
|Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |❌ |
|
|Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |❌ |
|
||||||
|
|
|
@ -4,7 +4,7 @@ use simple_logger::SimpleLogger;
|
||||||
use winit::{
|
use winit::{
|
||||||
event::{DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, WindowEvent},
|
event::{DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, WindowEvent},
|
||||||
event_loop::EventLoop,
|
event_loop::EventLoop,
|
||||||
window::WindowBuilder,
|
window::{CursorGrabMode, WindowBuilder},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -34,11 +34,23 @@ fn main() {
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
use winit::event::VirtualKeyCode::*;
|
use winit::event::VirtualKeyCode::*;
|
||||||
match key {
|
let result = match key {
|
||||||
Escape => control_flow.set_exit(),
|
Escape => {
|
||||||
G => window.set_cursor_grab(!modifiers.shift()).unwrap(),
|
control_flow.set_exit();
|
||||||
H => window.set_cursor_visible(modifiers.shift()),
|
Ok(())
|
||||||
_ => (),
|
}
|
||||||
|
G => window.set_cursor_grab(CursorGrabMode::Confined),
|
||||||
|
L => window.set_cursor_grab(CursorGrabMode::Locked),
|
||||||
|
A => window.set_cursor_grab(CursorGrabMode::None),
|
||||||
|
H => {
|
||||||
|
window.set_cursor_visible(modifiers.shift());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => Ok(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(err) = result {
|
||||||
|
println!("error: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WindowEvent::ModifiersChanged(m) => modifiers = m,
|
WindowEvent::ModifiersChanged(m) => modifiers = m,
|
||||||
|
|
|
@ -9,7 +9,7 @@ fn main() {
|
||||||
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||||
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
|
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
|
||||||
event_loop::EventLoop,
|
event_loop::EventLoop,
|
||||||
window::{CursorIcon, Fullscreen, WindowBuilder},
|
window::{CursorGrabMode, CursorIcon, Fullscreen, WindowBuilder},
|
||||||
};
|
};
|
||||||
|
|
||||||
const WINDOW_COUNT: usize = 3;
|
const WINDOW_COUNT: usize = 3;
|
||||||
|
@ -88,7 +88,21 @@ fn main() {
|
||||||
}
|
}
|
||||||
(false, _) => None,
|
(false, _) => None,
|
||||||
}),
|
}),
|
||||||
G => window.set_cursor_grab(state).unwrap(),
|
L if state => {
|
||||||
|
if let Err(err) = window.set_cursor_grab(CursorGrabMode::Locked) {
|
||||||
|
println!("error: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
G if state => {
|
||||||
|
if let Err(err) = window.set_cursor_grab(CursorGrabMode::Confined) {
|
||||||
|
println!("error: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
G | L if !state => {
|
||||||
|
if let Err(err) = window.set_cursor_grab(CursorGrabMode::None) {
|
||||||
|
println!("error: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
H => window.set_cursor_visible(!state),
|
H => window.set_cursor_visible(!state),
|
||||||
I => {
|
I => {
|
||||||
println!("Info:");
|
println!("Info:");
|
||||||
|
|
|
@ -20,7 +20,8 @@ use crate::{
|
||||||
error,
|
error,
|
||||||
event::{self, VirtualKeyCode},
|
event::{self, VirtualKeyCode},
|
||||||
event_loop::{self, ControlFlow},
|
event_loop::{self, ControlFlow},
|
||||||
monitor, window,
|
monitor,
|
||||||
|
window::{self, CursorGrabMode},
|
||||||
};
|
};
|
||||||
|
|
||||||
static CONFIG: Lazy<RwLock<Configuration>> = Lazy::new(|| {
|
static CONFIG: Lazy<RwLock<Configuration>> = Lazy::new(|| {
|
||||||
|
@ -765,7 +766,7 @@ impl Window {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_cursor_grab(&self, _: bool) -> Result<(), error::ExternalError> {
|
pub fn set_cursor_grab(&self, _: CursorGrabMode) -> Result<(), error::ExternalError> {
|
||||||
Err(error::ExternalError::NotSupported(
|
Err(error::ExternalError::NotSupported(
|
||||||
error::NotSupportedError::new(),
|
error::NotSupportedError::new(),
|
||||||
))
|
))
|
||||||
|
|
|
@ -23,7 +23,8 @@ use crate::{
|
||||||
monitor, view, EventLoopWindowTarget, MonitorHandle,
|
monitor, view, EventLoopWindowTarget, MonitorHandle,
|
||||||
},
|
},
|
||||||
window::{
|
window::{
|
||||||
CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, WindowId as RootWindowId,
|
CursorGrabMode, CursorIcon, Fullscreen, UserAttentionType, WindowAttributes,
|
||||||
|
WindowId as RootWindowId,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -184,7 +185,7 @@ impl Inner {
|
||||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_cursor_grab(&self, _grab: bool) -> Result<(), ExternalError> {
|
pub fn set_cursor_grab(&self, _: CursorGrabMode) -> Result<(), ExternalError> {
|
||||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,6 @@
|
||||||
#[cfg(all(not(feature = "x11"), not(feature = "wayland")))]
|
#[cfg(all(not(feature = "x11"), not(feature = "wayland")))]
|
||||||
compile_error!("Please select a feature to build for unix: `x11`, `wayland`");
|
compile_error!("Please select a feature to build for unix: `x11`, `wayland`");
|
||||||
|
|
||||||
#[cfg(feature = "wayland")]
|
|
||||||
use crate::window::Theme;
|
|
||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
|
@ -28,6 +26,8 @@ use raw_window_handle::RawWindowHandle;
|
||||||
pub use self::x11::XNotSupported;
|
pub use self::x11::XNotSupported;
|
||||||
#[cfg(feature = "x11")]
|
#[cfg(feature = "x11")]
|
||||||
use self::x11::{ffi::XVisualInfo, util::WindowType as XWindowType, XConnection, XError};
|
use self::x11::{ffi::XVisualInfo, util::WindowType as XWindowType, XConnection, XError};
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
|
use crate::window::Theme;
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||||
error::{ExternalError, NotSupportedError, OsError as RootOsError},
|
error::{ExternalError, NotSupportedError, OsError as RootOsError},
|
||||||
|
@ -37,7 +37,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
icon::Icon,
|
icon::Icon,
|
||||||
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
|
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
|
||||||
window::{CursorIcon, Fullscreen, UserAttentionType, WindowAttributes},
|
window::{CursorGrabMode, CursorIcon, Fullscreen, UserAttentionType, WindowAttributes},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
|
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
|
||||||
|
@ -388,8 +388,8 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
|
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||||
x11_or_wayland!(match self; Window(window) => window.set_cursor_grab(grab))
|
x11_or_wayland!(match self; Window(window) => window.set_cursor_grab(mode))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -24,23 +24,23 @@ use sctk::shm::ShmHandler;
|
||||||
/// Set of extra features that are supported by the compositor.
|
/// Set of extra features that are supported by the compositor.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct WindowingFeatures {
|
pub struct WindowingFeatures {
|
||||||
cursor_grab: bool,
|
pointer_constraints: bool,
|
||||||
xdg_activation: bool,
|
xdg_activation: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowingFeatures {
|
impl WindowingFeatures {
|
||||||
/// Create `WindowingFeatures` based on the presented interfaces.
|
/// Create `WindowingFeatures` based on the presented interfaces.
|
||||||
pub fn new(env: &Environment<WinitEnv>) -> Self {
|
pub fn new(env: &Environment<WinitEnv>) -> Self {
|
||||||
let cursor_grab = env.get_global::<ZwpPointerConstraintsV1>().is_some();
|
let pointer_constraints = env.get_global::<ZwpPointerConstraintsV1>().is_some();
|
||||||
let xdg_activation = env.get_global::<XdgActivationV1>().is_some();
|
let xdg_activation = env.get_global::<XdgActivationV1>().is_some();
|
||||||
Self {
|
Self {
|
||||||
cursor_grab,
|
pointer_constraints,
|
||||||
xdg_activation,
|
xdg_activation,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cursor_grab(&self) -> bool {
|
pub fn pointer_constraints(&self) -> bool {
|
||||||
self.cursor_grab
|
self.pointer_constraints
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xdg_activation(&self) -> bool {
|
pub fn xdg_activation(&self) -> bool {
|
||||||
|
|
|
@ -5,8 +5,9 @@ use std::rc::Rc;
|
||||||
|
|
||||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||||
use sctk::reexports::client::Attached;
|
use sctk::reexports::client::Attached;
|
||||||
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_pointer_constraints_v1::{ZwpPointerConstraintsV1};
|
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_pointer_constraints_v1::ZwpPointerConstraintsV1;
|
||||||
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_confined_pointer_v1::ZwpConfinedPointerV1;
|
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_confined_pointer_v1::ZwpConfinedPointerV1;
|
||||||
|
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_locked_pointer_v1::ZwpLockedPointerV1;
|
||||||
|
|
||||||
use crate::event::{ModifiersState, TouchPhase};
|
use crate::event::{ModifiersState, TouchPhase};
|
||||||
|
|
||||||
|
@ -25,6 +26,7 @@ pub(super) struct PointerData {
|
||||||
pub pointer_constraints: Option<Attached<ZwpPointerConstraintsV1>>,
|
pub pointer_constraints: Option<Attached<ZwpPointerConstraintsV1>>,
|
||||||
|
|
||||||
pub confined_pointer: Rc<RefCell<Option<ZwpConfinedPointerV1>>>,
|
pub confined_pointer: Rc<RefCell<Option<ZwpConfinedPointerV1>>>,
|
||||||
|
pub locked_pointer: Rc<RefCell<Option<ZwpLockedPointerV1>>>,
|
||||||
|
|
||||||
/// Latest observed serial in pointer events.
|
/// Latest observed serial in pointer events.
|
||||||
pub latest_serial: Rc<Cell<u32>>,
|
pub latest_serial: Rc<Cell<u32>>,
|
||||||
|
@ -39,6 +41,7 @@ pub(super) struct PointerData {
|
||||||
impl PointerData {
|
impl PointerData {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
confined_pointer: Rc<RefCell<Option<ZwpConfinedPointerV1>>>,
|
confined_pointer: Rc<RefCell<Option<ZwpConfinedPointerV1>>>,
|
||||||
|
locked_pointer: Rc<RefCell<Option<ZwpLockedPointerV1>>>,
|
||||||
pointer_constraints: Option<Attached<ZwpPointerConstraintsV1>>,
|
pointer_constraints: Option<Attached<ZwpPointerConstraintsV1>>,
|
||||||
modifiers_state: Rc<RefCell<ModifiersState>>,
|
modifiers_state: Rc<RefCell<ModifiersState>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -47,6 +50,7 @@ impl PointerData {
|
||||||
latest_serial: Rc::new(Cell::new(0)),
|
latest_serial: Rc::new(Cell::new(0)),
|
||||||
latest_enter_serial: Rc::new(Cell::new(0)),
|
latest_enter_serial: Rc::new(Cell::new(0)),
|
||||||
confined_pointer,
|
confined_pointer,
|
||||||
|
locked_pointer,
|
||||||
modifiers_state,
|
modifiers_state,
|
||||||
pointer_constraints,
|
pointer_constraints,
|
||||||
axis_data: AxisData::new(),
|
axis_data: AxisData::new(),
|
||||||
|
|
|
@ -60,6 +60,7 @@ pub(super) fn handle_pointer(
|
||||||
let winit_pointer = WinitPointer {
|
let winit_pointer = WinitPointer {
|
||||||
pointer,
|
pointer,
|
||||||
confined_pointer: Rc::downgrade(&pointer_data.confined_pointer),
|
confined_pointer: Rc::downgrade(&pointer_data.confined_pointer),
|
||||||
|
locked_pointer: Rc::downgrade(&pointer_data.locked_pointer),
|
||||||
pointer_constraints: pointer_data.pointer_constraints.clone(),
|
pointer_constraints: pointer_data.pointer_constraints.clone(),
|
||||||
latest_serial: pointer_data.latest_serial.clone(),
|
latest_serial: pointer_data.latest_serial.clone(),
|
||||||
latest_enter_serial: pointer_data.latest_enter_serial.clone(),
|
latest_enter_serial: pointer_data.latest_enter_serial.clone(),
|
||||||
|
@ -104,6 +105,7 @@ pub(super) fn handle_pointer(
|
||||||
let winit_pointer = WinitPointer {
|
let winit_pointer = WinitPointer {
|
||||||
pointer,
|
pointer,
|
||||||
confined_pointer: Rc::downgrade(&pointer_data.confined_pointer),
|
confined_pointer: Rc::downgrade(&pointer_data.confined_pointer),
|
||||||
|
locked_pointer: Rc::downgrade(&pointer_data.locked_pointer),
|
||||||
pointer_constraints: pointer_data.pointer_constraints.clone(),
|
pointer_constraints: pointer_data.pointer_constraints.clone(),
|
||||||
latest_serial: pointer_data.latest_serial.clone(),
|
latest_serial: pointer_data.latest_serial.clone(),
|
||||||
latest_enter_serial: pointer_data.latest_enter_serial.clone(),
|
latest_enter_serial: pointer_data.latest_enter_serial.clone(),
|
||||||
|
|
|
@ -11,6 +11,7 @@ use sctk::reexports::protocols::unstable::relative_pointer::v1::client::zwp_rela
|
||||||
use sctk::reexports::protocols::unstable::relative_pointer::v1::client::zwp_relative_pointer_v1::ZwpRelativePointerV1;
|
use sctk::reexports::protocols::unstable::relative_pointer::v1::client::zwp_relative_pointer_v1::ZwpRelativePointerV1;
|
||||||
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_pointer_constraints_v1::{ZwpPointerConstraintsV1, Lifetime};
|
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_pointer_constraints_v1::{ZwpPointerConstraintsV1, Lifetime};
|
||||||
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_confined_pointer_v1::ZwpConfinedPointerV1;
|
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_confined_pointer_v1::ZwpConfinedPointerV1;
|
||||||
|
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_locked_pointer_v1::ZwpLockedPointerV1;
|
||||||
|
|
||||||
use sctk::seat::pointer::{ThemeManager, ThemedPointer};
|
use sctk::seat::pointer::{ThemeManager, ThemedPointer};
|
||||||
use sctk::window::Window;
|
use sctk::window::Window;
|
||||||
|
@ -35,9 +36,13 @@ pub struct WinitPointer {
|
||||||
/// Cursor to handle confine requests.
|
/// Cursor to handle confine requests.
|
||||||
confined_pointer: Weak<RefCell<Option<ZwpConfinedPointerV1>>>,
|
confined_pointer: Weak<RefCell<Option<ZwpConfinedPointerV1>>>,
|
||||||
|
|
||||||
|
/// Cursor to handle locked requests.
|
||||||
|
locked_pointer: Weak<RefCell<Option<ZwpLockedPointerV1>>>,
|
||||||
|
|
||||||
/// Latest observed serial in pointer events.
|
/// Latest observed serial in pointer events.
|
||||||
/// used by Window::start_interactive_move()
|
/// used by Window::start_interactive_move()
|
||||||
latest_serial: Rc<Cell<u32>>,
|
latest_serial: Rc<Cell<u32>>,
|
||||||
|
|
||||||
/// Latest observed serial in pointer enter events.
|
/// Latest observed serial in pointer enter events.
|
||||||
/// used by Window::set_cursor()
|
/// used by Window::set_cursor()
|
||||||
latest_enter_serial: Rc<Cell<u32>>,
|
latest_enter_serial: Rc<Cell<u32>>,
|
||||||
|
@ -157,6 +162,52 @@ impl WinitPointer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lock(&self, surface: &WlSurface) {
|
||||||
|
let pointer_constraints = match &self.pointer_constraints {
|
||||||
|
Some(pointer_constraints) => pointer_constraints,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let locked_pointer = match self.locked_pointer.upgrade() {
|
||||||
|
Some(locked_pointer) => locked_pointer,
|
||||||
|
// A pointer is gone.
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
*locked_pointer.borrow_mut() = Some(init_locked_pointer(
|
||||||
|
pointer_constraints,
|
||||||
|
surface,
|
||||||
|
&*self.pointer,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unlock(&self) {
|
||||||
|
let locked_pointer = match self.locked_pointer.upgrade() {
|
||||||
|
Some(locked_pointer) => locked_pointer,
|
||||||
|
// A pointer is gone.
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut locked_pointer = locked_pointer.borrow_mut();
|
||||||
|
|
||||||
|
if let Some(locked_pointer) = locked_pointer.take() {
|
||||||
|
locked_pointer.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_cursor_position(&self, surface_x: u32, surface_y: u32) {
|
||||||
|
let locked_pointer = match self.locked_pointer.upgrade() {
|
||||||
|
Some(locked_pointer) => locked_pointer,
|
||||||
|
// A pointer is gone.
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let locked_pointer = locked_pointer.borrow_mut();
|
||||||
|
if let Some(locked_pointer) = locked_pointer.as_ref() {
|
||||||
|
locked_pointer.set_cursor_position_hint(surface_x.into(), surface_y.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn drag_window(&self, window: &Window<WinitFrame>) {
|
pub fn drag_window(&self, window: &Window<WinitFrame>) {
|
||||||
// WlPointer::setart_interactive_move() expects the last serial of *any*
|
// WlPointer::setart_interactive_move() expects the last serial of *any*
|
||||||
// pointer event (compare to set_cursor()).
|
// pointer event (compare to set_cursor()).
|
||||||
|
@ -174,6 +225,9 @@ pub(super) struct Pointers {
|
||||||
|
|
||||||
/// Confined pointer.
|
/// Confined pointer.
|
||||||
confined_pointer: Rc<RefCell<Option<ZwpConfinedPointerV1>>>,
|
confined_pointer: Rc<RefCell<Option<ZwpConfinedPointerV1>>>,
|
||||||
|
|
||||||
|
/// Locked pointer.
|
||||||
|
locked_pointer: Rc<RefCell<Option<ZwpLockedPointerV1>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pointers {
|
impl Pointers {
|
||||||
|
@ -185,11 +239,15 @@ impl Pointers {
|
||||||
modifiers_state: Rc<RefCell<ModifiersState>>,
|
modifiers_state: Rc<RefCell<ModifiersState>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let confined_pointer = Rc::new(RefCell::new(None));
|
let confined_pointer = Rc::new(RefCell::new(None));
|
||||||
|
let locked_pointer = Rc::new(RefCell::new(None));
|
||||||
|
|
||||||
let pointer_data = Rc::new(RefCell::new(PointerData::new(
|
let pointer_data = Rc::new(RefCell::new(PointerData::new(
|
||||||
confined_pointer.clone(),
|
confined_pointer.clone(),
|
||||||
|
locked_pointer.clone(),
|
||||||
pointer_constraints.clone(),
|
pointer_constraints.clone(),
|
||||||
modifiers_state,
|
modifiers_state,
|
||||||
)));
|
)));
|
||||||
|
|
||||||
let pointer_seat = seat.detach();
|
let pointer_seat = seat.detach();
|
||||||
let pointer = theme_manager.theme_pointer_with_impl(
|
let pointer = theme_manager.theme_pointer_with_impl(
|
||||||
seat,
|
seat,
|
||||||
|
@ -216,6 +274,7 @@ impl Pointers {
|
||||||
pointer,
|
pointer,
|
||||||
relative_pointer,
|
relative_pointer,
|
||||||
confined_pointer,
|
confined_pointer,
|
||||||
|
locked_pointer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -232,6 +291,11 @@ impl Drop for Pointers {
|
||||||
confined_pointer.destroy();
|
confined_pointer.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Drop lock ponter.
|
||||||
|
if let Some(locked_pointer) = self.locked_pointer.borrow_mut().take() {
|
||||||
|
locked_pointer.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
// Drop the pointer itself in case it's possible.
|
// Drop the pointer itself in case it's possible.
|
||||||
if self.pointer.as_ref().version() >= 3 {
|
if self.pointer.as_ref().version() >= 3 {
|
||||||
self.pointer.release();
|
self.pointer.release();
|
||||||
|
@ -264,3 +328,16 @@ pub(super) fn init_confined_pointer(
|
||||||
|
|
||||||
confined_pointer.detach()
|
confined_pointer.detach()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn init_locked_pointer(
|
||||||
|
pointer_constraints: &Attached<ZwpPointerConstraintsV1>,
|
||||||
|
surface: &WlSurface,
|
||||||
|
pointer: &WlPointer,
|
||||||
|
) -> ZwpLockedPointerV1 {
|
||||||
|
let locked_pointer =
|
||||||
|
pointer_constraints.lock_pointer(surface, pointer, None, Lifetime::Persistent);
|
||||||
|
|
||||||
|
locked_pointer.quick_assign(move |_, _, _| {});
|
||||||
|
|
||||||
|
locked_pointer.detach()
|
||||||
|
}
|
||||||
|
|
|
@ -17,7 +17,9 @@ use crate::platform_impl::{
|
||||||
MonitorHandle as PlatformMonitorHandle, OsError,
|
MonitorHandle as PlatformMonitorHandle, OsError,
|
||||||
PlatformSpecificWindowBuilderAttributes as PlatformAttributes,
|
PlatformSpecificWindowBuilderAttributes as PlatformAttributes,
|
||||||
};
|
};
|
||||||
use crate::window::{CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes};
|
use crate::window::{
|
||||||
|
CursorGrabMode, CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes,
|
||||||
|
};
|
||||||
|
|
||||||
use super::env::WindowingFeatures;
|
use super::env::WindowingFeatures;
|
||||||
use super::event_loop::WinitState;
|
use super::event_loop::WinitState;
|
||||||
|
@ -72,6 +74,9 @@ pub struct Window {
|
||||||
|
|
||||||
/// Whether the window is decorated.
|
/// Whether the window is decorated.
|
||||||
decorated: AtomicBool,
|
decorated: AtomicBool,
|
||||||
|
|
||||||
|
/// Grabbing mode.
|
||||||
|
cursor_grab_mode: Mutex<CursorGrabMode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
|
@ -285,6 +290,7 @@ impl Window {
|
||||||
windowing_features,
|
windowing_features,
|
||||||
resizeable: AtomicBool::new(attributes.resizable),
|
resizeable: AtomicBool::new(attributes.resizable),
|
||||||
decorated: AtomicBool::new(attributes.decorations),
|
decorated: AtomicBool::new(attributes.decorations),
|
||||||
|
cursor_grab_mode: Mutex::new(CursorGrabMode::None),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(window)
|
Ok(window)
|
||||||
|
@ -474,12 +480,17 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
|
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||||
if !self.windowing_features.cursor_grab() {
|
if !self.windowing_features.pointer_constraints() {
|
||||||
|
if mode == CursorGrabMode::None {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
return Err(ExternalError::NotSupported(NotSupportedError::new()));
|
return Err(ExternalError::NotSupported(NotSupportedError::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.send_request(WindowRequest::GrabCursor(grab));
|
*self.cursor_grab_mode.lock().unwrap() = mode;
|
||||||
|
self.send_request(WindowRequest::SetCursorGrabMode(mode));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -494,15 +505,19 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_position(&self, _: Position) -> Result<(), ExternalError> {
|
pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> {
|
||||||
// XXX This is possible if the locked pointer is being used. We don't have any
|
// Positon can be set only for locked cursor.
|
||||||
// API for that right now, but it could be added in
|
if *self.cursor_grab_mode.lock().unwrap() != CursorGrabMode::Locked {
|
||||||
// https://github.com/rust-windowing/winit/issues/1677.
|
return Err(ExternalError::Os(os_error!(OsError::WaylandMisc(
|
||||||
//
|
"cursor position can be set only for locked cursor."
|
||||||
// This function is essential for the locked pointer API.
|
))));
|
||||||
//
|
}
|
||||||
// See pointer-constraints-unstable-v1.xml.
|
|
||||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
let scale_factor = self.scale_factor() as f64;
|
||||||
|
let position = position.to_logical(scale_factor);
|
||||||
|
self.send_request(WindowRequest::SetLockedCursorPosition(position));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -19,7 +19,7 @@ use crate::platform_impl::wayland::event_loop::{EventSink, WinitState};
|
||||||
use crate::platform_impl::wayland::seat::pointer::WinitPointer;
|
use crate::platform_impl::wayland::seat::pointer::WinitPointer;
|
||||||
use crate::platform_impl::wayland::seat::text_input::TextInputHandler;
|
use crate::platform_impl::wayland::seat::text_input::TextInputHandler;
|
||||||
use crate::platform_impl::wayland::WindowId;
|
use crate::platform_impl::wayland::WindowId;
|
||||||
use crate::window::{CursorIcon, Theme, UserAttentionType};
|
use crate::window::{CursorGrabMode, CursorIcon, Theme, UserAttentionType};
|
||||||
|
|
||||||
use super::WinitFrame;
|
use super::WinitFrame;
|
||||||
|
|
||||||
|
@ -40,8 +40,11 @@ pub enum WindowRequest {
|
||||||
/// Change the cursor icon.
|
/// Change the cursor icon.
|
||||||
NewCursorIcon(CursorIcon),
|
NewCursorIcon(CursorIcon),
|
||||||
|
|
||||||
/// Grab cursor.
|
/// Change cursor grabbing mode.
|
||||||
GrabCursor(bool),
|
SetCursorGrabMode(CursorGrabMode),
|
||||||
|
|
||||||
|
/// Set cursor position.
|
||||||
|
SetLockedCursorPosition(LogicalPosition<u32>),
|
||||||
|
|
||||||
/// Drag window.
|
/// Drag window.
|
||||||
DragWindow,
|
DragWindow,
|
||||||
|
@ -172,7 +175,7 @@ pub struct WindowHandle {
|
||||||
cursor_visible: Cell<bool>,
|
cursor_visible: Cell<bool>,
|
||||||
|
|
||||||
/// Cursor confined to the surface.
|
/// Cursor confined to the surface.
|
||||||
confined: Cell<bool>,
|
cursor_grab_mode: Cell<CursorGrabMode>,
|
||||||
|
|
||||||
/// Pointers over the current surface.
|
/// Pointers over the current surface.
|
||||||
pointers: Vec<WinitPointer>,
|
pointers: Vec<WinitPointer>,
|
||||||
|
@ -208,7 +211,7 @@ impl WindowHandle {
|
||||||
pending_window_requests,
|
pending_window_requests,
|
||||||
cursor_icon: Cell::new(CursorIcon::Default),
|
cursor_icon: Cell::new(CursorIcon::Default),
|
||||||
is_resizable: Cell::new(true),
|
is_resizable: Cell::new(true),
|
||||||
confined: Cell::new(false),
|
cursor_grab_mode: Cell::new(CursorGrabMode::None),
|
||||||
cursor_visible: Cell::new(true),
|
cursor_visible: Cell::new(true),
|
||||||
pointers: Vec::new(),
|
pointers: Vec::new(),
|
||||||
text_inputs: Vec::new(),
|
text_inputs: Vec::new(),
|
||||||
|
@ -219,24 +222,37 @@ impl WindowHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_cursor_grab(&self, grab: bool) {
|
pub fn set_cursor_grab(&self, mode: CursorGrabMode) {
|
||||||
// The new requested state matches the current confine status, return.
|
// The new requested state matches the current confine status, return.
|
||||||
if self.confined.get() == grab {
|
let old_mode = self.cursor_grab_mode.replace(mode);
|
||||||
|
if old_mode == mode {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.confined.replace(grab);
|
// Clear old pointer data.
|
||||||
|
match old_mode {
|
||||||
|
CursorGrabMode::None => (),
|
||||||
|
CursorGrabMode::Confined => self.pointers.iter().for_each(|p| p.unconfine()),
|
||||||
|
CursorGrabMode::Locked => self.pointers.iter().for_each(|p| p.unlock()),
|
||||||
|
}
|
||||||
|
|
||||||
for pointer in self.pointers.iter() {
|
let surface = self.window.surface();
|
||||||
if self.confined.get() {
|
match mode {
|
||||||
let surface = self.window.surface();
|
CursorGrabMode::Locked => self.pointers.iter().for_each(|p| p.lock(surface)),
|
||||||
pointer.confine(surface);
|
CursorGrabMode::Confined => self.pointers.iter().for_each(|p| p.confine(surface)),
|
||||||
} else {
|
CursorGrabMode::None => {
|
||||||
pointer.unconfine();
|
// Current lock/confine was already removed.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_locked_cursor_position(&self, position: LogicalPosition<u32>) {
|
||||||
|
// XXX the cursor locking is ensured inside `Window`.
|
||||||
|
self.pointers
|
||||||
|
.iter()
|
||||||
|
.for_each(|p| p.set_cursor_position(position.x, position.y));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_user_attention(&self, request_type: Option<UserAttentionType>) {
|
pub fn set_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||||
let xdg_activation = match self.xdg_activation.as_ref() {
|
let xdg_activation = match self.xdg_activation.as_ref() {
|
||||||
None => return,
|
None => return,
|
||||||
|
@ -284,10 +300,13 @@ impl WindowHandle {
|
||||||
let position = self.pointers.iter().position(|p| *p == pointer);
|
let position = self.pointers.iter().position(|p| *p == pointer);
|
||||||
|
|
||||||
if position.is_none() {
|
if position.is_none() {
|
||||||
if self.confined.get() {
|
let surface = self.window.surface();
|
||||||
let surface = self.window.surface();
|
match self.cursor_grab_mode.get() {
|
||||||
pointer.confine(surface);
|
CursorGrabMode::None => (),
|
||||||
|
CursorGrabMode::Locked => pointer.lock(surface),
|
||||||
|
CursorGrabMode::Confined => pointer.confine(surface),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pointers.push(pointer);
|
self.pointers.push(pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,9 +321,11 @@ impl WindowHandle {
|
||||||
if let Some(position) = position {
|
if let Some(position) = position {
|
||||||
let pointer = self.pointers.remove(position);
|
let pointer = self.pointers.remove(position);
|
||||||
|
|
||||||
// Drop the confined pointer.
|
// Drop the grabbing mode.
|
||||||
if self.confined.get() {
|
match self.cursor_grab_mode.get() {
|
||||||
pointer.unconfine();
|
CursorGrabMode::None => (),
|
||||||
|
CursorGrabMode::Locked => pointer.unlock(),
|
||||||
|
CursorGrabMode::Confined => pointer.unconfine(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -428,8 +449,11 @@ pub fn handle_window_requests(winit_state: &mut WinitState) {
|
||||||
let event_sink = &mut winit_state.event_sink;
|
let event_sink = &mut winit_state.event_sink;
|
||||||
window_handle.set_ime_allowed(allow, event_sink);
|
window_handle.set_ime_allowed(allow, event_sink);
|
||||||
}
|
}
|
||||||
WindowRequest::GrabCursor(grab) => {
|
WindowRequest::SetCursorGrabMode(mode) => {
|
||||||
window_handle.set_cursor_grab(grab);
|
window_handle.set_cursor_grab(mode);
|
||||||
|
}
|
||||||
|
WindowRequest::SetLockedCursorPosition(position) => {
|
||||||
|
window_handle.set_locked_cursor_position(position);
|
||||||
}
|
}
|
||||||
WindowRequest::DragWindow => {
|
WindowRequest::DragWindow => {
|
||||||
window_handle.drag_window();
|
window_handle.drag_window();
|
||||||
|
|
|
@ -22,7 +22,7 @@ use crate::{
|
||||||
MonitorHandle as PlatformMonitorHandle, OsError, PlatformSpecificWindowBuilderAttributes,
|
MonitorHandle as PlatformMonitorHandle, OsError, PlatformSpecificWindowBuilderAttributes,
|
||||||
VideoMode as PlatformVideoMode,
|
VideoMode as PlatformVideoMode,
|
||||||
},
|
},
|
||||||
window::{CursorIcon, Fullscreen, Icon, UserAttentionType, WindowAttributes},
|
window::{CursorGrabMode, CursorIcon, Fullscreen, Icon, UserAttentionType, WindowAttributes},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
@ -106,7 +106,7 @@ 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<CursorIcon>,
|
cursor: Mutex<CursorIcon>,
|
||||||
cursor_grabbed: Mutex<bool>,
|
cursor_grabbed_mode: Mutex<CursorGrabMode>,
|
||||||
cursor_visible: Mutex<bool>,
|
cursor_visible: Mutex<bool>,
|
||||||
ime_sender: Mutex<ImeSender>,
|
ime_sender: Mutex<ImeSender>,
|
||||||
pub shared_state: Mutex<SharedState>,
|
pub shared_state: Mutex<SharedState>,
|
||||||
|
@ -276,7 +276,7 @@ impl UnownedWindow {
|
||||||
root,
|
root,
|
||||||
screen_id,
|
screen_id,
|
||||||
cursor: Default::default(),
|
cursor: Default::default(),
|
||||||
cursor_grabbed: Mutex::new(false),
|
cursor_grabbed_mode: Mutex::new(CursorGrabMode::None),
|
||||||
cursor_visible: Mutex::new(true),
|
cursor_visible: Mutex::new(true),
|
||||||
ime_sender: Mutex::new(event_loop.ime_sender.clone()),
|
ime_sender: Mutex::new(event_loop.ime_sender.clone()),
|
||||||
shared_state: SharedState::new(guessed_monitor, &window_attrs),
|
shared_state: SharedState::new(guessed_monitor, &window_attrs),
|
||||||
|
@ -1272,64 +1272,75 @@ impl UnownedWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
|
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||||
let mut grabbed_lock = self.cursor_grabbed.lock();
|
let mut grabbed_lock = self.cursor_grabbed_mode.lock();
|
||||||
if grab == *grabbed_lock {
|
if mode == *grabbed_lock {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// We ungrab before grabbing to prevent passive grabs from causing `AlreadyGrabbed`.
|
// We ungrab before grabbing to prevent passive grabs from causing `AlreadyGrabbed`.
|
||||||
// Therefore, this is common to both codepaths.
|
// 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);
|
||||||
}
|
}
|
||||||
let result = if grab {
|
|
||||||
let result = unsafe {
|
|
||||||
(self.xconn.xlib.XGrabPointer)(
|
|
||||||
self.xconn.display,
|
|
||||||
self.xwindow,
|
|
||||||
ffi::True,
|
|
||||||
(ffi::ButtonPressMask
|
|
||||||
| ffi::ButtonReleaseMask
|
|
||||||
| ffi::EnterWindowMask
|
|
||||||
| ffi::LeaveWindowMask
|
|
||||||
| ffi::PointerMotionMask
|
|
||||||
| ffi::PointerMotionHintMask
|
|
||||||
| ffi::Button1MotionMask
|
|
||||||
| ffi::Button2MotionMask
|
|
||||||
| ffi::Button3MotionMask
|
|
||||||
| ffi::Button4MotionMask
|
|
||||||
| ffi::Button5MotionMask
|
|
||||||
| ffi::ButtonMotionMask
|
|
||||||
| ffi::KeymapStateMask) as c_uint,
|
|
||||||
ffi::GrabModeAsync,
|
|
||||||
ffi::GrabModeAsync,
|
|
||||||
self.xwindow,
|
|
||||||
0,
|
|
||||||
ffi::CurrentTime,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
match result {
|
let result = match mode {
|
||||||
ffi::GrabSuccess => Ok(()),
|
CursorGrabMode::None => self
|
||||||
ffi::AlreadyGrabbed => {
|
.xconn
|
||||||
Err("Cursor could not be grabbed: already grabbed by another client")
|
|
||||||
}
|
|
||||||
ffi::GrabInvalidTime => Err("Cursor could not be grabbed: invalid time"),
|
|
||||||
ffi::GrabNotViewable => {
|
|
||||||
Err("Cursor could not be grabbed: grab location not viewable")
|
|
||||||
}
|
|
||||||
ffi::GrabFrozen => Err("Cursor could not be grabbed: frozen by another client"),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
.map_err(|err| ExternalError::Os(os_error!(OsError::XMisc(err))))
|
|
||||||
} else {
|
|
||||||
self.xconn
|
|
||||||
.flush_requests()
|
.flush_requests()
|
||||||
.map_err(|err| ExternalError::Os(os_error!(OsError::XError(err))))
|
.map_err(|err| ExternalError::Os(os_error!(OsError::XError(err)))),
|
||||||
|
CursorGrabMode::Confined => {
|
||||||
|
let result = unsafe {
|
||||||
|
(self.xconn.xlib.XGrabPointer)(
|
||||||
|
self.xconn.display,
|
||||||
|
self.xwindow,
|
||||||
|
ffi::True,
|
||||||
|
(ffi::ButtonPressMask
|
||||||
|
| ffi::ButtonReleaseMask
|
||||||
|
| ffi::EnterWindowMask
|
||||||
|
| ffi::LeaveWindowMask
|
||||||
|
| ffi::PointerMotionMask
|
||||||
|
| ffi::PointerMotionHintMask
|
||||||
|
| ffi::Button1MotionMask
|
||||||
|
| ffi::Button2MotionMask
|
||||||
|
| ffi::Button3MotionMask
|
||||||
|
| ffi::Button4MotionMask
|
||||||
|
| ffi::Button5MotionMask
|
||||||
|
| ffi::ButtonMotionMask
|
||||||
|
| ffi::KeymapStateMask) as c_uint,
|
||||||
|
ffi::GrabModeAsync,
|
||||||
|
ffi::GrabModeAsync,
|
||||||
|
self.xwindow,
|
||||||
|
0,
|
||||||
|
ffi::CurrentTime,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
match result {
|
||||||
|
ffi::GrabSuccess => Ok(()),
|
||||||
|
ffi::AlreadyGrabbed => {
|
||||||
|
Err("Cursor could not be confined: already confined by another client")
|
||||||
|
}
|
||||||
|
ffi::GrabInvalidTime => Err("Cursor could not be confined: invalid time"),
|
||||||
|
ffi::GrabNotViewable => {
|
||||||
|
Err("Cursor could not be confined: confine location not viewable")
|
||||||
|
}
|
||||||
|
ffi::GrabFrozen => {
|
||||||
|
Err("Cursor could not be confined: frozen by another client")
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
.map_err(|err| ExternalError::Os(os_error!(OsError::XMisc(err))))
|
||||||
|
}
|
||||||
|
CursorGrabMode::Locked => {
|
||||||
|
return Err(ExternalError::NotSupported(NotSupportedError::new()));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if result.is_ok() {
|
if result.is_ok() {
|
||||||
*grabbed_lock = grab;
|
*grabbed_lock = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1386,14 +1397,14 @@ impl UnownedWindow {
|
||||||
|
|
||||||
// we can't use `set_cursor_grab(false)` here because it doesn't run `XUngrabPointer`
|
// we can't use `set_cursor_grab(false)` here because it doesn't run `XUngrabPointer`
|
||||||
// if the cursor isn't currently grabbed
|
// if the cursor isn't currently grabbed
|
||||||
let mut grabbed_lock = self.cursor_grabbed.lock();
|
let mut grabbed_lock = self.cursor_grabbed_mode.lock();
|
||||||
unsafe {
|
unsafe {
|
||||||
(self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime);
|
(self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime);
|
||||||
}
|
}
|
||||||
self.xconn
|
self.xconn
|
||||||
.flush_requests()
|
.flush_requests()
|
||||||
.map_err(|err| ExternalError::Os(os_error!(OsError::XError(err))))?;
|
.map_err(|err| ExternalError::Os(os_error!(OsError::XError(err))))?;
|
||||||
*grabbed_lock = false;
|
*grabbed_lock = CursorGrabMode::None;
|
||||||
|
|
||||||
// we keep the lock until we are done
|
// we keep the lock until we are done
|
||||||
self.xconn
|
self.xconn
|
||||||
|
|
|
@ -29,7 +29,8 @@ use crate::{
|
||||||
OsError,
|
OsError,
|
||||||
},
|
},
|
||||||
window::{
|
window::{
|
||||||
CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, WindowId as RootWindowId,
|
CursorGrabMode, CursorIcon, Fullscreen, UserAttentionType, WindowAttributes,
|
||||||
|
WindowId as RootWindowId,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use cocoa::{
|
use cocoa::{
|
||||||
|
@ -621,9 +622,17 @@ impl UnownedWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
|
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||||
|
let associate_mouse_cursor = match mode {
|
||||||
|
CursorGrabMode::Locked => false,
|
||||||
|
CursorGrabMode::None => true,
|
||||||
|
CursorGrabMode::Confined => {
|
||||||
|
return Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: Do this for real https://stackoverflow.com/a/40922095/5435443
|
// TODO: Do this for real https://stackoverflow.com/a/40922095/5435443
|
||||||
CGDisplay::associate_mouse_and_mouse_cursor_position(!grab)
|
CGDisplay::associate_mouse_and_mouse_cursor_position(associate_mouse_cursor)
|
||||||
.map_err(|status| ExternalError::Os(os_error!(OsError::CGError(status))))
|
.map_err(|status| ExternalError::Os(os_error!(OsError::CGError(status))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,8 +89,8 @@ impl Canvas {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), RootOE> {
|
pub fn set_cursor_lock(&self, lock: bool) -> Result<(), RootOE> {
|
||||||
if grab {
|
if lock {
|
||||||
self.raw().request_pointer_lock();
|
self.raw().request_pointer_lock();
|
||||||
} else {
|
} else {
|
||||||
let window = web_sys::window()
|
let window = web_sys::window()
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::event;
|
||||||
use crate::icon::Icon;
|
use crate::icon::Icon;
|
||||||
use crate::monitor::MonitorHandle as RootMH;
|
use crate::monitor::MonitorHandle as RootMH;
|
||||||
use crate::window::{
|
use crate::window::{
|
||||||
CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, WindowId as RootWI,
|
CursorGrabMode, CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, WindowId as RootWI,
|
||||||
};
|
};
|
||||||
|
|
||||||
use raw_window_handle::{RawWindowHandle, WebHandle};
|
use raw_window_handle::{RawWindowHandle, WebHandle};
|
||||||
|
@ -216,10 +216,18 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
|
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||||
|
let lock = match mode {
|
||||||
|
CursorGrabMode::None => false,
|
||||||
|
CursorGrabMode::Locked => true,
|
||||||
|
CursorGrabMode::Confined => {
|
||||||
|
return Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
self.canvas
|
self.canvas
|
||||||
.borrow()
|
.borrow()
|
||||||
.set_cursor_grab(grab)
|
.set_cursor_lock(lock)
|
||||||
.map_err(ExternalError::Os)
|
.map_err(ExternalError::Os)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ use crate::{
|
||||||
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
|
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
|
||||||
Parent, PlatformSpecificWindowBuilderAttributes, WindowId,
|
Parent, PlatformSpecificWindowBuilderAttributes, WindowId,
|
||||||
},
|
},
|
||||||
window::{CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes},
|
window::{CursorGrabMode, CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The Win32 implementation of the main `Window` object.
|
/// The Win32 implementation of the main `Window` object.
|
||||||
|
@ -276,7 +276,15 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
|
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||||
|
let confine = match mode {
|
||||||
|
CursorGrabMode::None => false,
|
||||||
|
CursorGrabMode::Confined => true,
|
||||||
|
CursorGrabMode::Locked => {
|
||||||
|
return Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let window = self.window.clone();
|
let window = self.window.clone();
|
||||||
let window_state = Arc::clone(&self.window_state);
|
let window_state = Arc::clone(&self.window_state);
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
|
@ -286,7 +294,7 @@ impl Window {
|
||||||
let result = window_state
|
let result = window_state
|
||||||
.lock()
|
.lock()
|
||||||
.mouse
|
.mouse
|
||||||
.set_cursor_flags(window.0, |f| f.set(CursorFlags::GRABBED, grab))
|
.set_cursor_flags(window.0, |f| f.set(CursorFlags::GRABBED, confine))
|
||||||
.map_err(|e| ExternalError::Os(os_error!(e)));
|
.map_err(|e| ExternalError::Os(os_error!(e)));
|
||||||
let _ = tx.send(result);
|
let _ = tx.send(result);
|
||||||
});
|
});
|
||||||
|
|
|
@ -906,18 +906,24 @@ impl Window {
|
||||||
self.window.set_cursor_position(position.into())
|
self.window.set_cursor_position(position.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Grabs the cursor, preventing it from leaving the window.
|
/// Set grabbing [mode]([`CursorGrabMode`]) on the cursor preventing it from leaving the window.
|
||||||
///
|
///
|
||||||
/// There's no guarantee that the cursor will be hidden. You should
|
/// # Example
|
||||||
/// hide it by yourself if you want so.
|
|
||||||
///
|
///
|
||||||
/// ## Platform-specific
|
/// First try confining the cursor, and if that fails, try locking it instead.
|
||||||
///
|
///
|
||||||
/// - **macOS:** This locks the cursor in a fixed location, which looks visually awkward.
|
/// ```no-run
|
||||||
/// - **iOS / Android:** Always returns an [`ExternalError::NotSupported`].
|
/// # use winit::event_loop::EventLoop;
|
||||||
|
/// # use winit::window::{CursorGrabMode, Window};
|
||||||
|
/// # let mut event_loop = EventLoop::new();
|
||||||
|
/// # let window = Window::new(&event_loop).unwrap();
|
||||||
|
/// window.set_cursor_grab(CursorGrabMode::Confined)
|
||||||
|
/// .or_else(|_e| window.set_cursor_grab(CursorGrabMode::Locked))
|
||||||
|
/// .unwrap();
|
||||||
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
|
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||||
self.window.set_cursor_grab(grab)
|
self.window.set_cursor_grab(mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Modifies the cursor's visibility.
|
/// Modifies the cursor's visibility.
|
||||||
|
@ -1028,6 +1034,38 @@ unsafe impl raw_window_handle::HasRawWindowHandle for Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The behavior of cursor grabbing.
|
||||||
|
///
|
||||||
|
/// Use this enum with [`Window::set_cursor_grab`] to grab the cursor.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub enum CursorGrabMode {
|
||||||
|
/// No grabbing of the cursor is performed.
|
||||||
|
None,
|
||||||
|
|
||||||
|
/// The cursor is confined to the window area.
|
||||||
|
///
|
||||||
|
/// There's no guarantee that the cursor will be hidden. You should hide it by yourself if you
|
||||||
|
/// want to do so.
|
||||||
|
///
|
||||||
|
/// ## Platform-specific
|
||||||
|
///
|
||||||
|
/// - **macOS:** Not implemented. Always returns [`ExternalError::NotSupported`] for now.
|
||||||
|
/// - ** iOS / Android / Web:** Always returns an [`ExternalError::NotSupported`].
|
||||||
|
Confined,
|
||||||
|
|
||||||
|
/// The cursor is locked inside the window area to the certain position.
|
||||||
|
///
|
||||||
|
/// There's no guarantee that the cursor will be hidden. You should hide it by yourself if you
|
||||||
|
/// want to do so.
|
||||||
|
///
|
||||||
|
/// ## Platform-specific
|
||||||
|
///
|
||||||
|
/// - **X11 / Windows:** Not implemented. Always returns [`ExternalError::NotSupported`] for now.
|
||||||
|
/// - ** iOS / Android:** Always returns an [`ExternalError::NotSupported`].
|
||||||
|
Locked,
|
||||||
|
}
|
||||||
|
|
||||||
/// Describes the appearance of the mouse cursor.
|
/// Describes the appearance of the mouse cursor.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
|
Loading…
Reference in a new issue