diff --git a/src/video/cocoa/SDL_cocoamouse.m b/src/video/cocoa/SDL_cocoamouse.m index 56f679632..12f506cc1 100644 --- a/src/video/cocoa/SDL_cocoamouse.m +++ b/src/video/cocoa/SDL_cocoamouse.m @@ -29,6 +29,14 @@ #include "../../events/SDL_mouse_c.h" +/* #define DEBUG_COCOAMOUSE */ + +#ifdef DEBUG_COCOAMOUSE +#define DLog(fmt, ...) printf("%s: " fmt "\n", __func__, ##__VA_ARGS__) +#else +#define DLog(...) do { } while (0) +#endif + @implementation NSCursor (InvisibleCursor) + (NSCursor *)invisibleCursor { @@ -203,6 +211,15 @@ Cocoa_ShowCursor(SDL_Cursor * cursor) static void Cocoa_WarpMouse(SDL_Window * window, int x, int y) { + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + if ([data->listener isMoving]) + { + DLog("Postponing warp, window being moved."); + [data->listener setPendingMoveX:x + Y:y]; + return; + } + SDL_Mouse *mouse = SDL_GetMouse(); CGPoint point = CGPointMake(x + (float)window->x, y + (float)window->y); @@ -214,6 +231,7 @@ Cocoa_WarpMouse(SDL_Window * window, int x, int y) 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); } /* According to the docs, this was deprecated in 10.6, but it's still @@ -238,8 +256,10 @@ Cocoa_SetRelativeMouseMode(SDL_bool enabled) CGError result; if (enabled) { + DLog("Turning on."); result = CGAssociateMouseAndMouseCursorPosition(NO); } else { + DLog("Turning off."); result = CGAssociateMouseAndMouseCursorPosition(YES); } if (result != kCGErrorSuccess) { @@ -272,18 +292,42 @@ Cocoa_HandleMouseEvent(_THIS, NSEvent *event) { SDL_Mouse *mouse = SDL_GetMouse(); - if (mouse->relative_mode && - ([event type] == NSMouseMoved || - [event type] == NSLeftMouseDragged || - [event type] == NSRightMouseDragged || - [event type] == NSOtherMouseDragged)) { - SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata; - float x = [event deltaX] + driverdata->deltaXOffset; - float y = [event deltaY] + driverdata->deltaYOffset; - driverdata->deltaXOffset = driverdata->deltaYOffset = 0; - - SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int)x, (int)y); + /* Non-relative movement is handled in -[Cocoa_WindowListener mouseMoved:] */ + if (!mouse->relative_mode) { + return; } + + switch ([event type]) + { + case NSMouseMoved: + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSOtherMouseDragged: + break; + + default: + /* Ignore any other events. */ + return; + } + + /* Ignore events that aren't inside the client area (i.e. title bar.) */ + if ([event window]) { + NSRect windowRect = [[[event window] contentView] frame]; + if (!NSPointInRect([event locationInWindow], windowRect)) { + return; + } + } + + SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata; + float x = [event deltaX] + driverdata->deltaXOffset; + float y = [event deltaY] + driverdata->deltaYOffset; + driverdata->deltaXOffset = driverdata->deltaYOffset = 0; + + if (driverdata->deltaYOffset > 0 || driverdata->deltaXOffset > 0) { + DLog("Relative move was (%g, %g), offset to (%g, %g)", [event deltaX], [event deltaY], x, y); + } + + SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int)x, (int)y); } void diff --git a/src/video/cocoa/SDL_cocoawindow.h b/src/video/cocoa/SDL_cocoawindow.h index 65e0d4b3d..4f88ce1f7 100644 --- a/src/video/cocoa/SDL_cocoawindow.h +++ b/src/video/cocoa/SDL_cocoawindow.h @@ -43,6 +43,8 @@ typedef enum BOOL isFullscreenSpace; BOOL inFullscreenTransition; PendingWindowOperation pendingWindowOperation; + BOOL isMoving; + int pendingWindowWarpX, pendingWindowWarpY; } -(void) listen:(SDL_WindowData *) data; @@ -54,6 +56,10 @@ typedef enum -(void) addPendingWindowOperation:(PendingWindowOperation) operation; -(void) close; +-(BOOL) isMoving; +-(void) setPendingMoveX:(int)x Y:(int)y; +-(void) windowDidFinishMoving; + /* Window delegate functionality */ -(BOOL) windowShouldClose:(id) sender; -(void) windowDidExpose:(NSNotification *) aNotification; @@ -105,6 +111,7 @@ struct SDL_WindowData NSWindow *nswindow; NSMutableArray *nscontexts; SDL_bool created; + SDL_bool inWindowMove; Cocoa_WindowListener *listener; struct SDL_VideoData *videodata; }; diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m index a773cb684..e391ea716 100644 --- a/src/video/cocoa/SDL_cocoawindow.m +++ b/src/video/cocoa/SDL_cocoawindow.m @@ -39,6 +39,44 @@ #include "SDL_cocoamouse.h" #include "SDL_cocoaopengl.h" +@interface SDLWindow : NSWindow +/* These are needed for borderless/fullscreen windows */ +- (BOOL)canBecomeKeyWindow; +- (BOOL)canBecomeMainWindow; +- (void)sendEvent:(NSEvent *)event; +@end + +@implementation SDLWindow +- (BOOL)canBecomeKeyWindow +{ + return YES; +} + +- (BOOL)canBecomeMainWindow +{ + return YES; +} + +- (void)sendEvent:(NSEvent *)event +{ + [super sendEvent:event]; + + if ([event type] != NSLeftMouseUp) { + return; + } + + id delegate = [self delegate]; + if (![delegate isKindOfClass:[Cocoa_WindowListener class]]) { + return; + } + + if ([delegate isMoving]) { + [delegate windowDidFinishMoving]; + } +} +@end + + static Uint32 s_moveHack; static void ConvertNSRect(NSRect *r) @@ -130,6 +168,7 @@ SetWindowStyle(SDL_Window * window, unsigned int style) isFullscreenSpace = NO; inFullscreenTransition = NO; pendingWindowOperation = PENDING_OPERATION_NONE; + isMoving = NO; center = [NSNotificationCenter defaultCenter]; @@ -315,6 +354,34 @@ SetWindowStyle(SDL_Window * window, unsigned int style) } } +- (BOOL)isMoving +{ + return isMoving; +} + +-(void) setPendingMoveX:(int)x Y:(int)y +{ + pendingWindowWarpX = x; + pendingWindowWarpY = y; +} + +- (void)windowDidFinishMoving +{ + if ([self isMoving]) + { + isMoving = NO; + + SDL_Mouse *mouse = SDL_GetMouse(); + if (pendingWindowWarpX >= 0 && pendingWindowWarpY >= 0) { + mouse->WarpMouse(_data->window, pendingWindowWarpX, pendingWindowWarpY); + pendingWindowWarpX = pendingWindowWarpY = -1; + } + if (mouse->relative_mode && SDL_GetMouseFocus() == _data->window) { + mouse->SetRelativeMouseMode(SDL_TRUE); + } + } +} + - (BOOL)windowShouldClose:(id)sender { SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_CLOSE, 0, 0); @@ -326,6 +393,14 @@ SetWindowStyle(SDL_Window * window, unsigned int style) SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0); } +- (void)windowWillMove:(NSNotification *)aNotification +{ + if ([_data->nswindow isKindOfClass:[SDLWindow class]]) { + pendingWindowWarpX = pendingWindowWarpY = -1; + isMoving = YES; + } +} + - (void)windowDidMove:(NSNotification *)aNotification { int x, y; @@ -407,6 +482,9 @@ SetWindowStyle(SDL_Window * window, unsigned int style) { SDL_Window *window = _data->window; SDL_Mouse *mouse = SDL_GetMouse(); + if (mouse->relative_mode && ![self isMoving]) { + mouse->SetRelativeMouseMode(SDL_TRUE); + } /* We're going to get keyboard events, since we're key. */ SDL_SetKeyboardFocus(window); @@ -431,6 +509,11 @@ SetWindowStyle(SDL_Window * window, unsigned int style) - (void)windowDidResignKey:(NSNotification *)aNotification { + SDL_Mouse *mouse = SDL_GetMouse(); + if (mouse->relative_mode) { + mouse->SetRelativeMouseMode(SDL_FALSE); + } + /* Some other window will get mouse events, since we're not key. */ if (SDL_GetMouseFocus() == _data->window) { SDL_SetMouseFocus(NULL); @@ -624,8 +707,6 @@ SetWindowStyle(SDL_Window * window, unsigned int style) if (x < 0 || x >= window->w || y < 0 || y >= window->h) { if (window->flags & SDL_WINDOW_INPUT_GRABBED) { - CGPoint cgpoint; - if (x < 0) { x = 0; } else if (x >= window->w) { @@ -638,6 +719,8 @@ SetWindowStyle(SDL_Window * window, unsigned int style) } #if !SDL_MAC_NO_SANDBOX + CGPoint cgpoint; + /* When SDL_MAC_NO_SANDBOX is set, this is handled by * SDL_cocoamousetap.m. */ @@ -754,24 +837,6 @@ SetWindowStyle(SDL_Window * window, unsigned int style) @end -@interface SDLWindow : NSWindow -/* These are needed for borderless/fullscreen windows */ -- (BOOL)canBecomeKeyWindow; -- (BOOL)canBecomeMainWindow; -@end - -@implementation SDLWindow -- (BOOL)canBecomeKeyWindow -{ - return YES; -} - -- (BOOL)canBecomeMainWindow -{ - return YES; -} -@end - @interface SDLView : NSView /* The default implementation doesn't pass rightMouseDown to responder chain */ @@ -1338,7 +1403,8 @@ void Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed) { /* Move the cursor to the nearest point in the window */ - if (grabbed) { + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + if (grabbed && data && ![data->listener isMoving]) { int x, y; CGPoint cgpoint;