src/video/cocoa/SDL_cocoamouse.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Tue, 23 Apr 2013 18:47:44 -0700
changeset 7100 ffd910de700b
parent 7099 54f187c036e2
child 7106 31f8acac196b
permissions -rw-r--r--
Mac: Fix cursor not updating when re-focusing the window.

This fixes a bug where [NSCursor set] doesn't take when called in
certain event handlers (like windowDidBecomeKey:).

http://bugzilla.libsdl.org/show_bug.cgi?id=1795
     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_cocoavideo.h"
    28 
    29 #include "../../events/SDL_mouse_c.h"
    30 
    31 
    32 static SDL_Cursor *
    33 Cocoa_CreateDefaultCursor()
    34 {
    35     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    36     NSCursor *nscursor;
    37     SDL_Cursor *cursor = NULL;
    38 
    39     nscursor = [NSCursor arrowCursor];
    40 
    41     if (nscursor) {
    42         cursor = SDL_calloc(1, sizeof(*cursor));
    43         if (cursor) {
    44             cursor->driverdata = nscursor;
    45             [nscursor retain];
    46         }
    47     }
    48 
    49     [pool release];
    50 
    51     return cursor;
    52 }
    53 
    54 static SDL_Cursor *
    55 Cocoa_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
    56 {
    57     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    58     NSImage *nsimage;
    59     NSCursor *nscursor = NULL;
    60     SDL_Cursor *cursor = NULL;
    61 
    62     nsimage = Cocoa_CreateImage(surface);
    63     if (nsimage) {
    64         nscursor = [[NSCursor alloc] initWithImage: nsimage hotSpot: NSMakePoint(hot_x, hot_y)];
    65     }
    66 
    67     if (nscursor) {
    68         cursor = SDL_calloc(1, sizeof(*cursor));
    69         if (cursor) {
    70             cursor->driverdata = nscursor;
    71         }
    72     }
    73 
    74     [pool release];
    75 
    76     return cursor;
    77 }
    78 
    79 static SDL_Cursor *
    80 Cocoa_CreateSystemCursor(SDL_SystemCursor id)
    81 {
    82     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    83     NSCursor *nscursor = NULL;
    84     SDL_Cursor *cursor = NULL;
    85 
    86     switch(id)
    87     {
    88     case SDL_SYSTEM_CURSOR_ARROW:
    89         nscursor = [NSCursor arrowCursor];
    90         break;
    91     case SDL_SYSTEM_CURSOR_IBEAM:
    92         nscursor = [NSCursor IBeamCursor];
    93         break;
    94     case SDL_SYSTEM_CURSOR_WAIT:
    95         nscursor = [NSCursor arrowCursor];
    96         break;
    97     case SDL_SYSTEM_CURSOR_CROSSHAIR:
    98         nscursor = [NSCursor crosshairCursor];
    99         break;
   100     case SDL_SYSTEM_CURSOR_WAITARROW:
   101         nscursor = [NSCursor arrowCursor];
   102         break;
   103     case SDL_SYSTEM_CURSOR_SIZENWSE:
   104     case SDL_SYSTEM_CURSOR_SIZENESW:
   105         nscursor = [NSCursor closedHandCursor];
   106         break;
   107     case SDL_SYSTEM_CURSOR_SIZEWE:
   108         nscursor = [NSCursor resizeLeftRightCursor];
   109         break;
   110     case SDL_SYSTEM_CURSOR_SIZENS:
   111         nscursor = [NSCursor resizeUpDownCursor];
   112         break;
   113     case SDL_SYSTEM_CURSOR_SIZEALL:
   114         nscursor = [NSCursor closedHandCursor];
   115         break;
   116     case SDL_SYSTEM_CURSOR_NO:
   117         nscursor = [NSCursor operationNotAllowedCursor];
   118         break;
   119     case SDL_SYSTEM_CURSOR_HAND:
   120         nscursor = [NSCursor pointingHandCursor];
   121         break;
   122     default:
   123         SDL_assert(!"Unknown system cursor");
   124         return NULL;
   125     }
   126 
   127     if (nscursor) {
   128         cursor = SDL_calloc(1, sizeof(*cursor));
   129         if (cursor) {
   130             // We'll free it later, so retain it here
   131             [nscursor retain];
   132             cursor->driverdata = nscursor;
   133         }
   134     }
   135 
   136     [pool release];
   137 
   138     return cursor;
   139 }
   140 
   141 static void
   142 Cocoa_FreeCursor(SDL_Cursor * cursor)
   143 {
   144     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   145     NSCursor *nscursor = (NSCursor *)cursor->driverdata;
   146 
   147     [nscursor release];
   148     SDL_free(cursor);
   149 
   150     [pool release];
   151 }
   152 
   153 static int
   154 Cocoa_ShowCursor(SDL_Cursor * cursor)
   155 {
   156 	/* We need to track the previous state because hide and unhide calls need to
   157 	 * be matched, but ShowCursor calls don't.
   158 	 */
   159 	static SDL_bool isShown = SDL_TRUE;
   160     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   161 
   162     if (cursor) {
   163         NSCursor *nscursor = (NSCursor *)cursor->driverdata;
   164 
   165         /* We're possibly executing from an event handler where this operation
   166          * is unsupported. This will execute it in the main Cocoa event loop
   167          * after this returns.
   168          */
   169         [nscursor performSelectorOnMainThread:@selector(set)
   170                                    withObject:nil
   171                                 waitUntilDone:NO];
   172 
   173 		if (!isShown) {
   174 			[NSCursor unhide];
   175 			isShown = SDL_TRUE;
   176 		}
   177 	} else if (isShown) {
   178 		[NSCursor hide];
   179 		isShown = SDL_FALSE;
   180     }
   181 
   182     [pool release];
   183 
   184     return 0;
   185 }
   186 
   187 static void
   188 Cocoa_WarpMouse(SDL_Window * window, int x, int y)
   189 {
   190     CGPoint point;
   191 
   192     point.x = (float)window->x + x;
   193     point.y = (float)window->y + y;
   194     CGWarpMouseCursorPosition(point);
   195 }
   196 
   197 static int
   198 Cocoa_SetRelativeMouseMode(SDL_bool enabled)
   199 {
   200     CGError result;
   201 
   202     if (enabled) {
   203         result = CGAssociateMouseAndMouseCursorPosition(NO);
   204     } else {
   205         result = CGAssociateMouseAndMouseCursorPosition(YES);
   206     }
   207     if (result != kCGErrorSuccess) {
   208         return SDL_SetError("CGAssociateMouseAndMouseCursorPosition() failed");
   209     }
   210     return 0;
   211 }
   212 
   213 void
   214 Cocoa_InitMouse(_THIS)
   215 {
   216     SDL_Mouse *mouse = SDL_GetMouse();
   217 
   218     mouse->CreateCursor = Cocoa_CreateCursor;
   219     mouse->CreateSystemCursor = Cocoa_CreateSystemCursor;
   220     mouse->ShowCursor = Cocoa_ShowCursor;
   221     mouse->FreeCursor = Cocoa_FreeCursor;
   222     mouse->WarpMouse = Cocoa_WarpMouse;
   223     mouse->SetRelativeMouseMode = Cocoa_SetRelativeMouseMode;
   224 
   225     SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor());
   226 }
   227 
   228 void
   229 Cocoa_HandleMouseEvent(_THIS, NSEvent *event)
   230 {
   231     SDL_Mouse *mouse = SDL_GetMouse();
   232 
   233     if (mouse->relative_mode &&
   234         ([event type] == NSMouseMoved ||
   235          [event type] == NSLeftMouseDragged ||
   236          [event type] == NSRightMouseDragged ||
   237          [event type] == NSOtherMouseDragged)) {
   238         float x = [event deltaX];
   239         float y = [event deltaY];
   240         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int)x, (int)y);
   241     }
   242 }
   243 
   244 void
   245 Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event)
   246 {
   247     SDL_Mouse *mouse = SDL_GetMouse();
   248 
   249     float x = [event deltaX];
   250     float y = [event deltaY];
   251 
   252     if (x > 0) {
   253         x += 0.9f;
   254     } else if (x < 0) {
   255         x -= 0.9f;
   256     }
   257     if (y > 0) {
   258         y += 0.9f;
   259     } else if (y < 0) {
   260         y -= 0.9f;
   261     }
   262     SDL_SendMouseWheel(window, mouse->mouseID, (int)x, (int)y);
   263 }
   264 
   265 void
   266 Cocoa_QuitMouse(_THIS)
   267 {
   268 }
   269 
   270 #endif /* SDL_VIDEO_DRIVER_COCOA */
   271 
   272 /* vi: set ts=4 sw=4 expandtab: */