Skip to content

Commit

Permalink
On Windows, fix CursorEntered/CursorLeft not sent during mouse grab
Browse files Browse the repository at this point in the history
Fixes #3153.
  • Loading branch information
YouKnow-sys committed Oct 17, 2023
1 parent c65e224 commit f5b4d69
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 33 deletions.
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
}
}

0 comments on commit f5b4d69

Please sign in to comment.