SDL  2.0
SDL_cocoamouse.m
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2019 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 #include "SDL_cocoavideo.h"
30 
31 #include "../../events/SDL_mouse_c.h"
32 
33 /* #define DEBUG_COCOAMOUSE */
34 
35 #ifdef DEBUG_COCOAMOUSE
36 #define DLog(fmt, ...) printf("%s: " fmt "\n", __func__, ##__VA_ARGS__)
37 #else
38 #define DLog(...) do { } while (0)
39 #endif
40 
41 @implementation NSCursor (InvisibleCursor)
42 + (NSCursor *)invisibleCursor
43 {
44  static NSCursor *invisibleCursor = NULL;
45  if (!invisibleCursor) {
46  /* RAW 16x16 transparent GIF */
47  static unsigned char cursorBytes[] = {
48  0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x10, 0x00, 0x10, 0x00, 0x80,
49  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04,
50  0x01, 0x00, 0x00, 0x01, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x10,
51  0x00, 0x10, 0x00, 0x00, 0x02, 0x0E, 0x8C, 0x8F, 0xA9, 0xCB, 0xED,
52  0x0F, 0xA3, 0x9C, 0xB4, 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B
53  };
54 
55  NSData *cursorData = [NSData dataWithBytesNoCopy:&cursorBytes[0]
56  length:sizeof(cursorBytes)
57  freeWhenDone:NO];
58  NSImage *cursorImage = [[[NSImage alloc] initWithData:cursorData] autorelease];
59  invisibleCursor = [[NSCursor alloc] initWithImage:cursorImage
60  hotSpot:NSZeroPoint];
61  }
62 
63  return invisibleCursor;
64 }
65 @end
66 
67 
68 static SDL_Cursor *
69 Cocoa_CreateDefaultCursor()
70 { @autoreleasepool
71 {
72  NSCursor *nscursor;
74 
75  nscursor = [NSCursor arrowCursor];
76 
77  if (nscursor) {
78  cursor = SDL_calloc(1, sizeof(*cursor));
79  if (cursor) {
80  cursor->driverdata = nscursor;
81  [nscursor retain];
82  }
83  }
84 
85  return cursor;
86 }}
87 
88 static SDL_Cursor *
89 Cocoa_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
90 { @autoreleasepool
91 {
92  NSImage *nsimage;
93  NSCursor *nscursor = NULL;
94  SDL_Cursor *cursor = NULL;
95 
96  nsimage = Cocoa_CreateImage(surface);
97  if (nsimage) {
98  nscursor = [[NSCursor alloc] initWithImage: nsimage hotSpot: NSMakePoint(hot_x, hot_y)];
99  }
100 
101  if (nscursor) {
102  cursor = SDL_calloc(1, sizeof(*cursor));
103  if (cursor) {
104  cursor->driverdata = nscursor;
105  } else {
106  [nscursor release];
107  }
108  }
109 
110  return cursor;
111 }}
112 
113 static SDL_Cursor *
114 Cocoa_CreateSystemCursor(SDL_SystemCursor id)
115 { @autoreleasepool
116 {
117  NSCursor *nscursor = NULL;
118  SDL_Cursor *cursor = NULL;
119 
120  switch(id) {
122  nscursor = [NSCursor arrowCursor];
123  break;
125  nscursor = [NSCursor IBeamCursor];
126  break;
128  nscursor = [NSCursor arrowCursor];
129  break;
131  nscursor = [NSCursor crosshairCursor];
132  break;
134  nscursor = [NSCursor arrowCursor];
135  break;
138  nscursor = [NSCursor closedHandCursor];
139  break;
141  nscursor = [NSCursor resizeLeftRightCursor];
142  break;
144  nscursor = [NSCursor resizeUpDownCursor];
145  break;
147  nscursor = [NSCursor closedHandCursor];
148  break;
150  nscursor = [NSCursor operationNotAllowedCursor];
151  break;
153  nscursor = [NSCursor pointingHandCursor];
154  break;
155  default:
156  SDL_assert(!"Unknown system cursor");
157  return NULL;
158  }
159 
160  if (nscursor) {
161  cursor = SDL_calloc(1, sizeof(*cursor));
162  if (cursor) {
163  /* We'll free it later, so retain it here */
164  [nscursor retain];
165  cursor->driverdata = nscursor;
166  }
167  }
168 
169  return cursor;
170 }}
171 
172 static void
173 Cocoa_FreeCursor(SDL_Cursor * cursor)
174 { @autoreleasepool
175 {
176  NSCursor *nscursor = (NSCursor *)cursor->driverdata;
177 
178  [nscursor release];
179  SDL_free(cursor);
180 }}
181 
182 static int
183 Cocoa_ShowCursor(SDL_Cursor * cursor)
184 { @autoreleasepool
185 {
187  SDL_Window *window = (device ? device->windows : NULL);
188  for (; window != NULL; window = window->next) {
189  SDL_WindowData *driverdata = (SDL_WindowData *)window->driverdata;
190  if (driverdata) {
191  [driverdata->nswindow performSelectorOnMainThread:@selector(invalidateCursorRectsForView:)
192  withObject:[driverdata->nswindow contentView]
193  waitUntilDone:NO];
194  }
195  }
196  return 0;
197 }}
198 
199 static SDL_Window *
200 SDL_FindWindowAtPoint(const int x, const int y)
201 {
202  const SDL_Point pt = { x, y };
203  SDL_Window *i;
204  for (i = SDL_GetVideoDevice()->windows; i; i = i->next) {
205  const SDL_Rect r = { i->x, i->y, i->w, i->h };
206  if (SDL_PointInRect(&pt, &r)) {
207  return i;
208  }
209  }
210 
211  return NULL;
212 }
213 
214 static int
215 Cocoa_WarpMouseGlobal(int x, int y)
216 {
217  SDL_Mouse *mouse = SDL_GetMouse();
218  if (mouse->focus) {
220  if ([data->listener isMoving]) {
221  DLog("Postponing warp, window being moved.");
222  [data->listener setPendingMoveX:x Y:y];
223  return 0;
224  }
225  }
226  const CGPoint point = CGPointMake((float)x, (float)y);
227 
228  Cocoa_HandleMouseWarp(point.x, point.y);
229 
230  CGWarpMouseCursorPosition(point);
231 
232  /* CGWarpMouse causes a short delay by default, which is preventable by
233  * Calling this directly after. CGSetLocalEventsSuppressionInterval can also
234  * prevent it, but it's deprecated as of OS X 10.6.
235  */
236  if (!mouse->relative_mode) {
237  CGAssociateMouseAndMouseCursorPosition(YES);
238  }
239 
240  /* CGWarpMouseCursorPosition doesn't generate a window event, unlike our
241  * other implementations' APIs. Send what's appropriate.
242  */
243  if (!mouse->relative_mode) {
244  SDL_Window *win = SDL_FindWindowAtPoint(x, y);
245  SDL_SetMouseFocus(win);
246  if (win) {
247  SDL_assert(win == mouse->focus);
248  SDL_SendMouseMotion(win, mouse->mouseID, 0, x - win->x, y - win->y);
249  }
250  }
251 
252  return 0;
253 }
254 
255 static void
256 Cocoa_WarpMouse(SDL_Window * window, int x, int y)
257 {
258  Cocoa_WarpMouseGlobal(x + window->x, y + window->y);
259 }
260 
261 static int
262 Cocoa_SetRelativeMouseMode(SDL_bool enabled)
263 {
264  /* We will re-apply the relative mode when the window gets focus, if it
265  * doesn't have focus right now.
266  */
267  SDL_Window *window = SDL_GetMouseFocus();
268  if (!window) {
269  return 0;
270  }
271 
272  /* We will re-apply the relative mode when the window finishes being moved,
273  * if it is being moved right now.
274  */
275  SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
276  if ([data->listener isMoving]) {
277  return 0;
278  }
279 
280  CGError result;
281  if (enabled) {
282  DLog("Turning on.");
283  result = CGAssociateMouseAndMouseCursorPosition(NO);
284  } else {
285  DLog("Turning off.");
286  result = CGAssociateMouseAndMouseCursorPosition(YES);
287  }
288  if (result != kCGErrorSuccess) {
289  return SDL_SetError("CGAssociateMouseAndMouseCursorPosition() failed");
290  }
291 
292  /* The hide/unhide calls are redundant most of the time, but they fix
293  * https://bugzilla.libsdl.org/show_bug.cgi?id=2550
294  */
295  if (enabled) {
296  [NSCursor hide];
297  } else {
298  [NSCursor unhide];
299  }
300  return 0;
301 }
302 
303 static int
304 Cocoa_CaptureMouse(SDL_Window *window)
305 {
306  /* our Cocoa event code already tracks the mouse outside the window,
307  so all we have to do here is say "okay" and do what we always do. */
308  return 0;
309 }
310 
311 static Uint32
312 Cocoa_GetGlobalMouseState(int *x, int *y)
313 {
314  const NSUInteger cocoaButtons = [NSEvent pressedMouseButtons];
315  const NSPoint cocoaLocation = [NSEvent mouseLocation];
316  Uint32 retval = 0;
317 
318  *x = (int) cocoaLocation.x;
319  *y = (int) (CGDisplayPixelsHigh(kCGDirectMainDisplay) - cocoaLocation.y);
320 
321  retval |= (cocoaButtons & (1 << 0)) ? SDL_BUTTON_LMASK : 0;
322  retval |= (cocoaButtons & (1 << 1)) ? SDL_BUTTON_RMASK : 0;
323  retval |= (cocoaButtons & (1 << 2)) ? SDL_BUTTON_MMASK : 0;
324  retval |= (cocoaButtons & (1 << 3)) ? SDL_BUTTON_X1MASK : 0;
325  retval |= (cocoaButtons & (1 << 4)) ? SDL_BUTTON_X2MASK : 0;
326 
327  return retval;
328 }
329 
330 int
332 {
333  SDL_Mouse *mouse = SDL_GetMouse();
334  SDL_MouseData *driverdata = (SDL_MouseData*) SDL_calloc(1, sizeof(SDL_MouseData));
335  if (driverdata == NULL) {
336  return SDL_OutOfMemory();
337  }
338 
339  mouse->driverdata = driverdata;
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 
352  Cocoa_InitMouseEventTap(driverdata);
353 
354  const NSPoint location = [NSEvent mouseLocation];
355  driverdata->lastMoveX = location.x;
356  driverdata->lastMoveY = location.y;
357  return 0;
358 }
359 
360 void
362 {
363  switch ([event type]) {
364  case NSEventTypeMouseMoved:
365  case NSEventTypeLeftMouseDragged:
366  case NSEventTypeRightMouseDragged:
367  case NSEventTypeOtherMouseDragged:
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  SDL_MouseID mouseID = mouse ? mouse->mouseID : 0;
382  const SDL_bool seenWarp = driverdata->seenWarp;
383  driverdata->seenWarp = NO;
384 
385  const NSPoint location = [NSEvent mouseLocation];
386  const CGFloat lastMoveX = driverdata->lastMoveX;
387  const CGFloat lastMoveY = driverdata->lastMoveY;
388  driverdata->lastMoveX = location.x;
389  driverdata->lastMoveY = location.y;
390  DLog("Last seen mouse: (%g, %g)", location.x, location.y);
391 
392  /* Non-relative movement is handled in -[Cocoa_WindowListener mouseMoved:] */
393  if (!mouse->relative_mode) {
394  return;
395  }
396 
397  /* Ignore events that aren't inside the client area (i.e. title bar.) */
398  if ([event window]) {
399  NSRect windowRect = [[[event window] contentView] frame];
400  if (!NSMouseInRect([event locationInWindow], windowRect, NO)) {
401  return;
402  }
403  }
404 
405  float deltaX = [event deltaX];
406  float deltaY = [event deltaY];
407 
408  if (seenWarp) {
409  deltaX += (lastMoveX - driverdata->lastWarpX);
410  deltaY += ((CGDisplayPixelsHigh(kCGDirectMainDisplay) - lastMoveY) - driverdata->lastWarpY);
411 
412  DLog("Motion was (%g, %g), offset to (%g, %g)", [event deltaX], [event deltaY], deltaX, deltaY);
413  }
414 
415  SDL_SendMouseMotion(mouse->focus, mouseID, 1, (int)deltaX, (int)deltaY);
416 }
417 
418 void
419 Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event)
420 {
421  SDL_Mouse *mouse = SDL_GetMouse();
422  if (!mouse) {
423  return;
424  }
425 
426  SDL_MouseID mouseID = mouse->mouseID;
427  CGFloat x = -[event deltaX];
428  CGFloat y = [event deltaY];
430 
431  if ([event respondsToSelector:@selector(isDirectionInvertedFromDevice)]) {
432  if ([event isDirectionInvertedFromDevice] == YES) {
433  direction = SDL_MOUSEWHEEL_FLIPPED;
434  }
435  }
436 
437  if (x > 0) {
438  x = SDL_ceil(x);
439  } else if (x < 0) {
440  x = SDL_floor(x);
441  }
442  if (y > 0) {
443  y = SDL_ceil(y);
444  } else if (y < 0) {
445  y = SDL_floor(y);
446  }
447 
448  SDL_SendMouseWheel(window, mouseID, x, y, direction);
449 }
450 
451 void
452 Cocoa_HandleMouseWarp(CGFloat x, CGFloat y)
453 {
454  /* This makes Cocoa_HandleMouseEvent ignore the delta caused by the warp,
455  * since it gets included in the next movement event.
456  */
458  driverdata->lastWarpX = x;
459  driverdata->lastWarpY = y;
460  driverdata->seenWarp = SDL_TRUE;
461 
462  DLog("(%g, %g)", x, y);
463 }
464 
465 void
467 {
468  SDL_Mouse *mouse = SDL_GetMouse();
469  if (mouse) {
470  if (mouse->driverdata) {
472 
473  SDL_free(mouse->driverdata);
474  mouse->driverdata = NULL;
475  }
476  }
477 }
478 
479 #endif /* SDL_VIDEO_DRIVER_COCOA */
480 
481 /* vi: set ts=4 sw=4 expandtab: */
SDL_Window * next
Definition: SDL_sysvideo.h:114
GLdouble GLdouble GLdouble r
Definition: SDL_opengl.h:2079
SDL_Mouse * SDL_GetMouse(void)
Definition: SDL_mouse.c:178
int(* ShowCursor)(SDL_Cursor *cursor)
Definition: SDL_mouse_c.h:52
#define SDL_ceil
GLuint64EXT * result
#define SDL_BUTTON_RMASK
Definition: SDL_mouse.h:289
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1574
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:291
EGLSurface surface
Definition: eglext.h:248
The structure that defines a point (integer)
Definition: SDL_rect.h:48
A collection of pixels used in software blitting.
Definition: SDL_surface.h:70
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
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:99
void SDL_SetMouseFocus(SDL_Window *window)
Definition: SDL_mouse.c:211
void Cocoa_HandleMouseWarp(CGFloat x, CGFloat y)
CGFloat lastWarpX
#define SDL_floor
CGFloat lastWarpY
SDL_MouseID mouseID
Definition: SDL_mouse_c.h:76
Uint32 SDL_MouseID
Definition: SDL_mouse_c.h:28
static SDL_AudioDeviceID device
Definition: loopwave.c:37
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 Cocoa_QuitMouseEventTap(SDL_MouseData *driverdata)
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:301
void Cocoa_QuitMouse(_THIS)
Cocoa_WindowListener * listener
#define _THIS
#define SDL_free
struct _cl_event * event
GLint location
int Cocoa_InitMouse(_THIS)
SDL_bool relative_mode
Definition: SDL_mouse_c.h:87
#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)
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1574
SDL_Window * windows
Definition: SDL_sysvideo.h:317
SDL_Cursor * cursor
Definition: testwm2.c:40
void Cocoa_HandleMouseEvent(_THIS, NSEvent *event)
void SDL_SetDefaultCursor(SDL_Cursor *cursor)
Definition: SDL_mouse.c:167
int(* WarpMouseGlobal)(int x, int y)
Definition: SDL_mouse_c.h:64
GLenum GLenum GLsizei const GLuint GLboolean enabled
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:169
#define NULL
Definition: begin_code.h:167
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:161
int SDL_SendMouseWheel(SDL_Window *window, SDL_MouseID mouseID, float x, float y, SDL_MouseWheelDirection direction)
Definition: SDL_mouse.c:611
#define SDL_GetMouseFocus
CGFloat lastMoveX
#define SDL_SetError
#define SDL_calloc
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
SDL_bool seenWarp
The type used to identify a window.
Definition: SDL_sysvideo.h:73
uint32_t Uint32
Definition: SDL_stdinc.h:203
void(* WarpMouse)(SDL_Window *window, int x, int y)
Definition: SDL_mouse_c.h:61
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1571
SDL_VideoDevice * SDL_GetVideoDevice(void)
Definition: SDL_video.c:583
void * driverdata
Definition: SDL_sysvideo.h:111
void * driverdata
Definition: SDL_mouse_c.h:109
#define SDL_BUTTON_MMASK
Definition: SDL_mouse.h:288
SDL_Cursor *(* CreateSystemCursor)(SDL_SystemCursor id)
Definition: SDL_mouse_c.h:49
void * driverdata
Definition: SDL_mouse_c.h:33
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 (integer).
Definition: SDL_rect.h:77
int uint32_t uint32_t uint32_t uint32_t uint32_t int drmModeModeInfoPtr mode int uint32_t uint32_t uint32_t uint32_t int32_t hot_x
Definition: SDL_kmsdrmsym.h:55