diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 2970b768..65e303da 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -78,6 +78,7 @@ changelog entry. - Add `CustomCursorSource::Url`, `CustomCursorSource::from_animation`. - Implement `CustomIconProvider` for `RgbaIcon`. - Add `icon` module that exposes winit's icon API. +- On Windows, add `CursorGrabMode::Locked`. ### Changed diff --git a/src/platform_impl/windows/util.rs b/src/platform_impl/windows/util.rs index 5aee1d54..7f41b3ca 100644 --- a/src/platform_impl/windows/util.rs +++ b/src/platform_impl/windows/util.rs @@ -6,7 +6,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::{io, mem, ptr}; use windows_sys::core::{HRESULT, PCWSTR}; -use windows_sys::Win32::Foundation::{BOOL, HANDLE, HMODULE, HWND, RECT}; +use windows_sys::Win32::Foundation::{BOOL, HANDLE, HMODULE, HWND, POINT, RECT}; use windows_sys::Win32::Graphics::Gdi::{ClientToScreen, HMONITOR}; use windows_sys::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA}; use windows_sys::Win32::System::SystemServices::IMAGE_DOS_HEADER; @@ -16,9 +16,9 @@ use windows_sys::Win32::UI::HiDpi::{ use windows_sys::Win32::UI::Input::KeyboardAndMouse::GetActiveWindow; use windows_sys::Win32::UI::Input::Pointer::{POINTER_INFO, POINTER_TOUCH_INFO}; use windows_sys::Win32::UI::WindowsAndMessaging::{ - ClipCursor, GetClientRect, GetClipCursor, GetSystemMetrics, GetWindowPlacement, GetWindowRect, - IsIconic, ShowCursor, IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP, IDC_IBEAM, - IDC_NO, IDC_SIZEALL, IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE, IDC_SIZEWE, IDC_WAIT, + ClipCursor, GetClientRect, GetClipCursor, GetCursorPos, GetSystemMetrics, GetWindowPlacement, + GetWindowRect, IsIconic, ShowCursor, IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP, + IDC_IBEAM, IDC_NO, IDC_SIZEALL, IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE, IDC_SIZEWE, IDC_WAIT, SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN, SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN, SW_MAXIMIZE, WINDOWPLACEMENT, }; @@ -99,6 +99,13 @@ pub fn set_cursor_hidden(hidden: bool) { } } +pub fn get_cursor_position() -> Result { + unsafe { + let mut point: POINT = mem::zeroed(); + win_to_err(GetCursorPos(&mut point)).map(|_| point) + } +} + pub fn get_cursor_clip() -> Result { unsafe { let mut rect: RECT = mem::zeroed(); diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 6374b996..894cb605 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -52,7 +52,7 @@ use super::icon::WinCursor; use super::MonitorHandle; use crate::cursor::Cursor; use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size}; -use crate::error::{NotSupportedError, RequestError}; +use crate::error::RequestError; use crate::icon::{Icon, RgbaIcon}; use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle, MonitorHandleProvider}; use crate::platform::windows::{BackdropType, Color, CornerPreference, WinIcon}; @@ -652,14 +652,6 @@ impl CoreWindow for Window { } fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), RequestError> { - let confine = match mode { - CursorGrabMode::None => false, - CursorGrabMode::Confined => true, - CursorGrabMode::Locked => { - return Err(NotSupportedError::new("locked cursor is not supported").into()) - }, - }; - let window = self.window; let window_state = Arc::clone(&self.window_state); let (tx, rx) = channel(); @@ -670,7 +662,10 @@ impl CoreWindow for Window { .lock() .unwrap() .mouse - .set_cursor_flags(window.hwnd(), |f| f.set(CursorFlags::GRABBED, confine)) + .set_cursor_flags(window.hwnd(), |f| { + f.set(CursorFlags::GRABBED, mode != CursorGrabMode::None); + f.set(CursorFlags::LOCKED, mode == CursorGrabMode::Locked); + }) .map_err(|err| os_error!(err).into()); let _ = tx.send(result); }); diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index f1b8e022..f2536baf 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -87,6 +87,7 @@ bitflags! { const GRABBED = 1 << 0; const HIDDEN = 1 << 1; const IN_WINDOW = 1 << 2; + const LOCKED = 1 << 3; } } bitflags! { @@ -495,7 +496,22 @@ impl CursorFlags { if util::is_focused(window) { let cursor_clip = match self.contains(CursorFlags::GRABBED) { true => { - if self.contains(CursorFlags::HIDDEN) { + if self.contains(CursorFlags::LOCKED) { + if let Ok(pos) = util::get_cursor_position() { + Some(RECT { + left: pos.x, + right: pos.x + 1, + top: pos.y, + bottom: pos.y + 1, + }) + } else { + // If lock is applied while the cursor is not available, lock it to the + // middle of the window. + let cx = (client_rect.left + client_rect.right) / 2; + let cy = (client_rect.top + client_rect.bottom) / 2; + Some(RECT { left: cx, right: cx + 1, top: cy, bottom: cy + 1 }) + } + } else if self.contains(CursorFlags::HIDDEN) { // Confine the cursor to the center of the window if the cursor is hidden. // This avoids problems with the cursor activating // the taskbar if the window borders or overlaps that. diff --git a/src/window.rs b/src/window.rs index 51907828..dde0167f 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1396,8 +1396,7 @@ pub enum CursorGrabMode { /// /// ## Platform-specific /// - /// - **X11 / Windows:** Not implemented. Always returns [`RequestError::NotSupported`] for - /// now. + /// - **X11:** Not implemented. Always returns [`RequestError::NotSupported`] for now. /// - **iOS / Android:** Always returns an [`RequestError::NotSupported`]. Locked, }