From 4850d25988018401884c79f88b336d5d89ad6dc3 Mon Sep 17 00:00:00 2001 From: "J?rgen P. Tjern?" Date: Wed, 26 Feb 2014 11:35:02 -0800 Subject: [PATCH] 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 --- src/video/cocoa/SDL_cocoamouse.h | 11 ++++- src/video/cocoa/SDL_cocoamouse.m | 71 ++++++++++++++++++++----------- src/video/cocoa/SDL_cocoawindow.m | 15 +++++++ 3 files changed, 71 insertions(+), 26 deletions(-) diff --git a/src/video/cocoa/SDL_cocoamouse.h b/src/video/cocoa/SDL_cocoamouse.h index a0738eb98..336b84044 100644 --- a/src/video/cocoa/SDL_cocoamouse.h +++ b/src/video/cocoa/SDL_cocoamouse.h @@ -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; diff --git a/src/video/cocoa/SDL_cocoamouse.m b/src/video/cocoa/SDL_cocoamouse.m index 12f506cc1..71b782caa 100644 --- a/src/video/cocoa/SDL_cocoamouse.m +++ b/src/video/cocoa/SDL_cocoamouse.m @@ -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) { diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m index e391ea716..c1c0caac9 100644 --- a/src/video/cocoa/SDL_cocoawindow.m +++ b/src/video/cocoa/SDL_cocoawindow.m @@ -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); }