Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed CursorEntered and CursorLeft events not getting sent when holding a mouse button down #3154

Merged
merged 8 commits into from
Oct 17, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
116 changes: 83 additions & 33 deletions src/platform_impl/windows/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 = {
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::<TRACKMOUSEEVENT>() 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::<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 => (),
}

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

Expand Down Expand Up @@ -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