diff options
-rw-r--r-- | intern/ghost/intern/GHOST_SystemWin32.cpp | 50 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_SystemWin32.h | 4 |
2 files changed, 37 insertions, 17 deletions
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 1f85c7a4c5c..e0974373caf 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -1045,11 +1045,27 @@ void GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *window) GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); GHOST_TabletData td = window->getTabletData(); + /* Window's mouse history function returns a history of up to 64 mouse moves, including those + * processed during previous mouse move events. This means we must track and filter out events we + * have already processed. + * + * The API accepts and returns 32 bit points, but error or fails when passed a negative number + * where the HIWORD is not zeroed, and will return a negative numbers with HIWORD zeroed. + * Therefore we must zero the HIWORD of points passed in and sign extend from the LOWORD of + * points received. Note: negative position values occur for multi-monitor systems whose primary + * display is not the top-leftmost display. + * + * Querying the mouse history with valid mouse positions sometimes fails, commonly at the screen + * edge. In these cases we fall back to using information provided by the event instead of the + * mouse event history. + * + * Further explanation - https://devblogs.microsoft.com/oldnewthing/20120314-00/?p=8103 */ + DWORD msgPos = ::GetMessagePos(); LONG msgTime = ::GetMessageTime(); - /* GetMessagePointsEx processes points as 16 bit integers and can fail or return erroneous values - * if negative input is not truncated. */ + /* GetMessagePointsEx processes 32 bit points as 16 bit integers and can fail or return erroneous + * values if negative input's HIWORD is not zeroed. */ int msgPosX = GET_X_LPARAM(msgPos) & 0x0000FFFF; int msgPosY = GET_Y_LPARAM(msgPos) & 0x0000FFFF; @@ -1078,10 +1094,10 @@ void GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *window) break; } - /* GetMouseMovePointsEx returns 16 bit number as 32 bit. If negative, we need to sign extend. - */ - points[i].x = points[i].x > 32767 ? points[i].x | 0xFFFF0000 : points[i].x; - points[i].y = points[i].y > 32767 ? points[i].y | 0xFFFF0000 : points[i].y; + /* GetMouseMovePointsEx returns 16 bit number as 32 bit with HIWORD zeroed. If the signed + * LOWORD is negative, we need to sign extend. */ + points[i].x = points[i].x & 0x8000 ? points[i].x | 0xFFFF0000 : points[i].x; + points[i].y = points[i].y & 0x8000 ? points[i].y | 0xFFFF0000 : points[i].y; if (points[i].time == system->m_mouseTimestamp && points[i].x == system->m_mousePosX && points[i].y == system->m_mousePosY) { @@ -1097,7 +1113,14 @@ void GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *window) td)); } - DWORD lastTimestamp = points[0].time; + /* Update last processed mouse position and time, checking for time overflow. If not overflown + * and the this event's time is less than the last, this mousemove event occurred before a cursor + * wrap so we should not update last mouse position and time. */ + if (points[0].time >= system->m_mouseTimestamp || ::GetTickCount() < system->m_mouseTimestamp) { + system->m_mousePosX = points[0].x; + system->m_mousePosY = points[0].y; + system->m_mouseTimestamp = points[0].time; + } /* Check if we need to wrap the cursor. */ if (window->getCursorGrabModeIsWarp() && !window->m_tabletInRange) { @@ -1124,18 +1147,11 @@ void GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *window) system->setCursorPosition(x_wrap, y_wrap); window->setCursorGrabAccum(x_accum + (x_current - x_wrap), y_accum + (y_current - y_wrap)); - /* First message after SendInput wrap is invalid for unknown reasons, skip events until one - * tick after SendInput event time. */ - lastTimestamp = ::GetTickCount() + 1; + /* Update the mouse timestamp so that mouse moves prior to wrap are skipped. This is a lower + * bound, the timestamp for the generated mouse move may be different. */ + system->m_mouseTimestamp = ::GetTickCount(); } } - - system->m_mousePosX = points[0].x; - system->m_mousePosY = points[0].y; - /* Use latest time, checking for overflow. */ - if (lastTimestamp > system->m_mouseTimestamp || ::GetTickCount() < system->m_mouseTimestamp) { - system->m_mouseTimestamp = lastTimestamp; - } } void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam) diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h index 86c443bb1c7..0d55188b325 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.h +++ b/intern/ghost/intern/GHOST_SystemWin32.h @@ -454,6 +454,10 @@ class GHOST_SystemWin32 : public GHOST_System { /** Wheel delta accumulator. */ int m_wheelDeltaAccum; + /** + * Last mouse position and time values, used when inspecting mouse history to know what has + * already been processed. + */ /** Last mouse x position. */ int m_mousePosX; /** Last mouse y position. */ |