src/video/cocoa/SDL_cocoamouse.m
author Sam Lantinga <slouken@libsdl.org>
Mon, 07 Jul 2014 12:48:25 -0700
changeset 8986 1c81316ac642
parent 8953 dc80dc0bd22e
child 9086 c5e33f9a0d03
permissions -rw-r--r--
Fixed bug 2631 - Mac: minor code cleanup

Alex Szpakowski

Some minor changes to the Mac-specific backend code:

- Fixed up some code style issues (mostly brace style inconsistencies).

- Fixed a compiler warning in SDL_cocoaevents.m.

- Removed some useless code now that the 10.7 SDK is required to build SDL.

- Removed Gestalt(gestaltSystemVersion, ...) call and switched to NSAppKitVersionNumber for version checking code. Using Gestalt with gestaltSystemVersion will give 0x1090 in Mac OS 10.10+, and the whole Gestalt function was deprecated in Mac OS 10.8.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 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_internal.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 /* #define DEBUG_COCOAMOUSE */
    33 
    34 #ifdef DEBUG_COCOAMOUSE
    35 #define DLog(fmt, ...) printf("%s: " fmt "\n", __func__, ##__VA_ARGS__)
    36 #else
    37 #define DLog(...) do { } while (0)
    38 #endif
    39 
    40 @implementation NSCursor (InvisibleCursor)
    41 + (NSCursor *)invisibleCursor
    42 {
    43     static NSCursor *invisibleCursor = NULL;
    44     if (!invisibleCursor) {
    45         /* RAW 16x16 transparent GIF */
    46         static unsigned char cursorBytes[] = {
    47             0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x10, 0x00, 0x10, 0x00, 0x80,
    48             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04,
    49             0x01, 0x00, 0x00, 0x01, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x10,
    50             0x00, 0x10, 0x00, 0x00, 0x02, 0x0E, 0x8C, 0x8F, 0xA9, 0xCB, 0xED,
    51             0x0F, 0xA3, 0x9C, 0xB4, 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B
    52         };
    53 
    54         NSData *cursorData = [NSData dataWithBytesNoCopy:&cursorBytes[0]
    55                                                   length:sizeof(cursorBytes)
    56                                             freeWhenDone:NO];
    57         NSImage *cursorImage = [[[NSImage alloc] initWithData:cursorData] autorelease];
    58         invisibleCursor = [[NSCursor alloc] initWithImage:cursorImage
    59                                                   hotSpot:NSZeroPoint];
    60     }
    61 
    62     return invisibleCursor;
    63 }
    64 @end
    65 
    66 
    67 static SDL_Cursor *
    68 Cocoa_CreateDefaultCursor()
    69 {
    70     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    71     NSCursor *nscursor;
    72     SDL_Cursor *cursor = NULL;
    73 
    74     nscursor = [NSCursor arrowCursor];
    75 
    76     if (nscursor) {
    77         cursor = SDL_calloc(1, sizeof(*cursor));
    78         if (cursor) {
    79             cursor->driverdata = nscursor;
    80             [nscursor retain];
    81         }
    82     }
    83 
    84     [pool release];
    85 
    86     return cursor;
    87 }
    88 
    89 static SDL_Cursor *
    90 Cocoa_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
    91 {
    92     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    93     NSImage *nsimage;
    94     NSCursor *nscursor = NULL;
    95     SDL_Cursor *cursor = NULL;
    96 
    97     nsimage = Cocoa_CreateImage(surface);
    98     if (nsimage) {
    99         nscursor = [[NSCursor alloc] initWithImage: nsimage hotSpot: NSMakePoint(hot_x, hot_y)];
   100     }
   101 
   102     if (nscursor) {
   103         cursor = SDL_calloc(1, sizeof(*cursor));
   104         if (cursor) {
   105             cursor->driverdata = nscursor;
   106         } else {
   107             [nscursor release];
   108         }
   109     }
   110 
   111     [pool release];
   112 
   113     return cursor;
   114 }
   115 
   116 static SDL_Cursor *
   117 Cocoa_CreateSystemCursor(SDL_SystemCursor id)
   118 {
   119     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   120     NSCursor *nscursor = NULL;
   121     SDL_Cursor *cursor = NULL;
   122 
   123     switch(id) {
   124     case SDL_SYSTEM_CURSOR_ARROW:
   125         nscursor = [NSCursor arrowCursor];
   126         break;
   127     case SDL_SYSTEM_CURSOR_IBEAM:
   128         nscursor = [NSCursor IBeamCursor];
   129         break;
   130     case SDL_SYSTEM_CURSOR_WAIT:
   131         nscursor = [NSCursor arrowCursor];
   132         break;
   133     case SDL_SYSTEM_CURSOR_CROSSHAIR:
   134         nscursor = [NSCursor crosshairCursor];
   135         break;
   136     case SDL_SYSTEM_CURSOR_WAITARROW:
   137         nscursor = [NSCursor arrowCursor];
   138         break;
   139     case SDL_SYSTEM_CURSOR_SIZENWSE:
   140     case SDL_SYSTEM_CURSOR_SIZENESW:
   141         nscursor = [NSCursor closedHandCursor];
   142         break;
   143     case SDL_SYSTEM_CURSOR_SIZEWE:
   144         nscursor = [NSCursor resizeLeftRightCursor];
   145         break;
   146     case SDL_SYSTEM_CURSOR_SIZENS:
   147         nscursor = [NSCursor resizeUpDownCursor];
   148         break;
   149     case SDL_SYSTEM_CURSOR_SIZEALL:
   150         nscursor = [NSCursor closedHandCursor];
   151         break;
   152     case SDL_SYSTEM_CURSOR_NO:
   153         nscursor = [NSCursor operationNotAllowedCursor];
   154         break;
   155     case SDL_SYSTEM_CURSOR_HAND:
   156         nscursor = [NSCursor pointingHandCursor];
   157         break;
   158     default:
   159         SDL_assert(!"Unknown system cursor");
   160         return NULL;
   161     }
   162 
   163     if (nscursor) {
   164         cursor = SDL_calloc(1, sizeof(*cursor));
   165         if (cursor) {
   166             /* We'll free it later, so retain it here */
   167             [nscursor retain];
   168             cursor->driverdata = nscursor;
   169         }
   170     }
   171 
   172     [pool release];
   173 
   174     return cursor;
   175 }
   176 
   177 static void
   178 Cocoa_FreeCursor(SDL_Cursor * cursor)
   179 {
   180     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   181     NSCursor *nscursor = (NSCursor *)cursor->driverdata;
   182 
   183     [nscursor release];
   184     SDL_free(cursor);
   185 
   186     [pool release];
   187 }
   188 
   189 static int
   190 Cocoa_ShowCursor(SDL_Cursor * cursor)
   191 {
   192     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   193 
   194     SDL_VideoDevice *device = SDL_GetVideoDevice();
   195     SDL_Window *window = (device ? device->windows : NULL);
   196     for (; window != NULL; window = window->next) {
   197         SDL_WindowData *driverdata = (SDL_WindowData *)window->driverdata;
   198         if (driverdata) {
   199             [driverdata->nswindow performSelectorOnMainThread:@selector(invalidateCursorRectsForView:)
   200                                                    withObject:[driverdata->nswindow contentView]
   201                                                 waitUntilDone:NO];
   202         }
   203     }
   204 
   205     [pool release];
   206 
   207     return 0;
   208 }
   209 
   210 static void
   211 Cocoa_WarpMouse(SDL_Window * window, int x, int y)
   212 {
   213     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   214     if ([data->listener isMoving]) {
   215         DLog("Postponing warp, window being moved.");
   216         [data->listener setPendingMoveX:x
   217                                       Y:y];
   218         return;
   219     }
   220 
   221     SDL_Mouse *mouse = SDL_GetMouse();
   222     CGPoint point = CGPointMake(x + (float)window->x, y + (float)window->y);
   223 
   224     Cocoa_HandleMouseWarp(point.x, point.y);
   225 
   226     /* According to the docs, this was deprecated in 10.6, but it's still
   227      * around. The substitute requires a CGEventSource, but I'm not entirely
   228      * sure how we'd procure the right one for this event.
   229      */
   230     CGSetLocalEventsSuppressionInterval(0.0);
   231     CGWarpMouseCursorPosition(point);
   232     CGSetLocalEventsSuppressionInterval(0.25);
   233 
   234     if (!mouse->relative_mode) {
   235         /* CGWarpMouseCursorPosition doesn't generate a window event, unlike our
   236          * other implementations' APIs.
   237          */
   238         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y);
   239     }
   240 }
   241 
   242 static void
   243 Cocoa_WarpMouseGlobal(int x, int y)
   244 {
   245     SDL_Mouse *mouse = SDL_GetMouse();
   246     CGPoint point = CGPointMake((float)x, (float)y);
   247 
   248     Cocoa_HandleMouseWarp(point.x, point.y);
   249 
   250     /* According to the docs, this was deprecated in 10.6, but it's still
   251      * around. The substitute requires a CGEventSource, but I'm not entirely
   252      * sure how we'd procure the right one for this event.
   253      */
   254     CGSetLocalEventsSuppressionInterval(0.0);
   255     CGWarpMouseCursorPosition(point);
   256     CGSetLocalEventsSuppressionInterval(0.25);
   257 
   258     if (!mouse->relative_mode && mouse->focus) {
   259         /* CGWarpMouseCursorPosition doesn't generate a window event, unlike our
   260          * other implementations' APIs.
   261          */
   262         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x - mouse->focus->x, y - mouse->focus->y);
   263     }
   264 }
   265 
   266 static int
   267 Cocoa_SetRelativeMouseMode(SDL_bool enabled)
   268 {
   269     /* We will re-apply the relative mode when the window gets focus, if it
   270      * doesn't have focus right now.
   271      */
   272     SDL_Window *window = SDL_GetMouseFocus();
   273     if (!window) {
   274       return 0;
   275     }
   276 
   277     /* We will re-apply the relative mode when the window finishes being moved,
   278      * if it is being moved right now.
   279      */
   280     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   281     if ([data->listener isMoving]) {
   282         return 0;
   283     }
   284 
   285     CGError result;
   286     if (enabled) {
   287         DLog("Turning on.");
   288         result = CGAssociateMouseAndMouseCursorPosition(NO);
   289     } else {
   290         DLog("Turning off.");
   291         result = CGAssociateMouseAndMouseCursorPosition(YES);
   292     }
   293     if (result != kCGErrorSuccess) {
   294         return SDL_SetError("CGAssociateMouseAndMouseCursorPosition() failed");
   295     }
   296 
   297     /* The hide/unhide calls are redundant most of the time, but they fix
   298      * https://bugzilla.libsdl.org/show_bug.cgi?id=2550
   299      */
   300     if (enabled) {
   301         [NSCursor hide];
   302     } else {
   303         [NSCursor unhide];
   304     }
   305     return 0;
   306 }
   307 
   308 static int
   309 Cocoa_CaptureMouse(SDL_Window *window)
   310 {
   311     /* our Cocoa event code already tracks the mouse outside the window,
   312         so all we have to do here is say "okay" and do what we always do. */
   313     return 0;
   314 }
   315 
   316 static Uint32
   317 Cocoa_GetGlobalMouseState(int *x, int *y)
   318 {
   319     const NSUInteger cocoaButtons = [NSEvent pressedMouseButtons];
   320     const NSPoint cocoaLocation = [NSEvent mouseLocation];
   321     Uint32 retval = 0;
   322 
   323     for (NSScreen *screen in [NSScreen screens]) {
   324         NSRect frame = [screen frame];
   325         if (NSPointInRect(cocoaLocation, frame)) {
   326             *x = (int) cocoaLocation.x;
   327             *y = (int) ((frame.origin.y + frame.size.height) - cocoaLocation.y);
   328             break;
   329         }
   330     }
   331 
   332     retval |= (cocoaButtons & (1 << 0)) ? SDL_BUTTON_LMASK : 0;
   333     retval |= (cocoaButtons & (1 << 1)) ? SDL_BUTTON_RMASK : 0;
   334     retval |= (cocoaButtons & (1 << 2)) ? SDL_BUTTON_MMASK : 0;
   335     retval |= (cocoaButtons & (1 << 3)) ? SDL_BUTTON_X1MASK : 0;
   336     retval |= (cocoaButtons & (1 << 4)) ? SDL_BUTTON_X2MASK : 0;
   337 
   338     return retval;
   339 }
   340 
   341 void
   342 Cocoa_InitMouse(_THIS)
   343 {
   344     SDL_Mouse *mouse = SDL_GetMouse();
   345 
   346     mouse->driverdata = SDL_calloc(1, sizeof(SDL_MouseData));
   347 
   348     mouse->CreateCursor = Cocoa_CreateCursor;
   349     mouse->CreateSystemCursor = Cocoa_CreateSystemCursor;
   350     mouse->ShowCursor = Cocoa_ShowCursor;
   351     mouse->FreeCursor = Cocoa_FreeCursor;
   352     mouse->WarpMouse = Cocoa_WarpMouse;
   353     mouse->WarpMouseGlobal = Cocoa_WarpMouseGlobal;
   354     mouse->SetRelativeMouseMode = Cocoa_SetRelativeMouseMode;
   355     mouse->CaptureMouse = Cocoa_CaptureMouse;
   356     mouse->GetGlobalMouseState = Cocoa_GetGlobalMouseState;
   357 
   358     SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor());
   359 
   360     Cocoa_InitMouseEventTap(mouse->driverdata);
   361 
   362     SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
   363     const NSPoint location =  [NSEvent mouseLocation];
   364     driverdata->lastMoveX = location.x;
   365     driverdata->lastMoveY = location.y;
   366 }
   367 
   368 void
   369 Cocoa_HandleMouseEvent(_THIS, NSEvent *event)
   370 {
   371     switch ([event type]) {
   372         case NSMouseMoved:
   373         case NSLeftMouseDragged:
   374         case NSRightMouseDragged:
   375         case NSOtherMouseDragged:
   376             break;
   377 
   378         default:
   379             /* Ignore any other events. */
   380             return;
   381     }
   382 
   383     SDL_Mouse *mouse = SDL_GetMouse();
   384     SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
   385     if (!driverdata) {
   386         return;  /* can happen when returning from fullscreen Space on shutdown */
   387     }
   388 
   389     const SDL_bool seenWarp = driverdata->seenWarp;
   390     driverdata->seenWarp = NO;
   391 
   392     const NSPoint location =  [NSEvent mouseLocation];
   393     const CGFloat lastMoveX = driverdata->lastMoveX;
   394     const CGFloat lastMoveY = driverdata->lastMoveY;
   395     driverdata->lastMoveX = location.x;
   396     driverdata->lastMoveY = location.y;
   397     DLog("Last seen mouse: (%g, %g)", location.x, location.y);
   398 
   399     /* Non-relative movement is handled in -[Cocoa_WindowListener mouseMoved:] */
   400     if (!mouse->relative_mode) {
   401         return;
   402     }
   403 
   404     /* Ignore events that aren't inside the client area (i.e. title bar.) */
   405     if ([event window]) {
   406         NSRect windowRect = [[[event window] contentView] frame];
   407         if (!NSPointInRect([event locationInWindow], windowRect)) {
   408             return;
   409         }
   410     }
   411 
   412     float deltaX = [event deltaX];
   413     float deltaY = [event deltaY];
   414 
   415     if (seenWarp) {
   416         deltaX += (lastMoveX - driverdata->lastWarpX);
   417         deltaY += ((CGDisplayPixelsHigh(kCGDirectMainDisplay) - lastMoveY) - driverdata->lastWarpY);
   418 
   419         DLog("Motion was (%g, %g), offset to (%g, %g)", [event deltaX], [event deltaY], deltaX, deltaY);
   420     }
   421 
   422     SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int)deltaX, (int)deltaY);
   423 }
   424 
   425 void
   426 Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event)
   427 {
   428     SDL_Mouse *mouse = SDL_GetMouse();
   429 
   430     float x = -[event deltaX];
   431     float y = [event deltaY];
   432 
   433     if (x > 0) {
   434         x += 0.9f;
   435     } else if (x < 0) {
   436         x -= 0.9f;
   437     }
   438     if (y > 0) {
   439         y += 0.9f;
   440     } else if (y < 0) {
   441         y -= 0.9f;
   442     }
   443     SDL_SendMouseWheel(window, mouse->mouseID, (int)x, (int)y);
   444 }
   445 
   446 void
   447 Cocoa_HandleMouseWarp(CGFloat x, CGFloat y)
   448 {
   449     /* This makes Cocoa_HandleMouseEvent ignore the delta caused by the warp,
   450      * since it gets included in the next movement event.
   451      */
   452     SDL_MouseData *driverdata = (SDL_MouseData*)SDL_GetMouse()->driverdata;
   453     driverdata->lastWarpX = x;
   454     driverdata->lastWarpY = y;
   455     driverdata->seenWarp = SDL_TRUE;
   456 
   457     DLog("(%g, %g)", x, y);
   458 }
   459 
   460 void
   461 Cocoa_QuitMouse(_THIS)
   462 {
   463     SDL_Mouse *mouse = SDL_GetMouse();
   464     if (mouse) {
   465         if (mouse->driverdata) {
   466             Cocoa_QuitMouseEventTap(((SDL_MouseData*)mouse->driverdata));
   467         }
   468 
   469         SDL_free(mouse->driverdata);
   470     }
   471 }
   472 
   473 #endif /* SDL_VIDEO_DRIVER_COCOA */
   474 
   475 /* vi: set ts=4 sw=4 expandtab: */