On Windows, fix CursorEntered/CursorLeft not sent during mouse grab
Fixes #3153.
This commit is contained in:
parent
c65e2247a1
commit
f5b4d6938f
2 changed files with 85 additions and 34 deletions
|
|
@ -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`.
|
- **Breaking:** Bump `ndk` version to `0.8.0`, ndk-sys to `0.5.0`, `android-activity` to `0.5.0`.
|
||||||
- Make `WindowBuilder` `Send + Sync`.
|
- Make `WindowBuilder` `Send + Sync`.
|
||||||
- On macOS, fix assertion when pressing `Globe` key.
|
- 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
|
# 0.29.1-beta
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,8 @@ use windows_sys::Win32::{
|
||||||
RAWINPUT, RIM_TYPEKEYBOARD, RIM_TYPEMOUSE,
|
RAWINPUT, RIM_TYPEKEYBOARD, RIM_TYPEMOUSE,
|
||||||
},
|
},
|
||||||
WindowsAndMessaging::{
|
WindowsAndMessaging::{
|
||||||
CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetCursorPos,
|
CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetClientRect,
|
||||||
GetMenu, GetMessageW, KillTimer, LoadCursorW, PeekMessageW, PostMessageW,
|
GetCursorPos, GetMenu, GetMessageW, KillTimer, LoadCursorW, PeekMessageW, PostMessageW,
|
||||||
RegisterClassExW, RegisterWindowMessageA, SetCursor, SetTimer, SetWindowPos,
|
RegisterClassExW, RegisterWindowMessageA, SetCursor, SetTimer, SetWindowPos,
|
||||||
TranslateMessage, CREATESTRUCTW, GIDC_ARRIVAL, GIDC_REMOVAL, GWL_STYLE, GWL_USERDATA,
|
TranslateMessage, CREATESTRUCTW, GIDC_ARRIVAL, GIDC_REMOVAL, GWL_STYLE, GWL_USERDATA,
|
||||||
HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG, NCCALCSIZE_PARAMS, PM_REMOVE, PT_PEN,
|
HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG, NCCALCSIZE_PARAMS, PM_REMOVE, PT_PEN,
|
||||||
|
|
@ -1438,48 +1438,63 @@ unsafe fn public_window_callback_inner<T: 'static>(
|
||||||
}
|
}
|
||||||
|
|
||||||
WM_MOUSEMOVE => {
|
WM_MOUSEMOVE => {
|
||||||
use crate::event::WindowEvent::{CursorEntered, CursorMoved};
|
use crate::event::WindowEvent::{CursorEntered, CursorLeft, CursorMoved};
|
||||||
let mouse_was_outside_window = {
|
|
||||||
let mut w = userdata.window_state_lock();
|
|
||||||
|
|
||||||
let was_outside_window = !w.mouse.cursor_flags().contains(CursorFlags::IN_WINDOW);
|
let x = super::get_x_lparam(lparam as u32) as i32;
|
||||||
w.mouse
|
let y = super::get_y_lparam(lparam as u32) as i32;
|
||||||
.set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, true))
|
let position = PhysicalPosition::new(x as f64, y as f64);
|
||||||
.ok();
|
|
||||||
was_outside_window
|
|
||||||
};
|
|
||||||
|
|
||||||
if mouse_was_outside_window {
|
|
||||||
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::<TRACKMOUSEEVENT>() as u32,
|
|
||||||
dwFlags: TME_LEAVE,
|
|
||||||
hwndTrack: window,
|
|
||||||
dwHoverTime: HOVER_DEFAULT,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
let cursor_moved;
|
||||||
{
|
{
|
||||||
|
let mut w = userdata.window_state_lock();
|
||||||
|
let mouse_was_inside_window =
|
||||||
|
w.mouse.cursor_flags().contains(CursorFlags::IN_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();
|
||||||
|
|
||||||
|
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::<TRACKMOUSEEVENT>() 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 => (),
|
||||||
|
}
|
||||||
|
|
||||||
// handle spurious WM_MOUSEMOVE messages
|
// handle spurious WM_MOUSEMOVE messages
|
||||||
// see https://devblogs.microsoft.com/oldnewthing/20031001-00/?p=42343
|
// see https://devblogs.microsoft.com/oldnewthing/20031001-00/?p=42343
|
||||||
// and http://debugandconquer.blogspot.com/2015/08/the-cause-of-spurious-mouse-move.html
|
// 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);
|
cursor_moved = w.mouse.last_position != Some(position);
|
||||||
w.mouse.last_position = Some(position);
|
w.mouse.last_position = Some(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
if cursor_moved {
|
if cursor_moved {
|
||||||
update_modifiers(window, userdata);
|
update_modifiers(window, userdata);
|
||||||
|
|
||||||
|
|
@ -2562,3 +2577,38 @@ unsafe fn handle_raw_input<T: 'static>(userdata: &ThreadMsgTargetData<T>, 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue