cocoa: Try to use better system cursors.

These try to pull from the .pdf files that are installed with
macOS, which fit our needs better, and fall back to the most
reasonable defaults available from NSCursor if we can't load
them.

Since these are installed under /System, they should be sandbox
accessible, and if this totally fails, it should still go on,
albeit with a less good cursor.

Reference Issue #2123.
This commit is contained in:
Ryan C. Gordon 2022-05-17 12:50:13 -04:00
parent a12ffbd532
commit 56665e1d9d
1 changed files with 50 additions and 9 deletions

View File

@ -105,6 +105,45 @@ Cocoa_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
return cursor; return cursor;
}} }}
/* there are .pdf files of some of the cursors we need, installed by default on macOS, but not available through NSCursor.
If we can load them ourselves, use them, otherwise fallback to something standard but not super-great.
Since these are under /System, they should be available even to sandboxed apps. */
static NSCursor *
LoadHiddenSystemCursor(NSString *cursorName, SEL fallback)
{
NSString *cursorPath = [@"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors" stringByAppendingPathComponent:cursorName];
NSImage *image = [[NSImage alloc] initWithContentsOfFile:[cursorPath stringByAppendingPathComponent:@"cursor.pdf"]];
if ((image == nil) || (image.valid == NO)) {
return [NSCursor performSelector:fallback];
}
NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[cursorPath stringByAppendingPathComponent:@"info.plist"]];
/* we can't do animation atm. :/ */
const int frames = [[info valueForKey:@"frames"] integerValue];
if (frames > 1) {
const NSSize cropped_size = NSMakeSize(image.size.width, (int) (image.size.height / frames));
NSImage *cropped = [[NSImage alloc] initWithSize:cropped_size];
if (cropped == nil) {
return [NSCursor performSelector:fallback];
}
#ifdef MAC_OS_VERSION_12_0 /* same value as deprecated symbol. */
const NSCompositingOperation operation = NSCompositingOperationCopy;
#else
const NSCompositingOperation operation = NSCompositeCopy;
#endif
[cropped lockFocus];
const NSRect cropped_rect = NSMakeRect(0, 0, cropped_size.width, cropped_size.height);
[image drawInRect:cropped_rect fromRect:cropped_rect operation:operation fraction:1];
[cropped unlockFocus];
image = cropped;
}
NSCursor *cursor = [[NSCursor alloc] initWithImage:image hotSpot:NSMakePoint([[info valueForKey:@"hotx"] doubleValue], [[info valueForKey:@"hoty"] doubleValue])];
return cursor;
}
static SDL_Cursor * static SDL_Cursor *
Cocoa_CreateSystemCursor(SDL_SystemCursor id) Cocoa_CreateSystemCursor(SDL_SystemCursor id)
{ @autoreleasepool { @autoreleasepool
@ -119,27 +158,29 @@ Cocoa_CreateSystemCursor(SDL_SystemCursor id)
case SDL_SYSTEM_CURSOR_IBEAM: case SDL_SYSTEM_CURSOR_IBEAM:
nscursor = [NSCursor IBeamCursor]; nscursor = [NSCursor IBeamCursor];
break; break;
case SDL_SYSTEM_CURSOR_WAIT:
nscursor = [NSCursor arrowCursor];
break;
case SDL_SYSTEM_CURSOR_CROSSHAIR: case SDL_SYSTEM_CURSOR_CROSSHAIR:
nscursor = [NSCursor crosshairCursor]; nscursor = [NSCursor crosshairCursor];
break; break;
case SDL_SYSTEM_CURSOR_WAITARROW: case SDL_SYSTEM_CURSOR_WAIT: /* !!! FIXME: this is more like WAITARROW */
nscursor = [NSCursor arrowCursor]; nscursor = LoadHiddenSystemCursor(@"busybutclickable", @selector(arrowCursor));
break;
case SDL_SYSTEM_CURSOR_WAITARROW: /* !!! FIXME: this is meant to be animated */
nscursor = LoadHiddenSystemCursor(@"busybutclickable", @selector(arrowCursor));
break; break;
case SDL_SYSTEM_CURSOR_SIZENWSE: case SDL_SYSTEM_CURSOR_SIZENWSE:
nscursor = LoadHiddenSystemCursor(@"resizenorthwestsoutheast", @selector(closedHandCursor));
break;
case SDL_SYSTEM_CURSOR_SIZENESW: case SDL_SYSTEM_CURSOR_SIZENESW:
nscursor = [NSCursor closedHandCursor]; nscursor = LoadHiddenSystemCursor(@"resizenortheastsouthwest", @selector(closedHandCursor));
break; break;
case SDL_SYSTEM_CURSOR_SIZEWE: case SDL_SYSTEM_CURSOR_SIZEWE:
nscursor = [NSCursor resizeLeftRightCursor]; nscursor = LoadHiddenSystemCursor(@"resizeeastwest", @selector(resizeLeftRightCursor));
break; break;
case SDL_SYSTEM_CURSOR_SIZENS: case SDL_SYSTEM_CURSOR_SIZENS:
nscursor = [NSCursor resizeUpDownCursor]; nscursor = LoadHiddenSystemCursor(@"resizenorthsouth", @selector(resizeUpDownCursor));
break; break;
case SDL_SYSTEM_CURSOR_SIZEALL: case SDL_SYSTEM_CURSOR_SIZEALL:
nscursor = [NSCursor closedHandCursor]; nscursor = LoadHiddenSystemCursor(@"move", @selector(closedHandCursor));
break; break;
case SDL_SYSTEM_CURSOR_NO: case SDL_SYSTEM_CURSOR_NO:
nscursor = [NSCursor operationNotAllowedCursor]; nscursor = [NSCursor operationNotAllowedCursor];