Skip to content

Commit

Permalink
Mac: Redo cursor warp handling.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jorgenpt committed Feb 26, 2014
1 parent 52a63e8 commit 4850d25
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 26 deletions.
11 changes: 9 additions & 2 deletions src/video/cocoa/SDL_cocoamouse.h
Expand Up @@ -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;

Expand Down
71 changes: 47 additions & 24 deletions src/video/cocoa/SDL_cocoamouse.m
Expand Up @@ -223,16 +223,7 @@ + (NSCursor *)invisibleCursor
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
Expand Down Expand Up @@ -285,18 +276,16 @@ + (NSCursor *)invisibleCursor
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:
Expand All @@ -310,6 +299,24 @@ + (NSCursor *)invisibleCursor
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];
Expand All @@ -318,16 +325,18 @@ + (NSCursor *)invisibleCursor
}
}

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
Expand All @@ -351,6 +360,20 @@ + (NSCursor *)invisibleCursor
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)
{
Expand Down
15 changes: 15 additions & 0 deletions src/video/cocoa/SDL_cocoawindow.m
Expand Up @@ -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;
Expand Down Expand Up @@ -735,6 +744,8 @@ - (void)mouseMoved:(NSEvent *)theEvent
CGSetLocalEventsSuppressionInterval(0.0);
CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
CGSetLocalEventsSuppressionInterval(0.25);

Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
#endif
}
}
Expand Down Expand Up @@ -1411,6 +1422,10 @@ - (void)resetCursorRects
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);
}

Expand Down

0 comments on commit 4850d25

Please sign in to comment.