Mac: Use cursor rects instead of NSCursor hide/unhide.
authorJørgen P. Tjernø <jorgen@valvesoftware.com>
Tue, 07 May 2013 16:52:39 -0700
changeset 7158ff52fba70795
parent 7157 a082c2eeb247
child 7159 ffc613268eb1
Mac: Use cursor rects instead of NSCursor hide/unhide.

This should hopefully fix a class of problems around cursor hiding not behaving correctly on Mac.

http://bugzilla.libsdl.org/show_bug.cgi?id=1824
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	Mon May 06 23:02:37 2013 +0200
     1.2 +++ b/src/video/cocoa/SDL_cocoamouse.h	Tue May 07 16:52:39 2013 -0700
     1.3 @@ -35,6 +35,10 @@
     1.4      int deltaYOffset;
     1.5  } SDL_MouseData;
     1.6  
     1.7 +@interface NSCursor (InvisibleCursor)
     1.8 ++ (NSCursor *)invisibleCursor;
     1.9 +@end
    1.10 +
    1.11  #endif /* _SDL_cocoamouse_h */
    1.12  
    1.13  /* vi: set ts=4 sw=4 expandtab: */
     2.1 --- a/src/video/cocoa/SDL_cocoamouse.m	Mon May 06 23:02:37 2013 +0200
     2.2 +++ b/src/video/cocoa/SDL_cocoamouse.m	Tue May 07 16:52:39 2013 -0700
     2.3 @@ -28,6 +28,32 @@
     2.4  
     2.5  #include "../../events/SDL_mouse_c.h"
     2.6  
     2.7 +@implementation NSCursor (InvisibleCursor)
     2.8 ++ (NSCursor *)invisibleCursor
     2.9 +{
    2.10 +    static NSCursor *invisibleCursor = NULL;
    2.11 +    if (!invisibleCursor) {
    2.12 +        /* RAW 16x16 transparent GIF */
    2.13 +        static unsigned char cursorBytes[] = {
    2.14 +            0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x10, 0x00, 0x10, 0x00, 0x80,
    2.15 +            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04,
    2.16 +            0x01, 0x00, 0x00, 0x01, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x10,
    2.17 +            0x00, 0x10, 0x00, 0x00, 0x02, 0x0E, 0x8C, 0x8F, 0xA9, 0xCB, 0xED,
    2.18 +            0x0F, 0xA3, 0x9C, 0xB4, 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B
    2.19 +        };
    2.20 +
    2.21 +        NSData *cursorData = [NSData dataWithBytesNoCopy:&cursorBytes[0]
    2.22 +                                                  length:sizeof(cursorBytes)
    2.23 +                                            freeWhenDone:NO];
    2.24 +        NSImage *cursorImage = [[[NSImage alloc] initWithData:cursorData] autorelease];
    2.25 +        invisibleCursor = [[NSCursor alloc] initWithImage:cursorImage
    2.26 +                                                  hotSpot:NSZeroPoint];
    2.27 +    }
    2.28 +
    2.29 +    return invisibleCursor;
    2.30 +}
    2.31 +@end
    2.32 +
    2.33  
    2.34  static SDL_Cursor *
    2.35  Cocoa_CreateDefaultCursor()
    2.36 @@ -153,30 +179,17 @@
    2.37  static int
    2.38  Cocoa_ShowCursor(SDL_Cursor * cursor)
    2.39  {
    2.40 -	/* We need to track the previous state because hide and unhide calls need to
    2.41 -	 * be matched, but ShowCursor calls don't.
    2.42 -	 */
    2.43 -	static SDL_bool isShown = SDL_TRUE;
    2.44      NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    2.45  
    2.46 -    if (cursor) {
    2.47 -        NSCursor *nscursor = (NSCursor *)cursor->driverdata;
    2.48 -
    2.49 -        /* We're possibly executing from an event handler where this operation
    2.50 -         * is unsupported. This will execute it in the main Cocoa event loop
    2.51 -         * after this returns.
    2.52 -         */
    2.53 -        [nscursor performSelectorOnMainThread:@selector(set)
    2.54 -                                   withObject:nil
    2.55 -                                waitUntilDone:NO];
    2.56 -
    2.57 -		if (!isShown) {
    2.58 -			[NSCursor unhide];
    2.59 -			isShown = SDL_TRUE;
    2.60 -		}
    2.61 -	} else if (isShown) {
    2.62 -		[NSCursor hide];
    2.63 -		isShown = SDL_FALSE;
    2.64 +    SDL_VideoDevice *device = SDL_GetVideoDevice();
    2.65 +    SDL_Window *window = (device ? device->windows : NULL);
    2.66 +    for (; window != NULL; window = window->next) {
    2.67 +        SDL_WindowData *driverdata = (SDL_WindowData *)window->driverdata;
    2.68 +        if (driverdata) {
    2.69 +            [driverdata->nswindow performSelectorOnMainThread:@selector(invalidateCursorRectsForView:)
    2.70 +                                                   withObject:[driverdata->nswindow contentView]
    2.71 +                                                waitUntilDone:NO];
    2.72 +        }
    2.73      }
    2.74  
    2.75      [pool release];
     3.1 --- a/src/video/cocoa/SDL_cocoawindow.m	Mon May 06 23:02:37 2013 +0200
     3.2 +++ b/src/video/cocoa/SDL_cocoawindow.m	Tue May 07 16:52:39 2013 -0700
     3.3 @@ -254,7 +254,6 @@
     3.4  
     3.5          if (x >= 0 && x < window->w && y >= 0 && y < window->h) {
     3.6              SDL_SendMouseMotion(window, 0, 0, x, y);
     3.7 -            SDL_SetCursor(NULL);
     3.8          }
     3.9      }
    3.10  
    3.11 @@ -520,6 +519,7 @@
    3.12  @end
    3.13  
    3.14  @interface SDLView : NSView
    3.15 +
    3.16  /* The default implementation doesn't pass rightMouseDown to responder chain */
    3.17  - (void)rightMouseDown:(NSEvent *)theEvent;
    3.18  @end
    3.19 @@ -529,6 +529,20 @@
    3.20  {
    3.21      [[self nextResponder] rightMouseDown:theEvent];
    3.22  }
    3.23 +
    3.24 +- (void)resetCursorRects
    3.25 +{
    3.26 +    [super resetCursorRects];
    3.27 +    SDL_Mouse *mouse = SDL_GetMouse();
    3.28 +
    3.29 +    if (mouse->cursor_shown && mouse->cur_cursor) {
    3.30 +        [self addCursorRect:[self bounds]
    3.31 +                     cursor:mouse->cur_cursor->driverdata];
    3.32 +    } else {
    3.33 +        [self addCursorRect:[self bounds]
    3.34 +                     cursor:[NSCursor invisibleCursor]];
    3.35 +    }
    3.36 +}
    3.37  @end
    3.38  
    3.39  static unsigned int