From a224b3de0685434b4838b24b846e577d5357a1ac Mon Sep 17 00:00:00 2001 From: robtfm <50659922+robtfm@users.noreply.github.com> Date: Fri, 25 Apr 2025 12:41:56 +0200 Subject: [PATCH] windows: add locked cursor --- src/changelog/unreleased.md | 1 + src/platform_impl/windows/util.rs | 15 +++++++++++---- src/platform_impl/windows/window.rs | 13 ++++--------- src/platform_impl/windows/window_state.rs | 18 +++++++++++++++++- src/window.rs | 3 +-- 5 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 27d8a1447..5896142aa 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -43,6 +43,7 @@ changelog entry. ### Added - On Windows, add `IconExtWindows::from_resource_name`. +- On Windows, add `CursorGrabMode::Locked`. ### Fixed diff --git a/src/platform_impl/windows/util.rs b/src/platform_impl/windows/util.rs index 87bcbd425..52912c3c6 100644 --- a/src/platform_impl/windows/util.rs +++ b/src/platform_impl/windows/util.rs @@ -7,7 +7,7 @@ use std::{io, mem, ptr}; use crate::utils::Lazy; 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; @@ -17,9 +17,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_PEN_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 a8b375c9b..228c6f03e 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -428,14 +428,6 @@ impl Window { #[inline] 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; let window_state = Arc::clone(&self.window_state); let (tx, rx) = channel(); @@ -446,7 +438,10 @@ impl Window { .lock() .unwrap() .mouse - .set_cursor_flags(window, |f| f.set(CursorFlags::GRABBED, confine)) + .set_cursor_flags(window, |f| { + f.set(CursorFlags::GRABBED, mode != CursorGrabMode::None); + f.set(CursorFlags::LOCKED, mode == CursorGrabMode::Locked); + }) .map_err(|e| ExternalError::Os(os_error!(e))); let _ = tx.send(result); }); diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 911467b4c..8e24a4f25 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -77,6 +77,7 @@ bitflags! { const GRABBED = 1 << 0; const HIDDEN = 1 << 1; const IN_WINDOW = 1 << 2; + const LOCKED = 1 << 3; } } bitflags! { @@ -485,7 +486,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 fa861dc27..0db22bb32 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1707,8 +1707,7 @@ pub enum CursorGrabMode { /// /// ## Platform-specific /// - /// - **X11 / Windows:** Not implemented. Always returns [`ExternalError::NotSupported`] for - /// now. + /// - **X11:** Not implemented. Always returns [`ExternalError::NotSupported`] for now. /// - **iOS / Android:** Always returns an [`ExternalError::NotSupported`]. Locked, }