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
|
|
@ -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<T: 'static>(
|
|||
}
|
||||
|
||||
WM_MOUSEMOVE => {
|
||||
use crate::event::WindowEvent::{CursorEntered, CursorMoved};
|
||||
let mouse_was_outside_window = {
|
||||
let mut w = userdata.window_state_lock();
|
||||
use crate::event::WindowEvent::{CursorEntered, CursorLeft, CursorMoved};
|
||||
|
||||
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
|
||||
};
|
||||
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);
|
||||
|
||||
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 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
|
||||
// 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<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