Mac: Redo cursor warp handling.

This fixes bugs related to getting unnaturally large xrel/yrel for
SDL_MOUSEMOTION after warps and enabling / disabling relative mode.

Bug: https://bugzilla.libsdl.org/show_bug.cgi?id=1836
This commit is contained in:
J?rgen P. Tjern? 2014-02-26 11:35:02 -08:00
parent 52a63e823f
commit 4850d25988
3 changed files with 71 additions and 26 deletions

View File

@ -28,11 +28,18 @@
extern void Cocoa_InitMouse(_THIS);
extern void Cocoa_HandleMouseEvent(_THIS, NSEvent * event);
extern void Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent * event);
extern void Cocoa_HandleMouseWarp(CGFloat x, CGFloat y);
extern void Cocoa_QuitMouse(_THIS);
typedef struct {
int deltaXOffset;
int deltaYOffset;
/* Wether we've seen a cursor warp since the last move event. */
SDL_bool seenWarp;
/* What location our last cursor warp was to. */
CGFloat lastWarpX;
CGFloat lastWarpY;
/* What location we last saw the cursor move to. */
CGFloat lastMoveX;
CGFloat lastMoveY;
void *tapdata;
} SDL_MouseData;

View File

@ -223,16 +223,7 @@ Cocoa_WarpMouse(SDL_Window * window, int x, int y)
SDL_Mouse *mouse = SDL_GetMouse();
CGPoint point = CGPointMake(x + (float)window->x, y + (float)window->y);
{
/* This makes Cocoa_HandleMouseEvent ignore this delta in the next
* movement event.
*/
SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
NSPoint location = [NSEvent mouseLocation];
driverdata->deltaXOffset = location.x - point.x;
driverdata->deltaYOffset = point.y - location.y;
DLog("Warp to (%g, %g), offsetting next movement event by (%i, %i)", point.x, point.y, driverdata->deltaXOffset, driverdata->deltaYOffset);
}
Cocoa_HandleMouseWarp(point.x, point.y);
/* According to the docs, this was deprecated in 10.6, but it's still
* around. The substitute requires a CGEventSource, but I'm not entirely
@ -285,18 +276,16 @@ Cocoa_InitMouse(_THIS)
SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor());
Cocoa_InitMouseEventTap(mouse->driverdata);
SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
const NSPoint location = [NSEvent mouseLocation];
driverdata->lastMoveX = location.x;
driverdata->lastMoveY = location.y;
}
void
Cocoa_HandleMouseEvent(_THIS, NSEvent *event)
{
SDL_Mouse *mouse = SDL_GetMouse();
/* Non-relative movement is handled in -[Cocoa_WindowListener mouseMoved:] */
if (!mouse->relative_mode) {
return;
}
switch ([event type])
{
case NSMouseMoved:
@ -310,6 +299,24 @@ Cocoa_HandleMouseEvent(_THIS, NSEvent *event)
return;
}
SDL_Mouse *mouse = SDL_GetMouse();
SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
const SDL_bool seenWarp = driverdata->seenWarp;
driverdata->seenWarp = NO;
const NSPoint location = [NSEvent mouseLocation];
const CGFloat lastMoveX = driverdata->lastMoveX;
const CGFloat lastMoveY = driverdata->lastMoveY;
driverdata->lastMoveX = location.x;
driverdata->lastMoveY = location.y;
DLog("Last seen mouse: (%g, %g)", location.x, location.y);
/* Non-relative movement is handled in -[Cocoa_WindowListener mouseMoved:] */
if (!mouse->relative_mode) {
return;
}
/* Ignore events that aren't inside the client area (i.e. title bar.) */
if ([event window]) {
NSRect windowRect = [[[event window] contentView] frame];
@ -318,16 +325,18 @@ Cocoa_HandleMouseEvent(_THIS, NSEvent *event)
}
}
SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
float x = [event deltaX] + driverdata->deltaXOffset;
float y = [event deltaY] + driverdata->deltaYOffset;
driverdata->deltaXOffset = driverdata->deltaYOffset = 0;
float deltaX = [event deltaX];
float deltaY = [event deltaY];
if (driverdata->deltaYOffset > 0 || driverdata->deltaXOffset > 0) {
DLog("Relative move was (%g, %g), offset to (%g, %g)", [event deltaX], [event deltaY], x, y);
if (seenWarp)
{
deltaX += (lastMoveX - driverdata->lastWarpX);
deltaY += ((CGDisplayPixelsHigh(kCGDirectMainDisplay) - lastMoveY) - driverdata->lastWarpY);
DLog("Motion was (%g, %g), offset to (%g, %g)", [event deltaX], [event deltaY], deltaX, deltaY);
}
SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int)x, (int)y);
SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int)deltaX, (int)deltaY);
}
void
@ -351,6 +360,20 @@ Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event)
SDL_SendMouseWheel(window, mouse->mouseID, (int)x, (int)y);
}
void
Cocoa_HandleMouseWarp(CGFloat x, CGFloat y)
{
/* This makes Cocoa_HandleMouseEvent ignore the delta caused by the warp,
* since it gets included in the next movement event.
*/
SDL_MouseData *driverdata = (SDL_MouseData*)SDL_GetMouse()->driverdata;
driverdata->lastWarpX = x;
driverdata->lastWarpY = y;
driverdata->seenWarp = SDL_TRUE;
DLog("(%g, %g)", x, y);
}
void
Cocoa_QuitMouse(_THIS)
{

View File

@ -39,6 +39,15 @@
#include "SDL_cocoamouse.h"
#include "SDL_cocoaopengl.h"
/* #define DEBUG_COCOAWINDOW */
#ifdef DEBUG_COCOAWINDOW
#define DLog(fmt, ...) printf("%s: " fmt "\n", __func__, ##__VA_ARGS__)
#else
#define DLog(...) do { } while (0)
#endif
@interface SDLWindow : NSWindow
/* These are needed for borderless/fullscreen windows */
- (BOOL)canBecomeKeyWindow;
@ -735,6 +744,8 @@ SetWindowStyle(SDL_Window * window, unsigned int style)
CGSetLocalEventsSuppressionInterval(0.0);
CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
CGSetLocalEventsSuppressionInterval(0.25);
Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
#endif
}
}
@ -1411,6 +1422,10 @@ Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
SDL_GetMouseState(&x, &y);
cgpoint.x = window->x + x;
cgpoint.y = window->y + y;
Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
DLog("Returning cursor to (%g, %g)", cgpoint.x, cgpoint.y);
CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
}