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  /* According to the docs, this was deprecated in 10.6, but it's still
230  * around. The substitute requires a CGEventSource, but I'm not entirely
231  * sure how we'd procure the right one for this event.
232  */
233  CGSetLocalEventsSuppressionInterval(0.0);
234  CGWarpMouseCursorPosition(point);
235  CGSetLocalEventsSuppressionInterval(0.25);
236 
237  /* CGWarpMouseCursorPosition doesn't generate a window event, unlike our
238  * other implementations' APIs. Send what's appropriate.
239  */
240  if (!mouse->relative_mode) {
241  SDL_Window *win = SDL_FindWindowAtPoint(x, y);
242  SDL_SetMouseFocus(win);
243  if (win) {
244  SDL_assert(win == mouse->focus);
245  SDL_SendMouseMotion(win, mouse->mouseID, 0, x - win->x, y - win->y);
246  }
247  }
248 
249  return 0;
250 }
251 
252 static void
253 Cocoa_WarpMouse(SDL_Window * window, int x, int y)
254 {
255  Cocoa_WarpMouseGlobal(x + window->x, y + window->y);
256 }
257 
258 static int
259 Cocoa_SetRelativeMouseMode(SDL_bool enabled)
260 {
261  /* We will re-apply the relative mode when the window gets focus, if it
262  * doesn't have focus right now.
263  */
264  SDL_Window *window = SDL_GetMouseFocus();
265  if (!window) {
266  return 0;
267  }
268 
269  /* We will re-apply the relative mode when the window finishes being moved,
270  * if it is being moved right now.
271  */
272  SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
273  if ([data->listener isMoving]) {
274  return 0;
275  }
276 
277  CGError result;
278  if (enabled) {
279  DLog("Turning on.");
280  result = CGAssociateMouseAndMouseCursorPosition(NO);
281  } else {
282  DLog("Turning off.");
283  result = CGAssociateMouseAndMouseCursorPosition(YES);
284  }
285  if (result != kCGErrorSuccess) {
286  return SDL_SetError("CGAssociateMouseAndMouseCursorPosition() failed");
287  }
288 
289  /* The hide/unhide calls are redundant most of the time, but they fix
290  * https://bugzilla.libsdl.org/show_bug.cgi?id=2550
291  */
292  if (enabled) {
293  [NSCursor hide];
294  } else {
295  [NSCursor unhide];
296  }
297  return 0;
298 }
299 
300 static int
301 Cocoa_CaptureMouse(SDL_Window *window)
302 {
303  /* our Cocoa event code already tracks the mouse outside the window,
304  so all we have to do here is say "okay" and do what we always do. */
305  return 0;
306 }
307 
308 static Uint32
309 Cocoa_GetGlobalMouseState(int *x, int *y)
310 {
311  const NSUInteger cocoaButtons = [NSEvent pressedMouseButtons];
312  const NSPoint cocoaLocation = [NSEvent mouseLocation];
313  Uint32 retval = 0;
314 
315  for (NSScreen *screen in [NSScreen screens]) {
316  NSRect frame = [screen frame];
317  if (NSPointInRect(cocoaLocation, frame)) {
318  *x = (int) cocoaLocation.x;
319  *y = (int) ((frame.origin.y + frame.size.height) - cocoaLocation.y);
320  break;
321  }
322  }
323 
324  retval |= (cocoaButtons & (1 << 0)) ? SDL_BUTTON_LMASK : 0;
325  retval |= (cocoaButtons & (1 << 1)) ? SDL_BUTTON_RMASK : 0;
326  retval |= (cocoaButtons & (1 << 2)) ? SDL_BUTTON_MMASK : 0;
327  retval |= (cocoaButtons & (1 << 3)) ? SDL_BUTTON_X1MASK : 0;
328  retval |= (cocoaButtons & (1 << 4)) ? SDL_BUTTON_X2MASK : 0;
329 
330  return retval;
331 }
332 
333 void
335 {
336  SDL_Mouse *mouse = SDL_GetMouse();
337 
338  mouse->driverdata = SDL_calloc(1, sizeof(SDL_MouseData));
339 
340  mouse->CreateCursor = Cocoa_CreateCursor;
341  mouse->CreateSystemCursor = Cocoa_CreateSystemCursor;
342  mouse->ShowCursor = Cocoa_ShowCursor;
343  mouse->FreeCursor = Cocoa_FreeCursor;
344  mouse->WarpMouse = Cocoa_WarpMouse;
345  mouse->WarpMouseGlobal = Cocoa_WarpMouseGlobal;
346  mouse->SetRelativeMouseMode = Cocoa_SetRelativeMouseMode;
347  mouse->CaptureMouse = Cocoa_CaptureMouse;
348  mouse->GetGlobalMouseState = Cocoa_GetGlobalMouseState;
349 
350  SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor());
351 
353 
354  SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
355  const NSPoint location = [NSEvent mouseLocation];
356  driverdata->lastMoveX = location.x;
357  driverdata->lastMoveY = location.y;
358 }
359 
360 void
362 {
363  switch ([event type]) {
364  case NSMouseMoved:
365  case NSLeftMouseDragged:
366  case NSRightMouseDragged:
367  case NSOtherMouseDragged:
368  break;
369 
370  default:
371  /* Ignore any other events. */
372  return;
373  }
374 
375  SDL_Mouse *mouse = SDL_GetMouse();
376  SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
377  if (!driverdata) {
378  return; /* can happen when returning from fullscreen Space on shutdown */
379  }
380 
381  const SDL_bool seenWarp = driverdata->seenWarp;
382  driverdata->seenWarp = NO;
383 
384  const NSPoint location = [NSEvent mouseLocation];
385  const CGFloat lastMoveX = driverdata->lastMoveX;
386  const CGFloat lastMoveY = driverdata->lastMoveY;
387  driverdata->lastMoveX = location.x;
388  driverdata->lastMoveY = location.y;
389  DLog("Last seen mouse: (%g, %g)", location.x, location.y);
390 
391  /* Non-relative movement is handled in -[Cocoa_WindowListener mouseMoved:] */
392  if (!mouse->relative_mode) {
393  return;
394  }
395 
396  /* Ignore events that aren't inside the client area (i.e. title bar.) */
397  if ([event window]) {
398  NSRect windowRect = [[[event window] contentView] frame];
399  if (!NSPointInRect([event locationInWindow], windowRect)) {
400  return;
401  }
402  }
403 
404  float deltaX = [event deltaX];
405  float deltaY = [event deltaY];
406 
407  if (seenWarp) {
408  deltaX += (lastMoveX - driverdata->lastWarpX);
409  deltaY += ((CGDisplayPixelsHigh(kCGDirectMainDisplay) - lastMoveY) - driverdata->lastWarpY);
410 
411  DLog("Motion was (%g, %g), offset to (%g, %g)", [event deltaX], [event deltaY], deltaX, deltaY);
412  }
413 
414  SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int)deltaX, (int)deltaY);
415 }
416 
417 void
418 Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event)
419 {
420  SDL_Mouse *mouse = SDL_GetMouse();
421 
422  float x = -[event deltaX];
423  float y = [event deltaY];
425 
426  if ([event respondsToSelector:@selector(isDirectionInvertedFromDevice)]) {
427  if ([event isDirectionInvertedFromDevice] == YES) {
428  direction = SDL_MOUSEWHEEL_FLIPPED;
429  }
430  }
431 
432  if (x > 0) {
433  x += 0.9f;
434  } else if (x < 0) {
435  x -= 0.9f;
436  }
437  if (y > 0) {
438  y += 0.9f;
439  } else if (y < 0) {
440  y -= 0.9f;
441  }
442  SDL_SendMouseWheel(window, mouse->mouseID, (int)x, (int)y, direction);
443 }
444 
445 void
446 Cocoa_HandleMouseWarp(CGFloat x, CGFloat y)
447 {
448  /* This makes Cocoa_HandleMouseEvent ignore the delta caused by the warp,
449  * since it gets included in the next movement event.
450  */
452  driverdata->lastWarpX = x;
453  driverdata->lastWarpY = y;
454  driverdata->seenWarp = SDL_TRUE;
455 
456  DLog("(%g, %g)", x, y);
457 }
458 
459 void
461 {
462  SDL_Mouse *mouse = SDL_GetMouse();
463  if (mouse) {
464  if (mouse->driverdata) {
466  }
467 
468  SDL_free(mouse->driverdata);
469  }
470 }
471 
472 #endif /* SDL_VIDEO_DRIVER_COCOA */
473 
474 /* vi: set ts=4 sw=4 expandtab: */
SDL_Window * next
Definition: SDL_sysvideo.h:109
void Cocoa_InitMouse(_THIS)
GLdouble GLdouble GLdouble r
Definition: SDL_opengl.h:2072
SDL_Mouse * SDL_GetMouse(void)
Definition: SDL_mouse.c:66
int(* ShowCursor)(SDL_Cursor *cursor)
Definition: SDL_mouse_c.h:52
GLuint64EXT * result
uint32_t Uint32
An unsigned 32-bit integer type.
Definition: SDL_stdinc.h:155
#define SDL_BUTTON_RMASK
Definition: SDL_mouse.h:287
CGFloat lastMoveY
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:289
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
GLfloat f
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)
CGFloat lastWarpX
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1567
GLsizeiptr size
CGFloat lastWarpY
SDL_MouseID mouseID
Definition: SDL_mouse_c.h:76
SDL_bool
Definition: SDL_stdinc.h:126
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
void * SDL_calloc(size_t nmemb, size_t size)
void Cocoa_QuitMouseEventTap(SDL_MouseData *driverdata)
GLint GLint GLsizei GLsizei height
Definition: SDL_opengl.h:1565
NSImage * Cocoa_CreateImage(SDL_Surface *surface)
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:288
SDL_SystemCursor
Cursor types for SDL_CreateSystemCursor.
Definition: SDL_mouse.h:46
#define SDL_BUTTON_LMASK
Definition: SDL_mouse.h:285
void Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event)
SDL_Window * windows
Definition: SDL_sysvideo.h:281
SDL_Cursor * cursor
Definition: testwm2.c:40
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
void(* FreeCursor)(SDL_Cursor *cursor)
Definition: SDL_mouse_c.h:58
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:42
#define SDL_assert(condition)
Definition: SDL_assert.h:167
#define NULL
Definition: begin_code.h:143
#define SDL_GetMouseFocus
CGFloat lastMoveX
#define SDL_SetError
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1567
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:573
int SDL_SendMouseWheel(SDL_Window *window, SDL_MouseID mouseID, int x, int y, SDL_MouseWheelDirection direction)
Definition: SDL_mouse.c:406
void * driverdata
Definition: SDL_sysvideo.h:106
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1564
void * driverdata
Definition: SDL_mouse_c.h:97
#define SDL_BUTTON_MMASK
Definition: SDL_mouse.h:286
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