Commit 4850d25988018401884c79f88b336d5d89ad6dc3

J?rgen P. Tjern? 2014-02-26T11:35:02

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

diff --git a/src/video/cocoa/SDL_cocoamouse.h b/src/video/cocoa/SDL_cocoamouse.h
index a0738eb..336b840 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 12f506c..71b782c 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
@@ -352,6 +361,20 @@ Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event)
 }
 
 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)
 {
     SDL_Mouse *mouse = SDL_GetMouse();
diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m
index e391ea7..c1c0caa 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);
     }