Commit: a3a9459050a96e75138b3441c069898f211f179c
Author: Germano Cavalcante
Date:   Wed Dec 14 15:37:49 2022 -0300
Branches: master
https://developer.blender.org/rBa3a9459050a96e75138b3441c069898f211f179c

Fix erratic mouse wrapping movement on Windows (2)

This is a solution in response to the issues mentioned in comments on
rBe4f1d719080a and T103088.

Apparently the workaround of checking if the mouse is already inside
the area on the next event doesn't work for some tablets.

Perhaps the order of events or some very small jitter is causing this
issue on tablets. (Couldn't confirm).

Whatever the cause, the solution of checking the timestamp of the event
and thus ignoring the outdated ones is theoretically safer.

It is the same solution seen in MacOS.

Also calling `SendInput` 3 times every warp ensures that at least one
event is dispatched.

===================================================================

M       intern/ghost/intern/GHOST_SystemWin32.cpp

===================================================================

diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp 
b/intern/ghost/intern/GHOST_SystemWin32.cpp
index 75a4cc8389a..8cb007a756a 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -1061,11 +1061,16 @@ GHOST_EventCursor 
*GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
 
   int32_t x_screen = screen_co[0], y_screen = screen_co[1];
   if (window->getCursorGrabModeIsWarp()) {
-    /* WORKAROUND:
-     * Sometimes Windows ignores `SetCursorPos()` or `SendInput()` calls or 
the mouse event is
-     * outdated. Identify these cases by checking if the cursor is not yet 
within bounds. */
-    static bool is_warping_x = false;
-    static bool is_warping_y = false;
+    static uint64_t last_warp_time = 0;
+    {
+      /* WORKAROUND: Check the mouse event timestamp so we can ignore 
mousemove events that were
+       * already in the queue before we changed the cursor position. */
+      MOUSEMOVEPOINT mp = {x_screen, y_screen};
+      ::GetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &mp, &mp, 1, 
GMMP_USE_DISPLAY_POINTS);
+      if (mp.time <= last_warp_time) {
+        return NULL;
+      }
+    }
 
     int32_t x_new = x_screen;
     int32_t y_new = y_screen;
@@ -1112,31 +1117,35 @@ GHOST_EventCursor 
*GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
 
     window->getCursorGrabAccum(x_accum, y_accum);
     if (x_new != x_screen || y_new != y_screen) {
-      system->setCursorPosition(x_new, y_new); /* wrap */
-
-      /* Do not update the accum values if we are an outdated or failed 
pos-warp event. */
-      if (!is_warping_x) {
-        is_warping_x = x_new != x_screen;
-        if (is_warping_x) {
-          x_accum += (x_screen - x_new);
-        }
-      }
-
-      if (!is_warping_y) {
-        is_warping_y = y_new != y_screen;
-        if (is_warping_y) {
-          y_accum += (y_screen - y_new);
-        }
-      }
+      /* WORKAROUND: Store the current time so that we ignore outdated 
mousemove events. */
+      last_warp_time = ::GetTickCount64();
+
+      /* For more control over which timestamp to store in the event, we use 
`SendInput` instead of
+       * `SetCursorPos` here.
+       * It is quite unlikely to happen, but still possible that some event 
between
+       * `last_warp_time` and `GHOST_SystemWin32::setCursorPosition` is sent. 
*/
+      INPUT input[3] = {0};
+      input[0].type = INPUT_MOUSE;
+      input[0].mi.dx = (LONG)(x_new * (65535.0f / 
GetSystemMetrics(SM_CXSCREEN)));
+      input[0].mi.dy = (LONG)(y_new * (65535.0f / 
GetSystemMetrics(SM_CYSCREEN)));
+      input[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
+      input[0].mi.time = last_warp_time;
+
+      /* Send 3 events with a jitter to make sure Windows does not 
occasionally and
+       * inexplicably ignore `SetCursorPos` or `SendInput`. */
+      input[2] = input[1] = input[0];
+      input[1].mi.dx += 1;
+      ::SendInput(3, input, sizeof(INPUT));
+
+      x_accum += (x_screen - x_new);
+      y_accum += (y_screen - y_new);
       window->setCursorGrabAccum(x_accum, y_accum);
 
-      /* When wrapping we don't need to add an event because the 
setCursorPosition call will cause
-       * a new event after. */
+      /* When wrapping we don't need to add an event because the `SendInput` 
call will cause new
+       * events after. */
       return NULL;
     }
 
-    is_warping_x = false;
-    is_warping_y = false;
     x_screen += x_accum;
     y_screen += y_accum;
   }

_______________________________________________
Bf-blender-cvs mailing list
Bf-blender-cvs@blender.org
List details, subscription details or unsubscribe:
https://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to