SDL  2.0
SDL_cocoamouse.m
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2018 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  for (NSScreen *screen in [NSScreen screens]) {
319  NSRect frame = [screen frame];
320  if (NSMouseInRect(cocoaLocation, frame, NO)) {
321  *x = (int) cocoaLocation.x;
322  *y = (int) ((frame.origin.y + frame.size.height) - cocoaLocation.y);
323  break;
324  }
325  }
326 
327  retval |= (cocoaButtons & (1 << 0)) ? SDL_BUTTON_LMASK : 0;
328  retval |= (cocoaButtons & (1 << 1)) ? SDL_BUTTON_RMASK : 0;
329  retval |= (cocoaButtons & (1 << 2)) ? SDL_BUTTON_MMASK : 0;
330  retval |= (cocoaButtons & (1 << 3)) ? SDL_BUTTON_X1MASK : 0;
331  retval |= (cocoaButtons & (1 << 4)) ? SDL_BUTTON_X2MASK : 0;
332 
333  return retval;
334 }
335 
336 void
338 {
339  SDL_Mouse *mouse = SDL_GetMouse();
340 
341  mouse->driverdata = SDL_calloc(1, sizeof(SDL_MouseData));
342 
343  mouse->CreateCursor = Cocoa_CreateCursor;
344  mouse->CreateSystemCursor = Cocoa_CreateSystemCursor;
345  mouse->ShowCursor = Cocoa_ShowCursor;
346  mouse->FreeCursor = Cocoa_FreeCursor;
347  mouse->WarpMouse = Cocoa_WarpMouse;
348  mouse->WarpMouseGlobal = Cocoa_WarpMouseGlobal;
349  mouse->SetRelativeMouseMode = Cocoa_SetRelativeMouseMode;
350  mouse->CaptureMouse = Cocoa_CaptureMouse;
351  mouse->GetGlobalMouseState = Cocoa_GetGlobalMouseState;
352 
353  SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor());
354 
356 
357  SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
358  const NSPoint location = [NSEvent mouseLocation];
359  driverdata->lastMoveX = location.x;
360  driverdata->lastMoveY = location.y;
361 }
362 
363 void
365 {
366  switch ([event type]) {
367  case NSEventTypeMouseMoved:
368  case NSEventTypeLeftMouseDragged:
369  case NSEventTypeRightMouseDragged:
370  case NSEventTypeOtherMouseDragged:
371  break;
372 
373  default:
374  /* Ignore any other events. */
375  return;
376  }
377 
378  SDL_Mouse *mouse = SDL_GetMouse();
379  SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
380  if (!driverdata) {
381  return; /* can happen when returning from fullscreen Space on shutdown */
382  }
383 
384  const SDL_bool seenWarp = driverdata->seenWarp;
385  driverdata->seenWarp = NO;
386 
387  const NSPoint location = [NSEvent mouseLocation];
388  const CGFloat lastMoveX = driverdata->lastMoveX;
389  const CGFloat lastMoveY = driverdata->lastMoveY;
390  driverdata->lastMoveX = location.x;
391  driverdata->lastMoveY = location.y;
392  DLog("Last seen mouse: (%g, %g)", location.x, location.y);
393 
394  /* Non-relative movement is handled in -[Cocoa_WindowListener mouseMoved:] */
395  if (!mouse->relative_mode) {
396  return;
397  }
398 
399  /* Ignore events that aren't inside the client area (i.e. title bar.) */
400  if ([event window]) {
401  NSRect windowRect = [[[event window] contentView] frame];
402  if (!NSMouseInRect([event locationInWindow], windowRect, NO)) {
403  return;
404  }
405  }
406 
407  float deltaX = [event deltaX];
408  float deltaY = [event deltaY];
409 
410  if (seenWarp) {
411  deltaX += (lastMoveX - driverdata->lastWarpX);
412  deltaY += ((CGDisplayPixelsHigh(kCGDirectMainDisplay) - lastMoveY) - driverdata->lastWarpY);
413 
414  DLog("Motion was (%g, %g), offset to (%g, %g)", [event deltaX], [event deltaY], deltaX, deltaY);
415  }
416 
417  SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int)deltaX, (int)deltaY);
418 }
419 
420 void
421 Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event)
422 {
423  SDL_Mouse *mouse = SDL_GetMouse();
424 
425  CGFloat x = -[event deltaX];
426  CGFloat y = [event deltaY];
428 
429  if ([event respondsToSelector:@selector(isDirectionInvertedFromDevice)]) {
430  if ([event isDirectionInvertedFromDevice] == YES) {
431  direction = SDL_MOUSEWHEEL_FLIPPED;
432  }
433  }
434 
435  SDL_SendMouseWheel(window, mouse->mouseID, x, y, direction);
436 }
437 
438 void
439 Cocoa_HandleMouseWarp(CGFloat x, CGFloat y)
440 {
441  /* This makes Cocoa_HandleMouseEvent ignore the delta caused by the warp,
442  * since it gets included in the next movement event.
443  */
445  driverdata->lastWarpX = x;
446  driverdata->lastWarpY = y;
447  driverdata->seenWarp = SDL_TRUE;
448 
449  DLog("(%g, %g)", x, y);
450 }
451 
452 void
454 {
455  SDL_Mouse *mouse = SDL_GetMouse();
456  if (mouse) {
457  if (mouse->driverdata) {
459  }
460 
461  SDL_free(mouse->driverdata);
462  }
463 }
464 
465 #endif /* SDL_VIDEO_DRIVER_COCOA */
466 
467 /* vi: set ts=4 sw=4 expandtab: */
SDL_Window * next
Definition: SDL_sysvideo.h:114
void Cocoa_InitMouse(_THIS)
GLdouble GLdouble GLdouble r
Definition: SDL_opengl.h:2079
SDL_Mouse * SDL_GetMouse(void)
Definition: SDL_mouse.c:112
int(* ShowCursor)(SDL_Cursor *cursor)
Definition: SDL_mouse_c.h:52
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.
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: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:73
void SDL_SetMouseFocus(SDL_Window *window)
Definition: SDL_mouse.c:151
void Cocoa_HandleMouseWarp(CGFloat x, CGFloat y)
uint32_t Uint32
Definition: SDL_stdinc.h:181
CGFloat lastWarpX
CGFloat lastWarpY
SDL_MouseID mouseID
Definition: SDL_mouse_c.h:76
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:237
void Cocoa_QuitMouse(_THIS)
Cocoa_WindowListener * listener
#define _THIS
#define SDL_free
struct _cl_event * event
int frame
Definition: teststreaming.c:60
GLint location
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:313
void Cocoa_HandleMouseEvent(_THIS, NSEvent *event)
void SDL_SetDefaultCursor(SDL_Cursor *cursor)
Definition: SDL_mouse.c:101
GLsizeiptr size
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:169
#define NULL
Definition: begin_code.h:164
SDL_bool
Definition: SDL_stdinc.h:139
int SDL_SendMouseWheel(SDL_Window *window, SDL_MouseID mouseID, float x, float y, SDL_MouseWheelDirection direction)
Definition: SDL_mouse.c:512
#define SDL_GetMouseFocus
CGFloat lastMoveX
#define SDL_SetError
GLint GLint GLsizei GLsizei height
Definition: SDL_opengl.h:1572
#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
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:586
void * driverdata
Definition: SDL_sysvideo.h:111
void * driverdata
Definition: SDL_mouse_c.h:105
#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
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