From f5b4d6938ff76ff3632bec4823f77a9f5f695c77 Mon Sep 17 00:00:00 2001 From: YouKnow Date: Tue, 17 Oct 2023 06:53:22 +0330 Subject: [PATCH] On Windows, fix `CursorEntered`/`CursorLeft` not sent during mouse grab Fixes #3153. --- CHANGELOG.md | 1 + src/platform_impl/windows/event_loop.rs | 116 +++++++++++++++++------- 2 files changed, 84 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de34e4a947..6b864f2cb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ And please only add new entries to the top of this list, right below the `# Unre - **Breaking:** Bump `ndk` version to `0.8.0`, ndk-sys to `0.5.0`, `android-activity` to `0.5.0`. - Make `WindowBuilder` `Send + Sync`. - On macOS, fix assertion when pressing `Globe` key. +- On Windows, updated `WM_MOUSEMOVE` to detect when cursor Enter or Leave window client area while captured and send the corresponding events. (#3153) # 0.29.1-beta diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 0abd9b1798..43b162afae 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -49,8 +49,8 @@ use windows_sys::Win32::{ RAWINPUT, RIM_TYPEKEYBOARD, RIM_TYPEMOUSE, }, WindowsAndMessaging::{ - CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetCursorPos, - GetMenu, GetMessageW, KillTimer, LoadCursorW, PeekMessageW, PostMessageW, + CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetClientRect, + GetCursorPos, GetMenu, GetMessageW, KillTimer, LoadCursorW, PeekMessageW, PostMessageW, RegisterClassExW, RegisterWindowMessageA, SetCursor, SetTimer, SetWindowPos, TranslateMessage, CREATESTRUCTW, GIDC_ARRIVAL, GIDC_REMOVAL, GWL_STYLE, GWL_USERDATA, HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG, NCCALCSIZE_PARAMS, PM_REMOVE, PT_PEN, @@ -1438,48 +1438,63 @@ unsafe fn public_window_callback_inner( } WM_MOUSEMOVE => { - use crate::event::WindowEvent::{CursorEntered, CursorMoved}; - let mouse_was_outside_window = { + use crate::event::WindowEvent::{CursorEntered, CursorLeft, CursorMoved}; + + let x = super::get_x_lparam(lparam as u32) as i32; + let y = super::get_y_lparam(lparam as u32) as i32; + let position = PhysicalPosition::new(x as f64, y as f64); + + let cursor_moved; + { let mut w = userdata.window_state_lock(); + let mouse_was_inside_window = + w.mouse.cursor_flags().contains(CursorFlags::IN_WINDOW); - let was_outside_window = !w.mouse.cursor_flags().contains(CursorFlags::IN_WINDOW); - w.mouse - .set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, true)) - .ok(); - was_outside_window - }; + match get_pointer_move_kind(window, mouse_was_inside_window, x, y) { + PointerMoveKind::Enter => { + w.mouse + .set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, true)) + .ok(); - if mouse_was_outside_window { - userdata.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: CursorEntered { - device_id: DEVICE_ID, - }, - }); + userdata.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: CursorEntered { + device_id: DEVICE_ID, + }, + }); - // Calling TrackMouseEvent in order to receive mouse leave events. - unsafe { - TrackMouseEvent(&mut TRACKMOUSEEVENT { - cbSize: mem::size_of::() as u32, - dwFlags: TME_LEAVE, - hwndTrack: window, - dwHoverTime: HOVER_DEFAULT, - }) - }; - } + // Calling TrackMouseEvent in order to receive mouse leave events. + unsafe { + TrackMouseEvent(&mut TRACKMOUSEEVENT { + cbSize: mem::size_of::() as u32, + dwFlags: TME_LEAVE, + hwndTrack: window, + dwHoverTime: HOVER_DEFAULT, + }) + }; + } + PointerMoveKind::Leave => { + w.mouse + .set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, false)) + .ok(); + + userdata.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: CursorLeft { + device_id: DEVICE_ID, + }, + }); + } + PointerMoveKind::None => (), + } - let x = super::get_x_lparam(lparam as u32) as f64; - let y = super::get_y_lparam(lparam as u32) as f64; - let position = PhysicalPosition::new(x, y); - let cursor_moved; - { // handle spurious WM_MOUSEMOVE messages // see https://devblogs.microsoft.com/oldnewthing/20031001-00/?p=42343 // and http://debugandconquer.blogspot.com/2015/08/the-cause-of-spurious-mouse-move.html - let mut w = userdata.window_state_lock(); cursor_moved = w.mouse.last_position != Some(position); w.mouse.last_position = Some(position); } + if cursor_moved { update_modifiers(window, userdata); @@ -2562,3 +2577,38 @@ unsafe fn handle_raw_input(userdata: &ThreadMsgTargetData, data: }); } } + +enum PointerMoveKind { + /// Pointer enterd to the window. + Enter, + /// Pointer leaved the window client area. + Leave, + /// Pointer is inside the window or `GetClientRect` failed. + None, +} + +fn get_pointer_move_kind( + window: HWND, + mouse_was_inside_window: bool, + x: i32, + y: i32, +) -> PointerMoveKind { + let rect: RECT = unsafe { + let mut rect: RECT = mem::zeroed(); + if GetClientRect(window, &mut rect) == false.into() { + return PointerMoveKind::None; // exit early if GetClientRect failed + } + rect + }; + + let x = (rect.left..rect.right).contains(&x); + let y = (rect.top..rect.bottom).contains(&y); + + if !mouse_was_inside_window && x && y { + PointerMoveKind::Enter + } else if mouse_was_inside_window && !(x && y) { + PointerMoveKind::Leave + } else { + PointerMoveKind::None + } +}