src/video/cocoa/SDL_cocoamouse.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Tue, 07 May 2013 16:52:39 -0700
changeset 7158 ff52fba70795
parent 7114 02b2fe147478
child 7191 75360622e65f
permissions -rw-r--r--
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
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "SDL_config.h"
    22 
    23 #if SDL_VIDEO_DRIVER_COCOA
    24 
    25 #include "SDL_assert.h"
    26 #include "SDL_events.h"
    27 #include "SDL_cocoamouse.h"
    28 
    29 #include "../../events/SDL_mouse_c.h"
    30 
    31 @implementation NSCursor (InvisibleCursor)
    32 + (NSCursor *)invisibleCursor
    33 {
    34     static NSCursor *invisibleCursor = NULL;
    35     if (!invisibleCursor) {
    36         /* RAW 16x16 transparent GIF */
    37         static unsigned char cursorBytes[] = {
    38             0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x10, 0x00, 0x10, 0x00, 0x80,
    39             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04,
    40             0x01, 0x00, 0x00, 0x01, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x10,
    41             0x00, 0x10, 0x00, 0x00, 0x02, 0x0E, 0x8C, 0x8F, 0xA9, 0xCB, 0xED,
    42             0x0F, 0xA3, 0x9C, 0xB4, 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B
    43         };
    44 
    45         NSData *cursorData = [NSData dataWithBytesNoCopy:&cursorBytes[0]
    46                                                   length:sizeof(cursorBytes)
    47                                             freeWhenDone:NO];
    48         NSImage *cursorImage = [[[NSImage alloc] initWithData:cursorData] autorelease];
    49         invisibleCursor = [[NSCursor alloc] initWithImage:cursorImage
    50                                                   hotSpot:NSZeroPoint];
    51     }
    52 
    53     return invisibleCursor;
    54 }
    55 @end
    56 
    57 
    58 static SDL_Cursor *
    59 Cocoa_CreateDefaultCursor()
    60 {
    61     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    62     NSCursor *nscursor;
    63     SDL_Cursor *cursor = NULL;
    64 
    65     nscursor = [NSCursor arrowCursor];
    66 
    67     if (nscursor) {
    68         cursor = SDL_calloc(1, sizeof(*cursor));
    69         if (cursor) {
    70             cursor->driverdata = nscursor;
    71             [nscursor retain];
    72         }
    73     }
    74 
    75     [pool release];
    76 
    77     return cursor;
    78 }
    79 
    80 static SDL_Cursor *
    81 Cocoa_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
    82 {
    83     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    84     NSImage *nsimage;
    85     NSCursor *nscursor = NULL;
    86     SDL_Cursor *cursor = NULL;
    87 
    88     nsimage = Cocoa_CreateImage(surface);
    89     if (nsimage) {
    90         nscursor = [[NSCursor alloc] initWithImage: nsimage hotSpot: NSMakePoint(hot_x, hot_y)];
    91     }
    92 
    93     if (nscursor) {
    94         cursor = SDL_calloc(1, sizeof(*cursor));
    95         if (cursor) {
    96             cursor->driverdata = nscursor;
    97         }
    98     }
    99 
   100     [pool release];
   101 
   102     return cursor;
   103 }
   104 
   105 static SDL_Cursor *
   106 Cocoa_CreateSystemCursor(SDL_SystemCursor id)
   107 {
   108     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   109     NSCursor *nscursor = NULL;
   110     SDL_Cursor *cursor = NULL;
   111 
   112     switch(id)
   113     {
   114     case SDL_SYSTEM_CURSOR_ARROW:
   115         nscursor = [NSCursor arrowCursor];
   116         break;
   117     case SDL_SYSTEM_CURSOR_IBEAM:
   118         nscursor = [NSCursor IBeamCursor];
   119         break;
   120     case SDL_SYSTEM_CURSOR_WAIT:
   121         nscursor = [NSCursor arrowCursor];
   122         break;
   123     case SDL_SYSTEM_CURSOR_CROSSHAIR:
   124         nscursor = [NSCursor crosshairCursor];
   125         break;
   126     case SDL_SYSTEM_CURSOR_WAITARROW:
   127         nscursor = [NSCursor arrowCursor];
   128         break;
   129     case SDL_SYSTEM_CURSOR_SIZENWSE:
   130     case SDL_SYSTEM_CURSOR_SIZENESW:
   131         nscursor = [NSCursor closedHandCursor];
   132         break;
   133     case SDL_SYSTEM_CURSOR_SIZEWE:
   134         nscursor = [NSCursor resizeLeftRightCursor];
   135         break;
   136     case SDL_SYSTEM_CURSOR_SIZENS:
   137         nscursor = [NSCursor resizeUpDownCursor];
   138         break;
   139     case SDL_SYSTEM_CURSOR_SIZEALL:
   140         nscursor = [NSCursor closedHandCursor];
   141         break;
   142     case SDL_SYSTEM_CURSOR_NO:
   143         nscursor = [NSCursor operationNotAllowedCursor];
   144         break;
   145     case SDL_SYSTEM_CURSOR_HAND:
   146         nscursor = [NSCursor pointingHandCursor];
   147         break;
   148     default:
   149         SDL_assert(!"Unknown system cursor");
   150         return NULL;
   151     }
   152 
   153     if (nscursor) {
   154         cursor = SDL_calloc(1, sizeof(*cursor));
   155         if (cursor) {
   156             // We'll free it later, so retain it here
   157             [nscursor retain];
   158             cursor->driverdata = nscursor;
   159         }
   160     }
   161 
   162     [pool release];
   163 
   164     return cursor;
   165 }
   166 
   167 static void
   168 Cocoa_FreeCursor(SDL_Cursor * cursor)
   169 {
   170     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   171     NSCursor *nscursor = (NSCursor *)cursor->driverdata;
   172 
   173     [nscursor release];
   174     SDL_free(cursor);
   175 
   176     [pool release];
   177 }
   178 
   179 static int
   180 Cocoa_ShowCursor(SDL_Cursor * cursor)
   181 {
   182     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   183 
   184     SDL_VideoDevice *device = SDL_GetVideoDevice();
   185     SDL_Window *window = (device ? device->windows : NULL);
   186     for (; window != NULL; window = window->next) {
   187         SDL_WindowData *driverdata = (SDL_WindowData *)window->driverdata;
   188         if (driverdata) {
   189             [driverdata->nswindow performSelectorOnMainThread:@selector(invalidateCursorRectsForView:)
   190                                                    withObject:[driverdata->nswindow contentView]
   191                                                 waitUntilDone:NO];
   192         }
   193     }
   194 
   195     [pool release];
   196 
   197     return 0;
   198 }
   199 
   200 static void
   201 Cocoa_WarpMouse(SDL_Window * window, int x, int y)
   202 {
   203     SDL_Mouse *mouse = SDL_GetMouse();
   204     CGPoint point;
   205 
   206     point.x = (float)window->x + x;
   207     point.y = (float)window->y + y;
   208 
   209     {
   210         /* This makes Cocoa_HandleMouseEvent ignore this delta in the next
   211          * movement event.
   212          */
   213         SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
   214         NSPoint location =  [NSEvent mouseLocation];
   215         driverdata->deltaXOffset = location.x - point.x;
   216         driverdata->deltaYOffset = point.y - location.y;
   217     }
   218 
   219     /* According to the docs, this was deprecated in 10.6, but it's still
   220      * around. The substitute requires a CGEventSource, but I'm not entirely
   221      * sure how we'd procure the right one for this event.
   222      */
   223     CGSetLocalEventsSuppressionInterval(0.0);
   224     CGWarpMouseCursorPosition(point);
   225     CGSetLocalEventsSuppressionInterval(0.25);
   226 
   227     /* CGWarpMouseCursorPosition doesn't generate a window event, unlike our
   228      * other implementations' APIs.
   229      */
   230     SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y);
   231 }
   232 
   233 static int
   234 Cocoa_SetRelativeMouseMode(SDL_bool enabled)
   235 {
   236     CGError result;
   237 
   238     if (enabled) {
   239         result = CGAssociateMouseAndMouseCursorPosition(NO);
   240     } else {
   241         result = CGAssociateMouseAndMouseCursorPosition(YES);
   242     }
   243     if (result != kCGErrorSuccess) {
   244         return SDL_SetError("CGAssociateMouseAndMouseCursorPosition() failed");
   245     }
   246     return 0;
   247 }
   248 
   249 void
   250 Cocoa_InitMouse(_THIS)
   251 {
   252     SDL_Mouse *mouse = SDL_GetMouse();
   253 
   254     mouse->driverdata = SDL_calloc(1, sizeof(SDL_MouseData));
   255 
   256     mouse->CreateCursor = Cocoa_CreateCursor;
   257     mouse->CreateSystemCursor = Cocoa_CreateSystemCursor;
   258     mouse->ShowCursor = Cocoa_ShowCursor;
   259     mouse->FreeCursor = Cocoa_FreeCursor;
   260     mouse->WarpMouse = Cocoa_WarpMouse;
   261     mouse->SetRelativeMouseMode = Cocoa_SetRelativeMouseMode;
   262 
   263     SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor());
   264 }
   265 
   266 void
   267 Cocoa_HandleMouseEvent(_THIS, NSEvent *event)
   268 {
   269     SDL_Mouse *mouse = SDL_GetMouse();
   270 
   271     if (mouse->relative_mode &&
   272         ([event type] == NSMouseMoved ||
   273          [event type] == NSLeftMouseDragged ||
   274          [event type] == NSRightMouseDragged ||
   275          [event type] == NSOtherMouseDragged)) {
   276         SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
   277         float x = [event deltaX] + driverdata->deltaXOffset;
   278         float y = [event deltaY] + driverdata->deltaYOffset;
   279         driverdata->deltaXOffset = driverdata->deltaYOffset = 0;
   280 
   281         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int)x, (int)y);
   282     }
   283 }
   284 
   285 void
   286 Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event)
   287 {
   288     SDL_Mouse *mouse = SDL_GetMouse();
   289 
   290     float x = [event deltaX];
   291     float y = [event deltaY];
   292 
   293     if (x > 0) {
   294         x += 0.9f;
   295     } else if (x < 0) {
   296         x -= 0.9f;
   297     }
   298     if (y > 0) {
   299         y += 0.9f;
   300     } else if (y < 0) {
   301         y -= 0.9f;
   302     }
   303     SDL_SendMouseWheel(window, mouse->mouseID, (int)x, (int)y);
   304 }
   305 
   306 void
   307 Cocoa_QuitMouse(_THIS)
   308 {
   309     SDL_Mouse *mouse = SDL_GetMouse();
   310     if (mouse) {
   311         SDL_free(mouse->driverdata);
   312     }
   313 }
   314 
   315 #endif /* SDL_VIDEO_DRIVER_COCOA */
   316 
   317 /* vi: set ts=4 sw=4 expandtab: */