[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