src/video/cocoa/SDL_cocoamouse.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Tue, 04 Jun 2013 14:54:49 -0700
changeset 7271 963d2ba35cfe
parent 7191 75360622e65f
child 7464 7cdeb64faa72
permissions -rw-r--r--
Mac: Fix incorrect relative jump on focus / start.

We should no longer send huge jumps on relative mode focus changes if
the cursor was moved around, or on initial start.

Fixes http://bugzilla.libsdl.org/show_bug.cgi?id=1836
     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     if (!mouse->relative_mode) {
   228         /* CGWarpMouseCursorPosition doesn't generate a window event, unlike our
   229          * other implementations' APIs.
   230          */
   231         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y);
   232     }
   233 }
   234 
   235 static int
   236 Cocoa_SetRelativeMouseMode(SDL_bool enabled)
   237 {
   238     CGError result;
   239 
   240     if (enabled) {
   241         result = CGAssociateMouseAndMouseCursorPosition(NO);
   242     } else {
   243         result = CGAssociateMouseAndMouseCursorPosition(YES);
   244     }
   245     if (result != kCGErrorSuccess) {
   246         return SDL_SetError("CGAssociateMouseAndMouseCursorPosition() failed");
   247     }
   248     return 0;
   249 }
   250 
   251 void
   252 Cocoa_InitMouse(_THIS)
   253 {
   254     SDL_Mouse *mouse = SDL_GetMouse();
   255 
   256     mouse->driverdata = SDL_calloc(1, sizeof(SDL_MouseData));
   257 
   258     mouse->CreateCursor = Cocoa_CreateCursor;
   259     mouse->CreateSystemCursor = Cocoa_CreateSystemCursor;
   260     mouse->ShowCursor = Cocoa_ShowCursor;
   261     mouse->FreeCursor = Cocoa_FreeCursor;
   262     mouse->WarpMouse = Cocoa_WarpMouse;
   263     mouse->SetRelativeMouseMode = Cocoa_SetRelativeMouseMode;
   264 
   265     SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor());
   266 }
   267 
   268 void
   269 Cocoa_HandleMouseEvent(_THIS, NSEvent *event)
   270 {
   271     SDL_Mouse *mouse = SDL_GetMouse();
   272 
   273     if (mouse->relative_mode &&
   274         ([event type] == NSMouseMoved ||
   275          [event type] == NSLeftMouseDragged ||
   276          [event type] == NSRightMouseDragged ||
   277          [event type] == NSOtherMouseDragged)) {
   278         SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
   279         float x = [event deltaX] + driverdata->deltaXOffset;
   280         float y = [event deltaY] + driverdata->deltaYOffset;
   281         driverdata->deltaXOffset = driverdata->deltaYOffset = 0;
   282 
   283         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int)x, (int)y);
   284     }
   285 }
   286 
   287 void
   288 Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event)
   289 {
   290     SDL_Mouse *mouse = SDL_GetMouse();
   291 
   292     float x = [event deltaX];
   293     float y = [event deltaY];
   294 
   295     if (x > 0) {
   296         x += 0.9f;
   297     } else if (x < 0) {
   298         x -= 0.9f;
   299     }
   300     if (y > 0) {
   301         y += 0.9f;
   302     } else if (y < 0) {
   303         y -= 0.9f;
   304     }
   305     SDL_SendMouseWheel(window, mouse->mouseID, (int)x, (int)y);
   306 }
   307 
   308 void
   309 Cocoa_QuitMouse(_THIS)
   310 {
   311     SDL_Mouse *mouse = SDL_GetMouse();
   312     if (mouse) {
   313         SDL_free(mouse->driverdata);
   314     }
   315 }
   316 
   317 #endif /* SDL_VIDEO_DRIVER_COCOA */
   318 
   319 /* vi: set ts=4 sw=4 expandtab: */