[Bf-blender-cvs] [9fd6dae7939] blender-v3.4-release: Fix T102346: Mouse escapes window during walk navigation

Campbell Barton noreply at git.blender.org
Sat Nov 19 11:12:04 CET 2022


Commit: 9fd6dae7939a65b67045749a0eadeb6864ded183
Author: Campbell Barton
Date:   Sat Nov 19 20:57:59 2022 +1100
Branches: blender-v3.4-release
https://developer.blender.org/rB9fd6dae7939a65b67045749a0eadeb6864ded183

Fix T102346: Mouse escapes window during walk navigation

Regression in [0] which exposed a problem with GHOST_kGrabHide on Win32
and to some extent X11.

Prior to [0], walk mode used it's own warping logic (hiding the cursor
& recording the motion between events). Using GHOST's grabbing makes
sense in this case as it's not very convenient for operators to
implement their own cursor warping, however doing so exposed a problem
where the mouse cursor could leave the window.
This would happen because the cursor needed to be within 2px of the
screen edge before warping.

Resolve by warping within a small region in the middle of the window.

Note that warping to the window center on each motion would be ideal
but is more involved as the logic for Win32 & X11 doesn't work properly
when every motion warps, so this needs further investigation to support.

This problem doesn't apply to GHOST/Cocoa which warps every motion event
on the spot and GHOST/Wayland doesn't set the mouse position at all to
implement this functionality.

[0]: 4c4e8cc926a672ac60692b3fb8c20249f9cae679

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

M	intern/ghost/intern/GHOST_SystemWin32.cpp
M	intern/ghost/intern/GHOST_SystemX11.cpp

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

diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index 54c892d296e..829177793ef 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -1055,16 +1055,45 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
     int32_t x_new = x_screen;
     int32_t y_new = y_screen;
     int32_t x_accum, y_accum;
-    GHOST_Rect bounds;
 
-    /* Fallback to window bounds. */
-    if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) {
-      window->getClientBounds(bounds);
-    }
+    /* Warp within bounds. */
+    {
+      GHOST_Rect bounds;
+      int32_t bounds_margin = 0;
+      GHOST_TAxisFlag bounds_axis = GHOST_kAxisNone;
+
+      if (window->getCursorGrabMode() == GHOST_kGrabHide) {
+        window->getClientBounds(bounds);
+
+        /* WARNING(@campbellbarton): The current warping logic fails to warp on every event,
+         * so the box needs to small enough not to let the cursor escape the window but large
+         * enough that the cursor isn't being warped every time.
+         * If this was not the case it would be less trouble to simply warp the cursor to the
+         * center of the screen on every motion, see: T102346. */
+        const int32_t subregion_div = 4; /* One quarter of the region. */
+        const int32_t size[2] = {bounds.getWidth(), bounds.getHeight()};
+        const int center[2] = {(bounds.m_l + bounds.m_r) / 2, (bounds.m_t + bounds.m_b) / 2};
+        /* Shrink the box to prevent the cursor escaping. */
+        bounds.m_l = center[0] - (size[0] / (subregion_div * 2));
+        bounds.m_r = center[0] + (size[0] / (subregion_div * 2));
+        bounds.m_t = center[1] - (size[1] / (subregion_div * 2));
+        bounds.m_b = center[1] + (size[1] / (subregion_div * 2));
+        bounds_margin = 0;
+        bounds_axis = GHOST_TAxisFlag(GHOST_kAxisX | GHOST_kAxisY);
+      }
+      else {
+        /* Fallback to window bounds. */
+        if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) {
+          window->getClientBounds(bounds);
+        }
+        bounds_margin = 2;
+        bounds_axis = window->getCursorGrabAxis();
+      }
 
-    /* Could also clamp to screen bounds wrap with a window outside the view will
-     * fail at the moment. Use inset in case the window is at screen bounds. */
-    bounds.wrapPoint(x_new, y_new, 2, window->getCursorGrabAxis());
+      /* Could also clamp to screen bounds wrap with a window outside the view will
+       * fail at the moment. Use inset in case the window is at screen bounds. */
+      bounds.wrapPoint(x_new, y_new, bounds_margin, bounds_axis);
+    }
 
     window->getCursorGrabAccum(x_accum, y_accum);
     if (x_new != x_screen || y_new != y_screen) {
diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp
index 5c89febe97c..9e424015d0c 100644
--- a/intern/ghost/intern/GHOST_SystemX11.cpp
+++ b/intern/ghost/intern/GHOST_SystemX11.cpp
@@ -934,16 +934,44 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
         int32_t x_new = xme.x_root;
         int32_t y_new = xme.y_root;
         int32_t x_accum, y_accum;
-        GHOST_Rect bounds;
 
-        /* fallback to window bounds */
-        if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) {
-          window->getClientBounds(bounds);
-        }
+        /* Warp within bounds. */
+        {
+          GHOST_Rect bounds;
+          int32_t bounds_margin = 0;
+          GHOST_TAxisFlag bounds_axis = GHOST_kAxisNone;
+
+          if (window->getCursorGrabMode() == GHOST_kGrabHide) {
+            window->getClientBounds(bounds);
+
+            /* TODO(@campbellbarton): warp the cursor to `window->getCursorGrabInitPos`,
+             * on every motion event, see: T102346. */
+            const int32_t subregion_div = 4; /* One quarter of the region. */
+            const int32_t size[2] = {bounds.getWidth(), bounds.getHeight()};
+            const int center[2] = {(bounds.m_l + bounds.m_r) / 2, (bounds.m_t + bounds.m_b) / 2};
+            /* Shrink the box to prevent the cursor escaping. */
+            bounds.m_l = center[0] - (size[0] / (subregion_div * 2));
+            bounds.m_r = center[0] + (size[0] / (subregion_div * 2));
+            bounds.m_t = center[1] - (size[1] / (subregion_div * 2));
+            bounds.m_b = center[1] + (size[1] / (subregion_div * 2));
+            bounds_margin = 0;
+            bounds_axis = GHOST_TAxisFlag(GHOST_kAxisX | GHOST_kAxisY);
+          }
+          else {
+            /* Fallback to window bounds. */
+            if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) {
+              window->getClientBounds(bounds);
+            }
+            /* Could also clamp to screen bounds wrap with a window outside the view will
+             * fail at the moment. Use offset of 8 in case the window is at screen bounds. */
+            bounds_margin = 8;
+            bounds_axis = window->getCursorGrabAxis();
+          }
 
-        /* Could also clamp to screen bounds wrap with a window outside the view will
-         * fail at the moment. Use offset of 8 in case the window is at screen bounds. */
-        bounds.wrapPoint(x_new, y_new, 8, window->getCursorGrabAxis());
+          /* Could also clamp to screen bounds wrap with a window outside the view will
+           * fail at the moment. Use inset in case the window is at screen bounds. */
+          bounds.wrapPoint(x_new, y_new, bounds_margin, bounds_axis);
+        }
 
         window->getCursorGrabAccum(x_accum, y_accum);



More information about the Bf-blender-cvs mailing list