Fix T49498: continuous grab issues on macOS, particularly with gaming mouses.

Change the implementation so it no longer takes over the mouse cursor motion
from the OS, instead only move it when warping, similar to Windows and X11.
Probably the reason it was not done this way originally is that you then get
a 500ms delay after warping, but we can use a trick to avoid that and get much
smoother mouse motion than before.
This commit is contained in:
Brecht Van Lommel 2017-07-18 16:00:07 +02:00
parent 3cfb248bb6
commit 8b2785bda5
Notes: blender-bot 2023-02-14 11:29:52 +01:00
Referenced by issue #49498, Continuous Grab not working properly on macOS (Sierra)
3 changed files with 23 additions and 34 deletions

View File

@ -292,11 +292,6 @@ protected:
/** Ignores window size messages (when window is dragged). */
bool m_ignoreWindowSizedMessages;
/** Stores the mouse cursor delta due to setting a new cursor position
* Needed because cocoa event delta cursor move takes setCursorPosition changes too.
*/
GHOST_TInt32 m_cursorDelta_x, m_cursorDelta_y;
/** Temporarily ignore momentum scroll events */
bool m_ignoreMomentumScroll;
/** Is the scroll wheel event generated by a multitouch trackpad or mouse? */

View File

@ -366,8 +366,6 @@ GHOST_SystemCocoa::GHOST_SystemCocoa()
char *rstring = NULL;
m_modifierMask =0;
m_cursorDelta_x=0;
m_cursorDelta_y=0;
m_outsideLoopEventProcessed = false;
m_needDelayedApplicationBecomeActiveEventProcessing = false;
m_displayManager = new GHOST_DisplayManagerCocoa ();
@ -644,6 +642,13 @@ GHOST_TSuccess GHOST_SystemCocoa::setMouseCursorPosition(GHOST_TInt32 x, GHOST_T
CGDisplayMoveCursorToPoint((CGDirectDisplayID)[[[windowScreen deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue], CGPointMake(xf, yf));
// See https://stackoverflow.com/a/17559012. By default, hardware events
// will be suppressed for 500ms after a synthetic mouse event. For unknown
// reasons CGEventSourceSetLocalEventsSuppressionInterval does not work,
// however calling CGAssociateMouseAndMouseCursorPosition also removes the
// delay, even if this is undocumented.
CGAssociateMouseAndMouseCursorPosition(true);
[pool drain];
return GHOST_kSuccess;
}
@ -1354,9 +1359,8 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
case GHOST_kGrabWrap: //Wrap cursor at area/window boundaries
{
NSPoint mousePos = [cocoawindow mouseLocationOutsideOfEventStream];
GHOST_TInt32 x_mouse= mousePos.x;
GHOST_TInt32 y_mouse= mousePos.y;
GHOST_TInt32 x_accum, y_accum, x_cur, y_cur, x, y;
GHOST_TInt32 x_mouse = mousePos.x;
GHOST_TInt32 y_mouse = mousePos.y;
GHOST_Rect bounds, windowBounds, correctedBounds;
/* fallback to window bounds */
@ -1370,29 +1374,26 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
correctedBounds.m_b = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_b;
correctedBounds.m_t = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_t;
//Update accumulation counts
//Get accumulation from previous mouse warps
GHOST_TInt32 x_accum, y_accum;
window->getCursorGrabAccum(x_accum, y_accum);
x_accum += [event deltaX]-m_cursorDelta_x;
y_accum += -[event deltaY]-m_cursorDelta_y; //Strange Apple implementation (inverted coordinates for the deltaY) ...
window->setCursorGrabAccum(x_accum, y_accum);
//Warp mouse cursor if needed
x_mouse += [event deltaX]-m_cursorDelta_x;
y_mouse += -[event deltaY]-m_cursorDelta_y;
correctedBounds.wrapPoint(x_mouse, y_mouse, 2);
//Compensate for mouse moved event taking cursor position set into account
m_cursorDelta_x = x_mouse-mousePos.x;
m_cursorDelta_y = y_mouse-mousePos.y;
GHOST_TInt32 warped_x_mouse = x_mouse;
GHOST_TInt32 warped_y_mouse = y_mouse;
correctedBounds.wrapPoint(warped_x_mouse, warped_y_mouse, 4);
//Set new cursor position
window->clientToScreenIntern(x_mouse, y_mouse, x_cur, y_cur);
setMouseCursorPosition(x_cur, y_cur); /* wrap */
if (x_mouse != warped_x_mouse || y_mouse != warped_y_mouse) {
GHOST_TInt32 warped_x, warped_y;
window->clientToScreenIntern(warped_x_mouse, warped_y_mouse, warped_x, warped_y);
setMouseCursorPosition(warped_x, warped_y); /* wrap */
window->setCursorGrabAccum(x_accum + (x_mouse - warped_x_mouse), y_accum + (y_mouse - warped_y_mouse));
}
//Post event
window->getCursorGrabInitPos(x_cur, y_cur);
window->screenToClientIntern(x_cur, y_cur, x_cur, y_cur);
window->clientToScreenIntern(x_cur + x_accum, y_cur + y_accum, x, y);
//Generate event
GHOST_TInt32 x, y;
window->clientToScreenIntern(x_mouse + x_accum, y_mouse + y_accum, x, y);
pushEvent(new GHOST_EventCursor([event timestamp] * 1000, GHOST_kEventCursorMove, window, x, y));
break;
}
@ -1404,9 +1405,6 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
window->clientToScreenIntern(mousePos.x, mousePos.y, x, y);
pushEvent(new GHOST_EventCursor([event timestamp] * 1000, GHOST_kEventCursorMove, window, x, y));
m_cursorDelta_x=0;
m_cursorDelta_y=0; //Mouse motion occurred between two cursor warps, so we can reset the delta counter
break;
}
}

View File

@ -1370,9 +1370,6 @@ GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorGrab(GHOST_TGrabCursorMode mode
//Make window key if it wasn't to get the mouse move events
[m_window makeKeyWindow];
//Dissociate cursor position even for warp mode, to allow mouse acceleration to work even when warping the cursor
err = CGAssociateMouseAndMouseCursorPosition(false) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
[pool drain];
}
}
@ -1382,7 +1379,6 @@ GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorGrab(GHOST_TGrabCursorMode mode
setWindowCursorVisibility(true);
}
err = CGAssociateMouseAndMouseCursorPosition(true) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
/* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
setCursorGrabAccum(0, 0);
m_cursorGrabBounds.m_l= m_cursorGrabBounds.m_r= -1; /* disable */