src/video/cocoa/SDL_cocoamouse.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Wed, 07 Aug 2013 16:29:15 -0700
changeset 7593 20298a0d8631
parent 7465 1bcf7aac5623
child 7792 b8d4be87970a
permissions -rw-r--r--
Mac: Better mouse-grab if you define SDL_MAC_NO_SANDBOX.

This uses a better mouse grab if you define SDL_MAC_NO_SANDBOX. This
mouse grab uses CGEventTapCreate, which you cannot access if you have
sandboxing enabled.
     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 #include "SDL_cocoamousetap.h"
    29 
    30 #include "../../events/SDL_mouse_c.h"
    31 
    32 @implementation NSCursor (InvisibleCursor)
    33 + (NSCursor *)invisibleCursor
    34 {
    35     static NSCursor *invisibleCursor = NULL;
    36     if (!invisibleCursor) {
    37         /* RAW 16x16 transparent GIF */
    38         static unsigned char cursorBytes[] = {
    39             0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x10, 0x00, 0x10, 0x00, 0x80,
    40             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04,
    41             0x01, 0x00, 0x00, 0x01, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x10,
    42             0x00, 0x10, 0x00, 0x00, 0x02, 0x0E, 0x8C, 0x8F, 0xA9, 0xCB, 0xED,
    43             0x0F, 0xA3, 0x9C, 0xB4, 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B
    44         };
    45 
    46         NSData *cursorData = [NSData dataWithBytesNoCopy:&cursorBytes[0]
    47                                                   length:sizeof(cursorBytes)
    48                                             freeWhenDone:NO];
    49         NSImage *cursorImage = [[[NSImage alloc] initWithData:cursorData] autorelease];
    50         invisibleCursor = [[NSCursor alloc] initWithImage:cursorImage
    51                                                   hotSpot:NSZeroPoint];
    52     }
    53 
    54     return invisibleCursor;
    55 }
    56 @end
    57 
    58 
    59 static SDL_Cursor *
    60 Cocoa_CreateDefaultCursor()
    61 {
    62     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    63     NSCursor *nscursor;
    64     SDL_Cursor *cursor = NULL;
    65 
    66     nscursor = [NSCursor arrowCursor];
    67 
    68     if (nscursor) {
    69         cursor = SDL_calloc(1, sizeof(*cursor));
    70         if (cursor) {
    71             cursor->driverdata = nscursor;
    72             [nscursor retain];
    73         }
    74     }
    75 
    76     [pool release];
    77 
    78     return cursor;
    79 }
    80 
    81 static SDL_Cursor *
    82 Cocoa_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
    83 {
    84     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    85     NSImage *nsimage;
    86     NSCursor *nscursor = NULL;
    87     SDL_Cursor *cursor = NULL;
    88 
    89     nsimage = Cocoa_CreateImage(surface);
    90     if (nsimage) {
    91         nscursor = [[NSCursor alloc] initWithImage: nsimage hotSpot: NSMakePoint(hot_x, hot_y)];
    92     }
    93 
    94     if (nscursor) {
    95         cursor = SDL_calloc(1, sizeof(*cursor));
    96         if (cursor) {
    97             cursor->driverdata = nscursor;
    98         } else {
    99             [nscursor release];
   100         }
   101     }
   102 
   103     [pool release];
   104 
   105     return cursor;
   106 }
   107 
   108 static SDL_Cursor *
   109 Cocoa_CreateSystemCursor(SDL_SystemCursor id)
   110 {
   111     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   112     NSCursor *nscursor = NULL;
   113     SDL_Cursor *cursor = NULL;
   114 
   115     switch(id)
   116     {
   117     case SDL_SYSTEM_CURSOR_ARROW:
   118         nscursor = [NSCursor arrowCursor];
   119         break;
   120     case SDL_SYSTEM_CURSOR_IBEAM:
   121         nscursor = [NSCursor IBeamCursor];
   122         break;
   123     case SDL_SYSTEM_CURSOR_WAIT:
   124         nscursor = [NSCursor arrowCursor];
   125         break;
   126     case SDL_SYSTEM_CURSOR_CROSSHAIR:
   127         nscursor = [NSCursor crosshairCursor];
   128         break;
   129     case SDL_SYSTEM_CURSOR_WAITARROW:
   130         nscursor = [NSCursor arrowCursor];
   131         break;
   132     case SDL_SYSTEM_CURSOR_SIZENWSE:
   133     case SDL_SYSTEM_CURSOR_SIZENESW:
   134         nscursor = [NSCursor closedHandCursor];
   135         break;
   136     case SDL_SYSTEM_CURSOR_SIZEWE:
   137         nscursor = [NSCursor resizeLeftRightCursor];
   138         break;
   139     case SDL_SYSTEM_CURSOR_SIZENS:
   140         nscursor = [NSCursor resizeUpDownCursor];
   141         break;
   142     case SDL_SYSTEM_CURSOR_SIZEALL:
   143         nscursor = [NSCursor closedHandCursor];
   144         break;
   145     case SDL_SYSTEM_CURSOR_NO:
   146         nscursor = [NSCursor operationNotAllowedCursor];
   147         break;
   148     case SDL_SYSTEM_CURSOR_HAND:
   149         nscursor = [NSCursor pointingHandCursor];
   150         break;
   151     default:
   152         SDL_assert(!"Unknown system cursor");
   153         return NULL;
   154     }
   155 
   156     if (nscursor) {
   157         cursor = SDL_calloc(1, sizeof(*cursor));
   158         if (cursor) {
   159             /* We'll free it later, so retain it here */
   160             [nscursor retain];
   161             cursor->driverdata = nscursor;
   162         }
   163     }
   164 
   165     [pool release];
   166 
   167     return cursor;
   168 }
   169 
   170 static void
   171 Cocoa_FreeCursor(SDL_Cursor * cursor)
   172 {
   173     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   174     NSCursor *nscursor = (NSCursor *)cursor->driverdata;
   175 
   176     [nscursor release];
   177     SDL_free(cursor);
   178 
   179     [pool release];
   180 }
   181 
   182 static int
   183 Cocoa_ShowCursor(SDL_Cursor * cursor)
   184 {
   185     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   186 
   187     SDL_VideoDevice *device = SDL_GetVideoDevice();
   188     SDL_Window *window = (device ? device->windows : NULL);
   189     for (; window != NULL; window = window->next) {
   190         SDL_WindowData *driverdata = (SDL_WindowData *)window->driverdata;
   191         if (driverdata) {
   192             [driverdata->nswindow performSelectorOnMainThread:@selector(invalidateCursorRectsForView:)
   193                                                    withObject:[driverdata->nswindow contentView]
   194                                                 waitUntilDone:NO];
   195         }
   196     }
   197 
   198     [pool release];
   199 
   200     return 0;
   201 }
   202 
   203 static void
   204 Cocoa_WarpMouse(SDL_Window * window, int x, int y)
   205 {
   206     SDL_Mouse *mouse = SDL_GetMouse();
   207     CGPoint point = CGPointMake(x, y);
   208 
   209     if (!(window->flags & SDL_WINDOW_FULLSCREEN))
   210     {
   211         point.x += window->x;
   212         point.y += window->y;
   213     }
   214 
   215     {
   216         /* This makes Cocoa_HandleMouseEvent ignore this delta in the next
   217          * movement event.
   218          */
   219         SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
   220         NSPoint location =  [NSEvent mouseLocation];
   221         driverdata->deltaXOffset = location.x - point.x;
   222         driverdata->deltaYOffset = point.y - location.y;
   223     }
   224 
   225     /* According to the docs, this was deprecated in 10.6, but it's still
   226      * around. The substitute requires a CGEventSource, but I'm not entirely
   227      * sure how we'd procure the right one for this event.
   228      */
   229     CGSetLocalEventsSuppressionInterval(0.0);
   230     CGWarpMouseCursorPosition(point);
   231     CGSetLocalEventsSuppressionInterval(0.25);
   232 
   233     if (!mouse->relative_mode) {
   234         /* CGWarpMouseCursorPosition doesn't generate a window event, unlike our
   235          * other implementations' APIs.
   236          */
   237         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y);
   238     }
   239 }
   240 
   241 static int
   242 Cocoa_SetRelativeMouseMode(SDL_bool enabled)
   243 {
   244     CGError result;
   245 
   246     if (enabled) {
   247         result = CGAssociateMouseAndMouseCursorPosition(NO);
   248     } else {
   249         result = CGAssociateMouseAndMouseCursorPosition(YES);
   250     }
   251     if (result != kCGErrorSuccess) {
   252         return SDL_SetError("CGAssociateMouseAndMouseCursorPosition() failed");
   253     }
   254     return 0;
   255 }
   256 
   257 void
   258 Cocoa_InitMouse(_THIS)
   259 {
   260     SDL_Mouse *mouse = SDL_GetMouse();
   261 
   262     mouse->driverdata = SDL_calloc(1, sizeof(SDL_MouseData));
   263 
   264     mouse->CreateCursor = Cocoa_CreateCursor;
   265     mouse->CreateSystemCursor = Cocoa_CreateSystemCursor;
   266     mouse->ShowCursor = Cocoa_ShowCursor;
   267     mouse->FreeCursor = Cocoa_FreeCursor;
   268     mouse->WarpMouse = Cocoa_WarpMouse;
   269     mouse->SetRelativeMouseMode = Cocoa_SetRelativeMouseMode;
   270 
   271     SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor());
   272 
   273     Cocoa_InitMouseEventTap(mouse->driverdata);
   274 }
   275 
   276 void
   277 Cocoa_HandleMouseEvent(_THIS, NSEvent *event)
   278 {
   279     SDL_Mouse *mouse = SDL_GetMouse();
   280 
   281     if (mouse->relative_mode &&
   282         ([event type] == NSMouseMoved ||
   283          [event type] == NSLeftMouseDragged ||
   284          [event type] == NSRightMouseDragged ||
   285          [event type] == NSOtherMouseDragged)) {
   286         SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
   287         float x = [event deltaX] + driverdata->deltaXOffset;
   288         float y = [event deltaY] + driverdata->deltaYOffset;
   289         driverdata->deltaXOffset = driverdata->deltaYOffset = 0;
   290 
   291         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int)x, (int)y);
   292     }
   293 }
   294 
   295 void
   296 Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event)
   297 {
   298     SDL_Mouse *mouse = SDL_GetMouse();
   299 
   300     float x = [event deltaX];
   301     float y = [event deltaY];
   302 
   303     if (x > 0) {
   304         x += 0.9f;
   305     } else if (x < 0) {
   306         x -= 0.9f;
   307     }
   308     if (y > 0) {
   309         y += 0.9f;
   310     } else if (y < 0) {
   311         y -= 0.9f;
   312     }
   313     SDL_SendMouseWheel(window, mouse->mouseID, (int)x, (int)y);
   314 }
   315 
   316 void
   317 Cocoa_QuitMouse(_THIS)
   318 {
   319     SDL_Mouse *mouse = SDL_GetMouse();
   320     if (mouse) {
   321         if (mouse->driverdata) {
   322             Cocoa_QuitMouseEventTap(((SDL_MouseData*)mouse->driverdata));
   323         }
   324 
   325         SDL_free(mouse->driverdata);
   326     }
   327 }
   328 
   329 #endif /* SDL_VIDEO_DRIVER_COCOA */
   330 
   331 /* vi: set ts=4 sw=4 expandtab: */