diff --git a/CHANGELOG.md b/CHANGELOG.md index 173f17c5..1fad9364 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - On Windows, fix handling of surrogate pairs when dispatching `ReceivedCharacter`. - On macOS 10.15, fix freeze upon exiting exclusive fullscreen mode. - On iOS, fix null window on initial `HiDpiFactorChanged` event. +- On Windows, fix hovering the mouse over the active window creating an endless stream of CursorMoved events. # 0.20.0 Alpha 3 (2019-08-14) diff --git a/src/platform_impl/windows/util.rs b/src/platform_impl/windows/util.rs index 4b82cefa..96347849 100644 --- a/src/platform_impl/windows/util.rs +++ b/src/platform_impl/windows/util.rs @@ -141,6 +141,16 @@ pub fn set_cursor_hidden(hidden: bool) { } } +pub fn get_cursor_clip() -> Result { + unsafe { + let mut rect: RECT = mem::zeroed(); + win_to_err(|| winuser::GetClipCursor(&mut rect)).map(|_| rect) + } +} + +/// Sets the cursor's clip rect. +/// +/// Note that calling this will automatically dispatch a `WM_MOUSEMOVE` event. pub fn set_cursor_clip(rect: Option) -> Result<(), io::Error> { unsafe { let rect_ptr = rect @@ -151,6 +161,19 @@ pub fn set_cursor_clip(rect: Option) -> Result<(), io::Error> { } } +pub fn get_desktop_rect() -> RECT { + unsafe { + let left = winuser::GetSystemMetrics(winuser::SM_XVIRTUALSCREEN); + let top = winuser::GetSystemMetrics(winuser::SM_YVIRTUALSCREEN); + RECT { + left, + top, + right: left + winuser::GetSystemMetrics(winuser::SM_CXVIRTUALSCREEN), + bottom: top + winuser::GetSystemMetrics(winuser::SM_CYVIRTUALSCREEN), + } + } +} + pub fn is_focused(window: HWND) -> bool { window == unsafe { winuser::GetActiveWindow() } } diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 51bf45b8..105d71e6 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -307,10 +307,25 @@ impl CursorFlags { let client_rect = util::get_client_rect(window)?; if util::is_focused(window) { - if self.contains(CursorFlags::GRABBED) { - util::set_cursor_clip(Some(client_rect))?; - } else { - util::set_cursor_clip(None)?; + let cursor_clip = match self.contains(CursorFlags::GRABBED) { + true => Some(client_rect), + false => None, + }; + + let rect_to_tuple = |rect: RECT| (rect.left, rect.top, rect.right, rect.bottom); + let active_cursor_clip = rect_to_tuple(util::get_cursor_clip()?); + let desktop_rect = rect_to_tuple(util::get_desktop_rect()); + + let active_cursor_clip = match desktop_rect == active_cursor_clip { + true => None, + false => Some(active_cursor_clip), + }; + + // We do this check because calling `set_cursor_clip` incessantly will flood the event + // loop with `WM_MOUSEMOVE` events, and `refresh_os_cursor` is called by `set_cursor_flags` + // which at times gets called once every iteration of the eventloop. + if active_cursor_clip != cursor_clip.map(rect_to_tuple) { + util::set_cursor_clip(cursor_clip)?; } }