src/video/cocoa/SDL_cocoamouse.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Mon, 15 Jul 2013 11:58:45 -0700
changeset 7465 1bcf7aac5623
parent 7464 7cdeb64faa72
child 7593 20298a0d8631
permissions -rw-r--r--
Mac: CGMakePoint -> CGPointMake in previous change.
     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 = CGPointMake(x, y);
   205 
   206     if (!(window->flags & SDL_WINDOW_FULLSCREEN))
   207     {
   208         point.x += window->x;
   209         point.y += window->y;
   210     }
   211 
   212     {
   213         /* This makes Cocoa_HandleMouseEvent ignore this delta in the next
   214          * movement event.
   215          */
   216         SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
   217         NSPoint location =  [NSEvent mouseLocation];
   218         driverdata->deltaXOffset = location.x - point.x;
   219         driverdata->deltaYOffset = point.y - location.y;
   220     }
   221 
   222     /* According to the docs, this was deprecated in 10.6, but it's still
   223      * around. The substitute requires a CGEventSource, but I'm not entirely
   224      * sure how we'd procure the right one for this event.
   225      */
   226     CGSetLocalEventsSuppressionInterval(0.0);
   227     CGWarpMouseCursorPosition(point);
   228     CGSetLocalEventsSuppressionInterval(0.25);
   229 
   230     if (!mouse->relative_mode) {
   231         /* CGWarpMouseCursorPosition doesn't generate a window event, unlike our
   232          * other implementations' APIs.
   233          */
   234         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y);
   235     }
   236 }
   237 
   238 static int
   239 Cocoa_SetRelativeMouseMode(SDL_bool enabled)
   240 {
   241     CGError result;
   242 
   243     if (enabled) {
   244         result = CGAssociateMouseAndMouseCursorPosition(NO);
   245     } else {
   246         result = CGAssociateMouseAndMouseCursorPosition(YES);
   247     }
   248     if (result != kCGErrorSuccess) {
   249         return SDL_SetError("CGAssociateMouseAndMouseCursorPosition() failed");
   250     }
   251     return 0;
   252 }
   253 
   254 void
   255 Cocoa_InitMouse(_THIS)
   256 {
   257     SDL_Mouse *mouse = SDL_GetMouse();
   258 
   259     mouse->driverdata = SDL_calloc(1, sizeof(SDL_MouseData));
   260 
   261     mouse->CreateCursor = Cocoa_CreateCursor;
   262     mouse->CreateSystemCursor = Cocoa_CreateSystemCursor;
   263     mouse->ShowCursor = Cocoa_ShowCursor;
   264     mouse->FreeCursor = Cocoa_FreeCursor;
   265     mouse->WarpMouse = Cocoa_WarpMouse;
   266     mouse->SetRelativeMouseMode = Cocoa_SetRelativeMouseMode;
   267 
   268     SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor());
   269 }
   270 
   271 void
   272 Cocoa_HandleMouseEvent(_THIS, NSEvent *event)
   273 {
   274     SDL_Mouse *mouse = SDL_GetMouse();
   275 
   276     if (mouse->relative_mode &&
   277         ([event type] == NSMouseMoved ||
   278          [event type] == NSLeftMouseDragged ||
   279          [event type] == NSRightMouseDragged ||
   280          [event type] == NSOtherMouseDragged)) {
   281         SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
   282         float x = [event deltaX] + driverdata->deltaXOffset;
   283         float y = [event deltaY] + driverdata->deltaYOffset;
   284         driverdata->deltaXOffset = driverdata->deltaYOffset = 0;
   285 
   286         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int)x, (int)y);
   287     }
   288 }
   289 
   290 void
   291 Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event)
   292 {
   293     SDL_Mouse *mouse = SDL_GetMouse();
   294 
   295     float x = [event deltaX];
   296     float y = [event deltaY];
   297 
   298     if (x > 0) {
   299         x += 0.9f;
   300     } else if (x < 0) {
   301         x -= 0.9f;
   302     }
   303     if (y > 0) {
   304         y += 0.9f;
   305     } else if (y < 0) {
   306         y -= 0.9f;
   307     }
   308     SDL_SendMouseWheel(window, mouse->mouseID, (int)x, (int)y);
   309 }
   310 
   311 void
   312 Cocoa_QuitMouse(_THIS)
   313 {
   314     SDL_Mouse *mouse = SDL_GetMouse();
   315     if (mouse) {
   316         SDL_free(mouse->driverdata);
   317     }
   318 }
   319 
   320 #endif /* SDL_VIDEO_DRIVER_COCOA */
   321 
   322 /* vi: set ts=4 sw=4 expandtab: */