SDL  2.0
SDL_cocoamousetap.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_cocoamousetap.h"
26 
27 /* Event taps are forbidden in the Mac App Store, so we can only enable this
28  * code if your app doesn't need to ship through the app store.
29  * This code makes it so that a grabbed cursor cannot "leak" a mouse click
30  * past the edge of the window if moving the cursor too fast.
31  */
32 #if SDL_MAC_NO_SANDBOX
33 
34 #include "SDL_keyboard.h"
35 #include "SDL_cocoavideo.h"
36 #include "../../thread/SDL_systhread.h"
37 
38 #include "../../events/SDL_mouse_c.h"
39 
40 typedef struct {
41  CFMachPortRef tap;
42  CFRunLoopRef runloop;
43  CFRunLoopSourceRef runloopSource;
44  SDL_Thread *thread;
45  SDL_sem *runloopStartedSemaphore;
46 } SDL_MouseEventTapData;
47 
48 static const CGEventMask movementEventsMask =
49  CGEventMaskBit(kCGEventLeftMouseDragged)
50  | CGEventMaskBit(kCGEventRightMouseDragged)
51  | CGEventMaskBit(kCGEventMouseMoved);
52 
53 static const CGEventMask allGrabbedEventsMask =
54  CGEventMaskBit(kCGEventLeftMouseDown) | CGEventMaskBit(kCGEventLeftMouseUp)
55  | CGEventMaskBit(kCGEventRightMouseDown) | CGEventMaskBit(kCGEventRightMouseUp)
56  | CGEventMaskBit(kCGEventOtherMouseDown) | CGEventMaskBit(kCGEventOtherMouseUp)
57  | CGEventMaskBit(kCGEventLeftMouseDragged) | CGEventMaskBit(kCGEventRightMouseDragged)
58  | CGEventMaskBit(kCGEventMouseMoved);
59 
60 static CGEventRef
61 Cocoa_MouseTapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
62 {
63  SDL_MouseEventTapData *tapdata = (SDL_MouseEventTapData*)refcon;
64  SDL_Mouse *mouse = SDL_GetMouse();
66  NSWindow *nswindow;
67  NSRect windowRect;
68  CGPoint eventLocation;
69 
70  switch (type) {
71  case kCGEventTapDisabledByTimeout:
72  {
73  CGEventTapEnable(tapdata->tap, true);
74  return NULL;
75  }
76  case kCGEventTapDisabledByUserInput:
77  {
78  return NULL;
79  }
80  default:
81  break;
82  }
83 
84 
85  if (!window || !mouse) {
86  return event;
87  }
88 
89  if (mouse->relative_mode) {
90  return event;
91  }
92 
93  if (!(window->flags & SDL_WINDOW_INPUT_GRABBED)) {
94  return event;
95  }
96 
97  /* This is the same coordinate system as Cocoa uses. */
98  nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
99  eventLocation = CGEventGetUnflippedLocation(event);
100  windowRect = [nswindow contentRectForFrameRect:[nswindow frame]];
101 
102  if (!NSMouseInRect(NSPointFromCGPoint(eventLocation), windowRect, NO)) {
103 
104  /* This is in CGs global screenspace coordinate system, which has a
105  * flipped Y.
106  */
107  CGPoint newLocation = CGEventGetLocation(event);
108 
109  if (eventLocation.x < NSMinX(windowRect)) {
110  newLocation.x = NSMinX(windowRect);
111  } else if (eventLocation.x >= NSMaxX(windowRect)) {
112  newLocation.x = NSMaxX(windowRect) - 1.0;
113  }
114 
115  if (eventLocation.y <= NSMinY(windowRect)) {
116  newLocation.y -= (NSMinY(windowRect) - eventLocation.y + 1);
117  } else if (eventLocation.y > NSMaxY(windowRect)) {
118  newLocation.y += (eventLocation.y - NSMaxY(windowRect));
119  }
120 
121  CGWarpMouseCursorPosition(newLocation);
122  CGAssociateMouseAndMouseCursorPosition(YES);
123 
124  if ((CGEventMaskBit(type) & movementEventsMask) == 0) {
125  /* For click events, we just constrain the event to the window, so
126  * no other app receives the click event. We can't due the same to
127  * movement events, since they mean that our warp cursor above
128  * behaves strangely.
129  */
130  CGEventSetLocation(event, newLocation);
131  }
132  }
133 
134  return event;
135 }
136 
137 static void
138 SemaphorePostCallback(CFRunLoopTimerRef timer, void *info)
139 {
140  SDL_SemPost((SDL_sem*)info);
141 }
142 
143 static int
144 Cocoa_MouseTapThread(void *data)
145 {
146  SDL_MouseEventTapData *tapdata = (SDL_MouseEventTapData*)data;
147 
148  /* Tap was created on main thread but we own it now. */
149  CFMachPortRef eventTap = tapdata->tap;
150  if (eventTap) {
151  /* Try to create a runloop source we can schedule. */
152  CFRunLoopSourceRef runloopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
153  if (runloopSource) {
154  tapdata->runloopSource = runloopSource;
155  } else {
156  CFRelease(eventTap);
157  SDL_SemPost(tapdata->runloopStartedSemaphore);
158  /* TODO: Both here and in the return below, set some state in
159  * tapdata to indicate that initialization failed, which we should
160  * check in InitMouseEventTap, after we move the semaphore check
161  * from Quit to Init.
162  */
163  return 1;
164  }
165  } else {
166  SDL_SemPost(tapdata->runloopStartedSemaphore);
167  return 1;
168  }
169 
170  tapdata->runloop = CFRunLoopGetCurrent();
171  CFRunLoopAddSource(tapdata->runloop, tapdata->runloopSource, kCFRunLoopCommonModes);
172  CFRunLoopTimerContext context = {.info = tapdata->runloopStartedSemaphore};
173  /* We signal the runloop started semaphore *after* the run loop has started, indicating it's safe to CFRunLoopStop it. */
174  CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent(), 0, 0, 0, &SemaphorePostCallback, &context);
175  CFRunLoopAddTimer(tapdata->runloop, timer, kCFRunLoopCommonModes);
176  CFRelease(timer);
177 
178  /* Run the event loop to handle events in the event tap. */
179  CFRunLoopRun();
180  /* Make sure this is signaled so that SDL_QuitMouseEventTap knows it can safely SDL_WaitThread for us. */
181  if (SDL_SemValue(tapdata->runloopStartedSemaphore) < 1) {
182  SDL_SemPost(tapdata->runloopStartedSemaphore);
183  }
184  CFRunLoopRemoveSource(tapdata->runloop, tapdata->runloopSource, kCFRunLoopCommonModes);
185 
186  /* Clean up. */
187  CGEventTapEnable(tapdata->tap, false);
188  CFRelease(tapdata->runloopSource);
189  CFRelease(tapdata->tap);
190  tapdata->runloopSource = NULL;
191  tapdata->tap = NULL;
192 
193  return 0;
194 }
195 
196 void
198 {
199  SDL_MouseEventTapData *tapdata;
200  driverdata->tapdata = SDL_calloc(1, sizeof(SDL_MouseEventTapData));
201  tapdata = (SDL_MouseEventTapData*)driverdata->tapdata;
202 
203  tapdata->runloopStartedSemaphore = SDL_CreateSemaphore(0);
204  if (tapdata->runloopStartedSemaphore) {
205  tapdata->tap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap,
206  kCGEventTapOptionDefault, allGrabbedEventsMask,
207  &Cocoa_MouseTapCallback, tapdata);
208  if (tapdata->tap) {
209  /* Tap starts disabled, until app requests mouse grab */
210  CGEventTapEnable(tapdata->tap, false);
211  tapdata->thread = SDL_CreateThreadInternal(&Cocoa_MouseTapThread, "Event Tap Loop", 512 * 1024, tapdata);
212  if (tapdata->thread) {
213  /* Success - early out. Ownership transferred to thread. */
214  return;
215  }
216  CFRelease(tapdata->tap);
217  }
218  SDL_DestroySemaphore(tapdata->runloopStartedSemaphore);
219  }
220  SDL_free(driverdata->tapdata);
221  driverdata->tapdata = NULL;
222 }
223 
224 void
226 {
227  SDL_MouseEventTapData *tapdata = (SDL_MouseEventTapData*)driverdata->tapdata;
228  if (tapdata && tapdata->tap)
229  {
230  CGEventTapEnable(tapdata->tap, !!enabled);
231  }
232 }
233 
234 void
236 {
237  SDL_MouseEventTapData *tapdata = (SDL_MouseEventTapData*)driverdata->tapdata;
238  int status;
239 
240  if (tapdata == NULL) {
241  /* event tap was already cleaned up (possibly due to CGEventTapCreate
242  * returning null.)
243  */
244  return;
245  }
246 
247  /* Ensure that the runloop has been started first.
248  * TODO: Move this to InitMouseEventTap, check for error conditions that can
249  * happen in Cocoa_MouseTapThread, and fall back to the non-EventTap way of
250  * grabbing the mouse if it fails to Init.
251  */
252  status = SDL_SemWaitTimeout(tapdata->runloopStartedSemaphore, 5000);
253  if (status > -1) {
254  /* Then stop it, which will cause Cocoa_MouseTapThread to return. */
255  CFRunLoopStop(tapdata->runloop);
256  /* And then wait for Cocoa_MouseTapThread to finish cleaning up. It
257  * releases some of the pointers in tapdata. */
258  SDL_WaitThread(tapdata->thread, &status);
259  }
260 
261  SDL_free(driverdata->tapdata);
262  driverdata->tapdata = NULL;
263 }
264 
265 #else /* SDL_MAC_NO_SANDBOX */
266 
267 void
269 {
270 }
271 
272 void
274 {
275 }
276 
277 void
279 {
280 }
281 
282 #endif /* !SDL_MAC_NO_SANDBOX */
283 
284 #endif /* SDL_VIDEO_DRIVER_COCOA */
285 
286 /* vi: set ts=4 sw=4 expandtab: */
SDL_Mouse * SDL_GetMouse(void)
Definition: SDL_mouse.c:144
#define SDL_CreateSemaphore
void Cocoa_EnableMouseEventTap(SDL_MouseData *driverdata, SDL_bool enabled)
static screen_context_t context
Definition: video.c:25
set set set set set set set macro pixldst1 abits if abits op else op endif endm macro pixldst2 abits if abits op else op endif endm macro pixldst4 abits if abits op else op endif endm macro pixldst0 abits op endm macro pixldst3 mem_operand op endm macro pixldst30 mem_operand op endm macro pixldst abits if abits elseif abits elseif abits elseif abits elseif abits pixldst0 abits else pixldst0 abits pixldst0 abits pixldst0 abits pixldst0 abits endif elseif abits else pixldst0 abits pixldst0 abits endif elseif abits else error unsupported bpp *numpix else pixst endif endm macro pixld1_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl else error unsupported endif endm macro pixld2_s mem_operand if mov asr add asl add asl mov asr sub UNIT_X add asl mov asr add asl add asl mov asr add UNIT_X add asl else pixld1_s mem_operand pixld1_s mem_operand endif endm macro pixld0_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl endif endm macro pixld_s_internal mem_operand if mem_operand pixld2_s mem_operand pixdeinterleave basereg elseif mem_operand elseif mem_operand elseif mem_operand elseif mem_operand pixld0_s mem_operand else pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else error unsupported mem_operand if bpp mem_operand endif endm macro vuzp8 reg2 vuzp d d &reg2 endm macro vzip8 reg2 vzip d d &reg2 endm macro pixdeinterleave basereg basereg basereg basereg basereg endif endm macro pixinterleave basereg basereg basereg basereg basereg endif endm macro PF boost_increment endif if endif PF tst PF addne PF subne PF cmp ORIG_W if endif if endif if endif PF subge ORIG_W PF subges if endif if endif if endif endif endm macro cache_preload_simple endif if dst_r_bpp pld [DST_R, #(PREFETCH_DISTANCE_SIMPLE *dst_r_bpp/8)] endif if mask_bpp pld if[MASK, #(PREFETCH_DISTANCE_SIMPLE *mask_bpp/8)] endif endif endm macro fetch_mask_pixblock pixld mask_basereg pixblock_size MASK endm macro ensure_destination_ptr_alignment process_pixblock_tail_head if beq irp local skip1(dst_w_bpp<=(lowbit *8)) &&((lowbit *8)<(pixblock_size *dst_w_bpp)) .if lowbit< 16 tst DST_R
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
#define SDL_GetKeyboardFocus
#define SDL_SemPost
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
Definition: SDL_thread.c:429
void Cocoa_QuitMouseEventTap(SDL_MouseData *driverdata)
#define SDL_SemWaitTimeout
#define SDL_free
struct _cl_event * event
set set set set set set set set set set set set set set set set set set set set *set set set macro pixldst op &r &cond WK op &r &cond WK op &r &cond WK else op &m &cond &ia op &r &cond WK else op &m &cond &ia elseif elseif else error unsupported base if elseif elseif else error unsupported unaligned pixldst unaligned endm macro pixst base base else pixldst base endif endm macro PF base if bpp PF set rept prefetch_distance PF set OFFSET endr endif endm macro preload_leading_step2 base if bpp ifc DST PF PF else if bpp lsl PF PF lsl PF PF lsl PF PF PF else PF lsl PF lsl PF lsl PF endif SIZE macro preload_middle scratch_holds_offset if bpp if else PF PF endif endif endif endm macro preload_trailing base if bpp if bpp *pix_per_block PF PF lsl PF PF PF PF PF else PF lsl PF lsl PF PF PF PF PF base if bpp if narrow_case &&bpp<=dst_w_bpp) PF bic, WK0, base, #31 PF pld, [WK0] PF add, WK1, base, X, LSL #bpp_shift PF sub, WK1, WK1, #1 PF bic, WK1, WK1, #31 PF cmp, WK1, WK0 PF beq, 90f PF pld, [WK1]90:.else PF bic, WK0, base, #31 PF pld, [WK0] PF add, WK1, base, X, lsl #bpp_shift PF sub, WK1, WK1, #1 PF bic, WK1, WK1, #31 PF cmp, WK1, WK0 PF beq, 92f91:PF add, WK0, WK0, #32 PF cmp, WK0, WK1 PF pld, [WK0] PF bne, 91b92:.endif .endif.endm.macro conditional_process1_helper cond, process_head, process_tail, numbytes, firstreg, unaligned_src, unaligned_mask, decrementx process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, 0 .if decrementx sub &cond X, X, #8 *numbytes/dst_w_bpp .endif process_tail cond, numbytes, firstreg .if !((flags) &FLAG_PROCESS_DOES_STORE) pixst cond, numbytes, firstreg, DST .endif.endm.macro conditional_process1 cond, process_head, process_tail, numbytes, firstreg, unaligned_src, unaligned_mask, decrementx .if(flags) &FLAG_BRANCH_OVER .ifc cond, mi bpl 100f .endif .ifc cond, cs bcc 100f .endif .ifc cond, ne beq 100f .endif conditional_process1_helper, process_head, process_tail, numbytes, firstreg, unaligned_src, unaligned_mask, decrementx100:.else conditional_process1_helper cond, process_head, process_tail, numbytes, firstreg, unaligned_src, unaligned_mask, decrementx .endif.endm.macro conditional_process2 test, cond1, cond2, process_head, process_tail, numbytes1, numbytes2, firstreg1, firstreg2, unaligned_src, unaligned_mask, decrementx .if(flags) &(FLAG_DST_READWRITE|FLAG_BRANCH_OVER|FLAG_PROCESS_CORRUPTS_PSR|FLAG_PROCESS_DOES_STORE) test conditional_process1 cond1, process_head, process_tail, numbytes1, firstreg1, unaligned_src, unaligned_mask, decrementx .if(flags) &FLAG_PROCESS_CORRUPTS_PSR test .endif conditional_process1 cond2, process_head, process_tail, numbytes2, firstreg2, unaligned_src, unaligned_mask, decrementx .else test process_head cond1, numbytes1, firstreg1, unaligned_src, unaligned_mask, 0 process_head cond2, numbytes2, firstreg2, unaligned_src, unaligned_mask, 0 .if decrementx sub &cond1 X, X, #8 *numbytes1/dst_w_bpp sub &cond2 X, X, #8 *numbytes2/dst_w_bpp .endif process_tail cond1, numbytes1, firstreg1 process_tail cond2, numbytes2, firstreg2 pixst cond1, numbytes1, firstreg1, DST pixst cond2, numbytes2, firstreg2, DST .endif.endm.macro test_bits_1_0_ptr .if(flags) &FLAG_PROCESS_CORRUPTS_WK0 movs SCRATCH, X, lsl #32-1 .else movs SCRATCH, WK0, lsl #32-1 .endif.endm.macro test_bits_3_2_ptr .if(flags) &FLAG_PROCESS_CORRUPTS_WK0 movs SCRATCH, X, lsl #32-3 .else movs SCRATCH, WK0, lsl #32-3 .endif.endm.macro leading_15bytes process_head, process_tail .set DECREMENT_X, 1 .if(flags) &FLAG_PROCESS_CORRUPTS_WK0 .set DECREMENT_X, 0 sub X, X, WK0, lsr #dst_bpp_shift str X, [sp, #LINE_SAVED_REG_COUNT *4] mov X, WK0 .endif .if dst_w_bpp==8 conditional_process2 test_bits_1_0_ptr, mi, cs, process_head, process_tail, 1, 2, 1, 2, 1, 1, DECREMENT_X .elseif dst_w_bpp==16 test_bits_1_0_ptr conditional_process1 cs, process_head, process_tail, 2, 2, 1, 1, DECREMENT_X .endif conditional_process2 test_bits_3_2_ptr, mi, cs, process_head, process_tail, 4, 8, 1, 2, 1, 1, DECREMENT_X .if(flags) &FLAG_PROCESS_CORRUPTS_WK0 ldr X, [sp, #LINE_SAVED_REG_COUNT *4] .endif.endm.macro test_bits_3_2_pix movs SCRATCH, X, lsl #dst_bpp_shift+32-3.endm.macro test_bits_1_0_pix .if dst_w_bpp==8 movs SCRATCH, X, lsl #dst_bpp_shift+32-1 .else movs SCRATCH, X, lsr #1 .endif.endm.macro trailing_15bytes process_head, process_tail, unaligned_src, unaligned_mask conditional_process2 test_bits_3_2_pix, cs, mi, process_head, process_tail, 8, 4, 0, 2, unaligned_src, unaligned_mask, 0 .if dst_w_bpp==16 test_bits_1_0_pix conditional_process1 cs, process_head, process_tail, 2, 0, unaligned_src, unaligned_mask, 0 .elseif dst_w_bpp==8 conditional_process2 test_bits_1_0_pix, cs, mi, process_head, process_tail, 2, 1, 0, 1, unaligned_src, unaligned_mask, 0 .endif.endm.macro wide_case_inner_loop process_head, process_tail, unaligned_src, unaligned_mask, dst_alignment110:.set SUBBLOCK, 0 .rept pix_per_block *dst_w_bpp/128 process_head, 16, 0, unaligned_src, unaligned_mask, 1 .if(src_bpp > 0) &&(mask_bpp==0) &&((flags) &FLAG_PROCESS_PRESERVES_SCRATCH) preload_middle src_bpp, SRC, 1 .elseif(src_bpp==0) &&(mask_bpp > 0) &&((flags) &FLAG_PROCESS_PRESERVES_SCRATCH) preload_middle mask_bpp, MASK, 1 .else preload_middle src_bpp, SRC, 0 preload_middle mask_bpp, MASK, 0 .endif .if(dst_r_bpp > 0) &&((SUBBLOCK % 2)==0) &&(((flags) &FLAG_NO_PRELOAD_DST)==0) PF pld, [DST, #32 *prefetch_distance - dst_alignment] .endif process_tail, 16, 0 .if !((flags) &FLAG_PROCESS_DOES_STORE) pixst, 16, 0, DST .endif .set SUBBLOCK, SUBBLOCK+1 .endr subs X, X, #pix_per_block bhs 110b.endm.macro wide_case_inner_loop_and_trailing_pixels process_head, process_tail, process_inner_loop, exit_label, unaligned_src, unaligned_mask .if dst_r_bpp > tst bne process_inner_loop DST_PRELOAD_BIAS endif preload_trailing SRC preload_trailing MASK DST endif add medium_case_inner_loop_and_trailing_pixels unaligned_mask endm macro medium_case_inner_loop_and_trailing_pixels unused
SDL_bool relative_mode
Definition: SDL_mouse_c.h:87
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1574
GLenum GLenum GLsizei const GLuint GLboolean enabled
#define NULL
Definition: begin_code.h:164
SDL_bool
Definition: SDL_stdinc.h:161
#define SDL_calloc
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
The type used to identify a window.
Definition: SDL_sysvideo.h:73
#define SDL_DestroySemaphore
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1571
void * driverdata
Definition: SDL_sysvideo.h:111
#define SDL_SemValue
Uint32 flags
Definition: SDL_sysvideo.h:83
void Cocoa_InitMouseEventTap(SDL_MouseData *driverdata)
#define SDL_WaitThread