Mac: Redo cursor warp handling.
authorJørgen P. Tjernø <jorgen@valvesoftware.com>
Wed, 26 Feb 2014 11:35:02 -0800
changeset 8261841b66e4397a
parent 8260 028ed8da2524
child 8262 acc0dcf38b3d
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
src/video/cocoa/SDL_cocoamouse.m
src/video/cocoa/SDL_cocoawindow.m
     1.1 --- a/src/video/cocoa/SDL_cocoamouse.h	Tue Feb 25 17:27:41 2014 -0800
     1.2 +++ b/src/video/cocoa/SDL_cocoamouse.h	Wed Feb 26 11:35:02 2014 -0800
     1.3 @@ -28,11 +28,18 @@
     1.4  extern void Cocoa_InitMouse(_THIS);
     1.5  extern void Cocoa_HandleMouseEvent(_THIS, NSEvent * event);
     1.6  extern void Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent * event);
     1.7 +extern void Cocoa_HandleMouseWarp(CGFloat x, CGFloat y);
     1.8  extern void Cocoa_QuitMouse(_THIS);
     1.9  
    1.10  typedef struct {
    1.11 -    int deltaXOffset;
    1.12 -    int deltaYOffset;
    1.13 +    /* Wether we've seen a cursor warp since the last move event. */
    1.14 +    SDL_bool seenWarp;
    1.15 +    /* What location our last cursor warp was to. */
    1.16 +    CGFloat lastWarpX;
    1.17 +    CGFloat lastWarpY;
    1.18 +    /* What location we last saw the cursor move to. */
    1.19 +    CGFloat lastMoveX;
    1.20 +    CGFloat lastMoveY;
    1.21      void *tapdata;
    1.22  } SDL_MouseData;
    1.23  
     2.1 --- a/src/video/cocoa/SDL_cocoamouse.m	Tue Feb 25 17:27:41 2014 -0800
     2.2 +++ b/src/video/cocoa/SDL_cocoamouse.m	Wed Feb 26 11:35:02 2014 -0800
     2.3 @@ -223,16 +223,7 @@
     2.4      SDL_Mouse *mouse = SDL_GetMouse();
     2.5      CGPoint point = CGPointMake(x + (float)window->x, y + (float)window->y);
     2.6  
     2.7 -    {
     2.8 -        /* This makes Cocoa_HandleMouseEvent ignore this delta in the next
     2.9 -         * movement event.
    2.10 -         */
    2.11 -        SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
    2.12 -        NSPoint location =  [NSEvent mouseLocation];
    2.13 -        driverdata->deltaXOffset = location.x - point.x;
    2.14 -        driverdata->deltaYOffset = point.y - location.y;
    2.15 -        DLog("Warp to (%g, %g), offsetting next movement event by (%i, %i)", point.x, point.y, driverdata->deltaXOffset, driverdata->deltaYOffset);
    2.16 -    }
    2.17 +    Cocoa_HandleMouseWarp(point.x, point.y);
    2.18  
    2.19      /* According to the docs, this was deprecated in 10.6, but it's still
    2.20       * around. The substitute requires a CGEventSource, but I'm not entirely
    2.21 @@ -285,18 +276,16 @@
    2.22      SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor());
    2.23  
    2.24      Cocoa_InitMouseEventTap(mouse->driverdata);
    2.25 +
    2.26 +    SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
    2.27 +    const NSPoint location =  [NSEvent mouseLocation];
    2.28 +    driverdata->lastMoveX = location.x;
    2.29 +    driverdata->lastMoveY = location.y;
    2.30  }
    2.31  
    2.32  void
    2.33  Cocoa_HandleMouseEvent(_THIS, NSEvent *event)
    2.34  {
    2.35 -    SDL_Mouse *mouse = SDL_GetMouse();
    2.36 -
    2.37 -    /* Non-relative movement is handled in -[Cocoa_WindowListener mouseMoved:] */
    2.38 -    if (!mouse->relative_mode) {
    2.39 -        return;
    2.40 -    }
    2.41 -
    2.42      switch ([event type])
    2.43      {
    2.44          case NSMouseMoved:
    2.45 @@ -310,6 +299,24 @@
    2.46              return;
    2.47      }
    2.48  
    2.49 +    SDL_Mouse *mouse = SDL_GetMouse();
    2.50 +
    2.51 +    SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
    2.52 +    const SDL_bool seenWarp = driverdata->seenWarp;
    2.53 +    driverdata->seenWarp = NO;
    2.54 +
    2.55 +    const NSPoint location =  [NSEvent mouseLocation];
    2.56 +    const CGFloat lastMoveX = driverdata->lastMoveX;
    2.57 +    const CGFloat lastMoveY = driverdata->lastMoveY;
    2.58 +    driverdata->lastMoveX = location.x;
    2.59 +    driverdata->lastMoveY = location.y;
    2.60 +    DLog("Last seen mouse: (%g, %g)", location.x, location.y);
    2.61 +
    2.62 +    /* Non-relative movement is handled in -[Cocoa_WindowListener mouseMoved:] */
    2.63 +    if (!mouse->relative_mode) {
    2.64 +        return;
    2.65 +    }
    2.66 +
    2.67      /* Ignore events that aren't inside the client area (i.e. title bar.) */
    2.68      if ([event window]) {
    2.69          NSRect windowRect = [[[event window] contentView] frame];
    2.70 @@ -318,16 +325,18 @@
    2.71          }
    2.72      }
    2.73  
    2.74 -    SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
    2.75 -    float x = [event deltaX] + driverdata->deltaXOffset;
    2.76 -    float y = [event deltaY] + driverdata->deltaYOffset;
    2.77 -    driverdata->deltaXOffset = driverdata->deltaYOffset = 0;
    2.78 +    float deltaX = [event deltaX];
    2.79 +    float deltaY = [event deltaY];
    2.80  
    2.81 -    if (driverdata->deltaYOffset > 0 || driverdata->deltaXOffset > 0) {
    2.82 -        DLog("Relative move was (%g, %g), offset to (%g, %g)", [event deltaX], [event deltaY], x, y);
    2.83 +    if (seenWarp)
    2.84 +    {
    2.85 +        deltaX += (lastMoveX - driverdata->lastWarpX);
    2.86 +        deltaY += ((CGDisplayPixelsHigh(kCGDirectMainDisplay) - lastMoveY) - driverdata->lastWarpY);
    2.87 +
    2.88 +        DLog("Motion was (%g, %g), offset to (%g, %g)", [event deltaX], [event deltaY], deltaX, deltaY);
    2.89      }
    2.90  
    2.91 -    SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int)x, (int)y);
    2.92 +    SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int)deltaX, (int)deltaY);
    2.93  }
    2.94  
    2.95  void
    2.96 @@ -352,6 +361,20 @@
    2.97  }
    2.98  
    2.99  void
   2.100 +Cocoa_HandleMouseWarp(CGFloat x, CGFloat y)
   2.101 +{
   2.102 +    /* This makes Cocoa_HandleMouseEvent ignore the delta caused by the warp,
   2.103 +     * since it gets included in the next movement event.
   2.104 +     */
   2.105 +    SDL_MouseData *driverdata = (SDL_MouseData*)SDL_GetMouse()->driverdata;
   2.106 +    driverdata->lastWarpX = x;
   2.107 +    driverdata->lastWarpY = y;
   2.108 +    driverdata->seenWarp = SDL_TRUE;
   2.109 +
   2.110 +    DLog("(%g, %g)", x, y);
   2.111 +}
   2.112 +
   2.113 +void
   2.114  Cocoa_QuitMouse(_THIS)
   2.115  {
   2.116      SDL_Mouse *mouse = SDL_GetMouse();
     3.1 --- a/src/video/cocoa/SDL_cocoawindow.m	Tue Feb 25 17:27:41 2014 -0800
     3.2 +++ b/src/video/cocoa/SDL_cocoawindow.m	Wed Feb 26 11:35:02 2014 -0800
     3.3 @@ -39,6 +39,15 @@
     3.4  #include "SDL_cocoamouse.h"
     3.5  #include "SDL_cocoaopengl.h"
     3.6  
     3.7 +/* #define DEBUG_COCOAWINDOW */
     3.8 +
     3.9 +#ifdef DEBUG_COCOAWINDOW
    3.10 +#define DLog(fmt, ...) printf("%s: " fmt "\n", __func__, ##__VA_ARGS__)
    3.11 +#else
    3.12 +#define DLog(...) do { } while (0)
    3.13 +#endif
    3.14 +
    3.15 +
    3.16  @interface SDLWindow : NSWindow
    3.17  /* These are needed for borderless/fullscreen windows */
    3.18  - (BOOL)canBecomeKeyWindow;
    3.19 @@ -735,6 +744,8 @@
    3.20              CGSetLocalEventsSuppressionInterval(0.0);
    3.21              CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
    3.22              CGSetLocalEventsSuppressionInterval(0.25);
    3.23 +
    3.24 +            Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
    3.25  #endif
    3.26          }
    3.27      }
    3.28 @@ -1411,6 +1422,10 @@
    3.29          SDL_GetMouseState(&x, &y);
    3.30          cgpoint.x = window->x + x;
    3.31          cgpoint.y = window->y + y;
    3.32 +
    3.33 +        Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
    3.34 +
    3.35 +        DLog("Returning cursor to (%g, %g)", cgpoint.x, cgpoint.y);
    3.36          CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
    3.37      }
    3.38