windows: add locked cursor

This commit is contained in:
robtfm
2025-04-25 12:41:56 +02:00
committed by GitHub
parent 6461cfa9b1
commit ab96fa8395
5 changed files with 35 additions and 17 deletions

View File

@@ -78,6 +78,7 @@ changelog entry.
- Add `CustomCursorSource::Url`, `CustomCursorSource::from_animation`. - Add `CustomCursorSource::Url`, `CustomCursorSource::from_animation`.
- Implement `CustomIconProvider` for `RgbaIcon`. - Implement `CustomIconProvider` for `RgbaIcon`.
- Add `icon` module that exposes winit's icon API. - Add `icon` module that exposes winit's icon API.
- On Windows, add `CursorGrabMode::Locked`.
### Changed ### Changed

View File

@@ -6,7 +6,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::{io, mem, ptr}; use std::{io, mem, ptr};
use windows_sys::core::{HRESULT, PCWSTR}; 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::Graphics::Gdi::{ClientToScreen, HMONITOR};
use windows_sys::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA}; use windows_sys::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA};
use windows_sys::Win32::System::SystemServices::IMAGE_DOS_HEADER; 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::KeyboardAndMouse::GetActiveWindow;
use windows_sys::Win32::UI::Input::Pointer::{POINTER_INFO, POINTER_TOUCH_INFO}; use windows_sys::Win32::UI::Input::Pointer::{POINTER_INFO, POINTER_TOUCH_INFO};
use windows_sys::Win32::UI::WindowsAndMessaging::{ use windows_sys::Win32::UI::WindowsAndMessaging::{
ClipCursor, GetClientRect, GetClipCursor, GetSystemMetrics, GetWindowPlacement, GetWindowRect, ClipCursor, GetClientRect, GetClipCursor, GetCursorPos, GetSystemMetrics, GetWindowPlacement,
IsIconic, ShowCursor, IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP, IDC_IBEAM, GetWindowRect, IsIconic, ShowCursor, IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP,
IDC_NO, IDC_SIZEALL, IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE, IDC_SIZEWE, IDC_WAIT, 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, SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN, SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN, SW_MAXIMIZE,
WINDOWPLACEMENT, WINDOWPLACEMENT,
}; };
@@ -99,6 +99,13 @@ pub fn set_cursor_hidden(hidden: bool) {
} }
} }
pub fn get_cursor_position() -> Result<POINT, io::Error> {
unsafe {
let mut point: POINT = mem::zeroed();
win_to_err(GetCursorPos(&mut point)).map(|_| point)
}
}
pub fn get_cursor_clip() -> Result<RECT, io::Error> { pub fn get_cursor_clip() -> Result<RECT, io::Error> {
unsafe { unsafe {
let mut rect: RECT = mem::zeroed(); let mut rect: RECT = mem::zeroed();

View File

@@ -52,7 +52,7 @@ use super::icon::WinCursor;
use super::MonitorHandle; use super::MonitorHandle;
use crate::cursor::Cursor; use crate::cursor::Cursor;
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size}; use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{NotSupportedError, RequestError}; use crate::error::RequestError;
use crate::icon::{Icon, RgbaIcon}; use crate::icon::{Icon, RgbaIcon};
use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle, MonitorHandleProvider}; use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle, MonitorHandleProvider};
use crate::platform::windows::{BackdropType, Color, CornerPreference, WinIcon}; 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> { 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 = self.window;
let window_state = Arc::clone(&self.window_state); let window_state = Arc::clone(&self.window_state);
let (tx, rx) = channel(); let (tx, rx) = channel();
@@ -670,7 +662,10 @@ impl CoreWindow for Window {
.lock() .lock()
.unwrap() .unwrap()
.mouse .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()); .map_err(|err| os_error!(err).into());
let _ = tx.send(result); let _ = tx.send(result);
}); });

View File

@@ -87,6 +87,7 @@ bitflags! {
const GRABBED = 1 << 0; const GRABBED = 1 << 0;
const HIDDEN = 1 << 1; const HIDDEN = 1 << 1;
const IN_WINDOW = 1 << 2; const IN_WINDOW = 1 << 2;
const LOCKED = 1 << 3;
} }
} }
bitflags! { bitflags! {
@@ -495,7 +496,22 @@ impl CursorFlags {
if util::is_focused(window) { if util::is_focused(window) {
let cursor_clip = match self.contains(CursorFlags::GRABBED) { let cursor_clip = match self.contains(CursorFlags::GRABBED) {
true => { 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. // Confine the cursor to the center of the window if the cursor is hidden.
// This avoids problems with the cursor activating // This avoids problems with the cursor activating
// the taskbar if the window borders or overlaps that. // the taskbar if the window borders or overlaps that.

View File

@@ -1396,8 +1396,7 @@ pub enum CursorGrabMode {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **X11 / Windows:** Not implemented. Always returns [`RequestError::NotSupported`] for /// - **X11:** Not implemented. Always returns [`RequestError::NotSupported`] for now.
/// now.
/// - **iOS / Android:** Always returns an [`RequestError::NotSupported`]. /// - **iOS / Android:** Always returns an [`RequestError::NotSupported`].
Locked, Locked,
} }