Atlas - SDL_cocoamouse.m

Home / ext / SDL2 / src / video / cocoa Lines: 2 | Size: 13957 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 Sam Lantinga <[email protected]> 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 68static SDL_Cursor * 69Cocoa_CreateDefaultCursor() 70{ @autoreleasepool 71{ 72 NSCursor *nscursor; 73 SDL_Cursor *cursor = NULL; 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 88static SDL_Cursor * 89Cocoa_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 113static SDL_Cursor * 114Cocoa_CreateSystemCursor(SDL_SystemCursor id) 115{ @autoreleasepool 116{ 117 NSCursor *nscursor = NULL; 118 SDL_Cursor *cursor = NULL; 119 120 switch(id) { 121 case SDL_SYSTEM_CURSOR_ARROW: 122 nscursor = [NSCursor arrowCursor]; 123 break; 124 case SDL_SYSTEM_CURSOR_IBEAM: 125 nscursor = [NSCursor IBeamCursor]; 126 break; 127 case SDL_SYSTEM_CURSOR_WAIT: 128 nscursor = [NSCursor arrowCursor]; 129 break; 130 case SDL_SYSTEM_CURSOR_CROSSHAIR: 131 nscursor = [NSCursor crosshairCursor]; 132 break; 133 case SDL_SYSTEM_CURSOR_WAITARROW: 134 nscursor = [NSCursor arrowCursor]; 135 break; 136 case SDL_SYSTEM_CURSOR_SIZENWSE: 137 case SDL_SYSTEM_CURSOR_SIZENESW: 138 nscursor = [NSCursor closedHandCursor]; 139 break; 140 case SDL_SYSTEM_CURSOR_SIZEWE: 141 nscursor = [NSCursor resizeLeftRightCursor]; 142 break; 143 case SDL_SYSTEM_CURSOR_SIZENS: 144 nscursor = [NSCursor resizeUpDownCursor]; 145 break; 146 case SDL_SYSTEM_CURSOR_SIZEALL: 147 nscursor = [NSCursor closedHandCursor]; 148 break; 149 case SDL_SYSTEM_CURSOR_NO: 150 nscursor = [NSCursor operationNotAllowedCursor]; 151 break; 152 case SDL_SYSTEM_CURSOR_HAND: 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 172static void 173Cocoa_FreeCursor(SDL_Cursor * cursor) 174{ @autoreleasepool 175{ 176 NSCursor *nscursor = (NSCursor *)cursor->driverdata; 177 178 [nscursor release]; 179 SDL_free(cursor); 180}} 181 182static int 183Cocoa_ShowCursor(SDL_Cursor * cursor) 184{ @autoreleasepool 185{ 186 SDL_VideoDevice *device = SDL_GetVideoDevice(); 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 199static SDL_Window * 200SDL_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 214static int 215Cocoa_WarpMouseGlobal(int x, int y) 216{ 217 SDL_Mouse *mouse = SDL_GetMouse(); 218 if (mouse->focus) { 219 SDL_WindowData *data = (SDL_WindowData *) mouse->focus->driverdata; 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 255static void 256Cocoa_WarpMouse(SDL_Window * window, int x, int y) 257{ 258 Cocoa_WarpMouseGlobal(x + window->x, y + window->y); 259} 260 261static int 262Cocoa_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 303static int 304Cocoa_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 311static Uint32 312Cocoa_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 336void 337Cocoa_InitMouse(_THIS) 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 355 Cocoa_InitMouseEventTap(mouse->driverdata); 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 363void 364Cocoa_HandleMouseEvent(_THIS, NSEvent *event) 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 420void 421Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event) 422{ 423 SDL_Mouse *mouse = SDL_GetMouse(); 424 425 CGFloat x = -[event deltaX]; 426 CGFloat y = [event deltaY]; 427 SDL_MouseWheelDirection direction = SDL_MOUSEWHEEL_NORMAL; 428 429 if ([event respondsToSelector:@selector(isDirectionInvertedFromDevice)]) { 430 if ([event isDirectionInvertedFromDevice] == YES) { 431 direction = SDL_MOUSEWHEEL_FLIPPED; 432 } 433 } 434 435 if (x > 0) { 436 x = SDL_ceil(x); 437 } else if (x < 0) { 438 x = SDL_floor(x); 439 } 440 if (y > 0) { 441 y = SDL_ceil(y); 442 } else if (y < 0) { 443 y = SDL_floor(y); 444 } 445 SDL_SendMouseWheel(window, mouse->mouseID, x, y, direction); 446} 447 448void 449Cocoa_HandleMouseWarp(CGFloat x, CGFloat y) 450{ 451 /* This makes Cocoa_HandleMouseEvent ignore the delta caused by the warp, 452 * since it gets included in the next movement event. 453 */ 454 SDL_MouseData *driverdata = (SDL_MouseData*)SDL_GetMouse()->driverdata; 455 driverdata->lastWarpX = x; 456 driverdata->lastWarpY = y; 457 driverdata->seenWarp = SDL_TRUE; 458 459 DLog("(%g, %g)", x, y); 460} 461 462void 463Cocoa_QuitMouse(_THIS) 464{ 465 SDL_Mouse *mouse = SDL_GetMouse(); 466 if (mouse) { 467 if (mouse->driverdata) { 468 Cocoa_QuitMouseEventTap(((SDL_MouseData*)mouse->driverdata)); 469 } 470 471 SDL_free(mouse->driverdata); 472 } 473} 474 475#endif /* SDL_VIDEO_DRIVER_COCOA */ 476 477/* vi: set ts=4 sw=4 expandtab: */ 478
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.