SDL  2.0
SDL_cocoamouse.m
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2016 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 { @autoreleasepool
70 {
71  NSCursor *nscursor;
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  return cursor;
85 }}
86 
87 static SDL_Cursor *
88 Cocoa_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
89 { @autoreleasepool
90 {
91  NSImage *nsimage;
92  NSCursor *nscursor = NULL;
93  SDL_Cursor *cursor = NULL;
94 
95  nsimage = Cocoa_CreateImage(surface);
96  if (nsimage) {
97  nscursor = [[NSCursor alloc] initWithImage: nsimage hotSpot: NSMakePoint(hot_x, hot_y)];
98  }
99 
100  if (nscursor) {
101  cursor = SDL_calloc(1, sizeof(*cursor));
102  if (cursor) {
103  cursor->driverdata = nscursor;
104  } else {
105  [nscursor release];
106  }
107  }
108 
109  return cursor;
110 }}
111 
112 static SDL_Cursor *
113 Cocoa_CreateSystemCursor(SDL_SystemCursor id)
114 { @autoreleasepool
115 {
116  NSCursor *nscursor = NULL;
117  SDL_Cursor *cursor = NULL;
118 
119  switch(id) {
121  nscursor = [NSCursor arrowCursor];
122  break;
124  nscursor = [NSCursor IBeamCursor];
125  break;
127  nscursor = [NSCursor arrowCursor];
128  break;
130  nscursor = [NSCursor crosshairCursor];
131  break;
133  nscursor = [NSCursor arrowCursor];
134  break;
137  nscursor = [NSCursor closedHandCursor];
138  break;
140  nscursor = [NSCursor resizeLeftRightCursor];
141  break;
143  nscursor = [NSCursor resizeUpDownCursor];
144  break;
146  nscursor = [NSCursor closedHandCursor];
147  break;
149  nscursor = [NSCursor operationNotAllowedCursor];
150  break;
152  nscursor = [NSCursor pointingHandCursor];
153  break;
154  default:
155  SDL_assert(!"Unknown system cursor");
156  return NULL;
157  }
158 
159  if (nscursor) {
160  cursor = SDL_calloc(1, sizeof(*cursor));
161  if (cursor) {
162  /* We'll free it later, so retain it here */
163  [nscursor retain];
164  cursor->driverdata = nscursor;
165  }
166  }
167 
168  return cursor;
169 }}
170 
171 static void
172 Cocoa_FreeCursor(SDL_Cursor * cursor)
173 { @autoreleasepool
174 {
175  NSCursor *nscursor = (NSCursor *)cursor->driverdata;
176 
177  [nscursor release];
178  SDL_free(cursor);
179 }}
180 
181 static int
182 Cocoa_ShowCursor(SDL_Cursor * cursor)
183 { @autoreleasepool
184 {
186  SDL_Window *window = (device ? device->windows : NULL);
187  for (; window != NULL; window = window->next) {
188  SDL_WindowData *driverdata = (SDL_WindowData *)window->driverdata;
189  if (driverdata) {
190  [driverdata->nswindow performSelectorOnMainThread:@selector(invalidateCursorRectsForView:)
191  withObject:[driverdata->nswindow contentView]
192  waitUntilDone:NO];
193  }
194  }
195  return 0;
196 }}
197 
198 static SDL_Window *
199 SDL_FindWindowAtPoint(const int x, const int y)
200 {
201  const SDL_Point pt = { x, y };
202  SDL_Window *i;
203  for (i = SDL_GetVideoDevice()->windows; i; i = i->next) {
204  const SDL_Rect r = { i->x, i->y, i->w, i->h };
205  if (SDL_PointInRect(&pt, &r)) {
206  return i;
207  }
208  }
209 
210  return NULL;
211 }
212 
213 static int
214 Cocoa_WarpMouseGlobal(int x, int y)
215 {
216  SDL_Mouse *mouse = SDL_GetMouse();
217  if (mouse->focus) {
219  if ([data->listener isMoving]) {
220  DLog("Postponing warp, window being moved.");
221  [data->listener setPendingMoveX:x Y:y];
222  return 0;
223  }
224  }
225  const CGPoint point = CGPointMake((float)x, (float)y);
226 
227  Cocoa_HandleMouseWarp(point.x, point.y);
228 
229  CGWarpMouseCursorPosition(point);
230 
231  /* CGWarpMouse causes a short delay by default, which is preventable by
232  * Calling this directly after. CGSetLocalEventsSuppressionInterval can also
233  * prevent it, but it's deprecated as of OS X 10.6.
234  */
235  if (!mouse->relative_mode) {
236  CGAssociateMouseAndMouseCursorPosition(YES);
237  }
238 
239  /* CGWarpMouseCursorPosition doesn't generate a window event, unlike our
240  * other implementations' APIs. Send what's appropriate.
241  */
242  if (!mouse->relative_mode) {
243  SDL_Window *win = SDL_FindWindowAtPoint(x, y);
244  SDL_SetMouseFocus(win);
245  if (win) {
246  SDL_assert(win == mouse->focus);
247  SDL_SendMouseMotion(win, mouse->mouseID, 0, x - win->x, y - win->y);
248  }
249  }
250 
251  return 0;
252 }
253 
254 static void
255 Cocoa_WarpMouse(SDL_Window * window, int x, int y)
256 {
257  Cocoa_WarpMouseGlobal(x + window->x, y + window->y);
258 }
259 
260 static int
261 Cocoa_SetRelativeMouseMode(SDL_bool enabled)
262 {
263  /* We will re-apply the relative mode when the window gets focus, if it
264  * doesn't have focus right now.
265  */
266  SDL_Window *window = SDL_GetMouseFocus();
267  if (!window) {
268  return 0;
269  }
270 
271  /* We will re-apply the relative mode when the window finishes being moved,
272  * if it is being moved right now.
273  */
274  SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
275  if ([data->listener isMoving]) {
276  return 0;
277  }
278 
279  CGError result;
280  if (enabled) {
281  DLog("Turning on.");
282  result = CGAssociateMouseAndMouseCursorPosition(NO);
283  } else {
284  DLog("Turning off.");
285  result = CGAssociateMouseAndMouseCursorPosition(YES);
286  }
287  if (result != kCGErrorSuccess) {
288  return SDL_SetError("CGAssociateMouseAndMouseCursorPosition() failed");
289  }
290 
291  /* The hide/unhide calls are redundant most of the time, but they fix
292  * https://bugzilla.libsdl.org/show_bug.cgi?id=2550
293  */
294  if (enabled) {
295  [NSCursor hide];
296  } else {
297  [NSCursor unhide];
298  }
299  return 0;
300 }
301 
302 static int
303 Cocoa_CaptureMouse(SDL_Window *window)
304 {
305  /* our Cocoa event code already tracks the mouse outside the window,
306  so all we have to do here is say "okay" and do what we always do. */
307  return 0;
308 }
309 
310 static Uint32
311 Cocoa_GetGlobalMouseState(int *x, int *y)
312 {
313  const NSUInteger cocoaButtons = [NSEvent pressedMouseButtons];
314  const NSPoint cocoaLocation = [NSEvent mouseLocation];
315  Uint32 retval = 0;
316 
317  for (NSScreen *screen in [NSScreen screens]) {
318  NSRect frame = [screen frame];
319  if (NSMouseInRect(cocoaLocation, frame, NO)) {
320  *x = (int) cocoaLocation.x;
321  *y = (int) ((frame.origin.y + frame.size.height) - cocoaLocation.y);
322  break;
323  }
324  }
325 
326  retval |= (cocoaButtons & (1 << 0)) ? SDL_BUTTON_LMASK : 0;
327  retval |= (cocoaButtons & (1 << 1)) ? SDL_BUTTON_RMASK : 0;
328  retval |= (cocoaButtons & (1 << 2)) ? SDL_BUTTON_MMASK : 0;
329  retval |= (cocoaButtons & (1 << 3)) ? SDL_BUTTON_X1MASK : 0;
330  retval |= (cocoaButtons & (1 << 4)) ? SDL_BUTTON_X2MASK : 0;
331 
332  return retval;
333 }
334 
335 void
337 {
338  SDL_Mouse *mouse = SDL_GetMouse();
339 
340  mouse->driverdata = SDL_calloc(1, sizeof(SDL_MouseData));
341 
342  mouse->CreateCursor = Cocoa_CreateCursor;
343  mouse->CreateSystemCursor = Cocoa_CreateSystemCursor;
344  mouse->ShowCursor = Cocoa_ShowCursor;
345  mouse->FreeCursor = Cocoa_FreeCursor;
346  mouse->WarpMouse = Cocoa_WarpMouse;
347  mouse->WarpMouseGlobal = Cocoa_WarpMouseGlobal;
348  mouse->SetRelativeMouseMode = Cocoa_SetRelativeMouseMode;
349  mouse->CaptureMouse = Cocoa_CaptureMouse;
350  mouse->GetGlobalMouseState = Cocoa_GetGlobalMouseState;
351 
352  SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor());
353 
355 
356  SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
357  const NSPoint location = [NSEvent mouseLocation];
358  driverdata->lastMoveX = location.x;
359  driverdata->lastMoveY = location.y;
360 }
361 
362 void
364 {
365  switch ([event type]) {
366  case NSMouseMoved:
367  case NSLeftMouseDragged:
368  case NSRightMouseDragged:
369  case NSOtherMouseDragged:
370  break;
371 
372  default:
373  /* Ignore any other events. */
374  return;
375  }
376 
377  SDL_Mouse *mouse = SDL_GetMouse();
378  SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
379  if (!driverdata) {
380  return; /* can happen when returning from fullscreen Space on shutdown */
381  }
382 
383  const SDL_bool seenWarp = driverdata->seenWarp;
384  driverdata->seenWarp = NO;
385 
386  const NSPoint location = [NSEvent mouseLocation];
387  const CGFloat lastMoveX = driverdata->lastMoveX;
388  const CGFloat lastMoveY = driverdata->lastMoveY;
389  driverdata->lastMoveX = location.x;
390  driverdata->lastMoveY = location.y;
391  DLog("Last seen mouse: (%g, %g)", location.x, location.y);
392 
393  /* Non-relative movement is handled in -[Cocoa_WindowListener mouseMoved:] */
394  if (!mouse->relative_mode) {
395  return;
396  }
397 
398  /* Ignore events that aren't inside the client area (i.e. title bar.) */
399  if ([event window]) {
400  NSRect windowRect = [[[event window] contentView] frame];
401  if (!NSMouseInRect([event locationInWindow], windowRect, NO)) {
402  return;
403  }
404  }
405 
406  float deltaX = [event deltaX];
407  float deltaY = [event deltaY];
408 
409  if (seenWarp) {
410  deltaX += (lastMoveX - driverdata->lastWarpX);
411  deltaY += ((CGDisplayPixelsHigh(kCGDirectMainDisplay) - lastMoveY) - driverdata->lastWarpY);
412 
413  DLog("Motion was (%g, %g), offset to (%g, %g)", [event deltaX], [event deltaY], deltaX, deltaY);
414  }
415 
416  SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int)deltaX, (int)deltaY);
417 }
418 
419 void
420 Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event)
421 {
422  SDL_Mouse *mouse = SDL_GetMouse();
423 
424  CGFloat x = -[event deltaX];
425  CGFloat y = [event deltaY];
427 
428  if ([event respondsToSelector:@selector(isDirectionInvertedFromDevice)]) {
429  if ([event isDirectionInvertedFromDevice] == YES) {
430  direction = SDL_MOUSEWHEEL_FLIPPED;
431  }
432  }
433 
434  if (x > 0) {
435  x = SDL_ceil(x);
436  } else if (x < 0) {
437  x = SDL_floor(x);
438  }
439  if (y > 0) {
440  y = SDL_ceil(y);
441  } else if (y < 0) {
442  y = SDL_floor(y);
443  }
444  SDL_SendMouseWheel(window, mouse->mouseID, (int)x, (int)y, direction);
445 }
446 
447 void
448 Cocoa_HandleMouseWarp(CGFloat x, CGFloat y)
449 {
450  /* This makes Cocoa_HandleMouseEvent ignore the delta caused by the warp,
451  * since it gets included in the next movement event.
452  */
454  driverdata->lastWarpX = x;
455  driverdata->lastWarpY = y;
456  driverdata->seenWarp = SDL_TRUE;
457 
458  DLog("(%g, %g)", x, y);
459 }
460 
461 void
463 {
464  SDL_Mouse *mouse = SDL_GetMouse();
465  if (mouse) {
466  if (mouse->driverdata) {
468  }
469 
470  SDL_free(mouse->driverdata);
471  }
472 }
473 
474 #endif /* SDL_VIDEO_DRIVER_COCOA */
475 
476 /* vi: set ts=4 sw=4 expandtab: */
SDL_Window * next
Definition: SDL_sysvideo.h:112
void Cocoa_InitMouse(_THIS)
SDL_Mouse * SDL_GetMouse(void)
Definition: SDL_mouse.c:66
int(* ShowCursor)(SDL_Cursor *cursor)
Definition: SDL_mouse_c.h:52
GLdouble GLdouble GLdouble r
Definition: SDL_opengl.h:2072
#define SDL_ceil
GLuint64EXT * result
#define SDL_BUTTON_RMASK
Definition: SDL_mouse.h:289
CGFloat lastMoveY
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1567
SDL_Window * focus
Definition: SDL_mouse_c.h:77
int(* SetRelativeMouseMode)(SDL_bool enabled)
Definition: SDL_mouse_c.h:67
#define SDL_BUTTON_X2MASK
Definition: SDL_mouse.h:291
static SDL_Window * window
The structure that defines a point.
Definition: SDL_rect.h:48
A collection of pixels used in software blitting.
Definition: SDL_surface.h:69
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1967
Uint32(* GetGlobalMouseState)(int *x, int *y)
Definition: SDL_mouse_c.h:73
SDL_FORCE_INLINE SDL_bool SDL_PointInRect(const SDL_Point *p, const SDL_Rect *r)
Returns true if point resides inside a rectangle.
Definition: SDL_rect.h:73
void SDL_SetMouseFocus(SDL_Window *window)
Definition: SDL_mouse.c:103
void Cocoa_HandleMouseWarp(CGFloat x, CGFloat y)
uint32_t Uint32
An unsigned 32-bit integer type.
Definition: SDL_stdinc.h:161
CGFloat lastWarpX
GLint GLint GLsizei GLsizei height
Definition: SDL_opengl.h:1565
#define SDL_floor
GLsizeiptr size
CGFloat lastWarpY
SDL_MouseID mouseID
Definition: SDL_mouse_c.h:76
int(* CaptureMouse)(SDL_Window *window)
Definition: SDL_mouse_c.h:70
SDL_bool retval
SDL_Cursor *(* CreateCursor)(SDL_Surface *surface, int hot_x, int hot_y)
Definition: SDL_mouse_c.h:46
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1564
void * SDL_calloc(size_t nmemb, size_t size)
void Cocoa_QuitMouseEventTap(SDL_MouseData *driverdata)
NSImage * Cocoa_CreateImage(SDL_Surface *surface)
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1567
int SDL_SendMouseMotion(SDL_Window *window, SDL_MouseID mouseID, int relative, int x, int y)
Definition: SDL_mouse.c:188
void Cocoa_QuitMouse(_THIS)
Cocoa_WindowListener * listener
#define _THIS
struct _cl_event * event
int frame
Definition: teststreaming.c:60
void SDL_free(void *mem)
GLint location
SDL_bool relative_mode
Definition: SDL_mouse_c.h:84
#define SDL_BUTTON_X1MASK
Definition: SDL_mouse.h:290
SDL_SystemCursor
Cursor types for SDL_CreateSystemCursor().
Definition: SDL_mouse.h:46
#define SDL_BUTTON_LMASK
Definition: SDL_mouse.h:287
void Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event)
SDL_Window * windows
Definition: SDL_sysvideo.h:294
void Cocoa_HandleMouseEvent(_THIS, NSEvent *event)
void SDL_SetDefaultCursor(SDL_Cursor *cursor)
Definition: SDL_mouse.c:55
int(* WarpMouseGlobal)(int x, int y)
Definition: SDL_mouse_c.h:64
GLenum GLenum GLsizei const GLuint GLboolean enabled
SDL_Cursor * cursor
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
void(* FreeCursor)(SDL_Cursor *cursor)
Definition: SDL_mouse_c.h:58
#define SDL_assert(condition)
Definition: SDL_assert.h:167
#define NULL
Definition: begin_code.h:143
SDL_bool
Definition: SDL_stdinc.h:130
#define SDL_GetMouseFocus
CGFloat lastMoveX
#define SDL_SetError
SDL_bool seenWarp
The type used to identify a window.
Definition: SDL_sysvideo.h:71
void(* WarpMouse)(SDL_Window *window, int x, int y)
Definition: SDL_mouse_c.h:61
SDL_VideoDevice * SDL_GetVideoDevice(void)
Definition: SDL_video.c:571
int SDL_SendMouseWheel(SDL_Window *window, SDL_MouseID mouseID, int x, int y, SDL_MouseWheelDirection direction)
Definition: SDL_mouse.c:420
void * driverdata
Definition: SDL_sysvideo.h:109
void * driverdata
Definition: SDL_mouse_c.h:97
#define SDL_BUTTON_MMASK
Definition: SDL_mouse.h:288
GLuint in
SDL_Cursor *(* CreateSystemCursor)(SDL_SystemCursor id)
Definition: SDL_mouse_c.h:49
void * driverdata
Definition: SDL_mouse_c.h:33
SDL_Renderer * screen
SDL_MouseWheelDirection
Scroll direction types for the Scroll event.
Definition: SDL_mouse.h:66
void Cocoa_InitMouseEventTap(SDL_MouseData *driverdata)
A rectangle, with the origin at the upper left.
Definition: SDL_rect.h:64