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
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 35 additions and 17 deletions

View file

@ -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

View file

@ -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<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> {
unsafe {
let mut rect: RECT = mem::zeroed();

View file

@ -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);
});

View file

@ -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.

View file

@ -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,
}