Implement mouse shape support for macOS

Code for loading hidden system cursors not available via NCursor comes
from the SDL library, with thanks.
This commit is contained in:
Kovid Goyal
2023-10-15 09:42:06 +05:30
parent 4f1971c480
commit 792b74503c
2 changed files with 83 additions and 13 deletions

View File

@@ -58,6 +58,7 @@ def main(args: List[str]=sys.argv) -> None:
glfw_xfont_map = []
kitty_to_enum_map = {}
enum_to_glfw_map = {}
glfw_cocoa_map = {}
for line in cursors.splitlines():
line = line.strip()
if line:
@@ -78,6 +79,15 @@ def main(args: List[str]=sys.argv) -> None:
else:
items = tuple('"' + x.replace('!', '') + '"' for x in xc)
glfw_xfont_map.append(f'case {glfw_name}: return try_cursor_names(cursor, {len(items)}, {", ".join(items)});')
parts = cocoa.split(':', 1)
if len(parts) == 1:
if parts[0].startswith('_'):
glfw_cocoa_map[glfw_name] = f'U({glfw_name}, {parts[0]});'
else:
glfw_cocoa_map[glfw_name] = f'C({glfw_name}, {parts[0]});'
else:
glfw_cocoa_map[glfw_name] = f'S({glfw_name}, {parts[0]}, {parts[1]});'
glfw_enum.append('GLFW_INVALID_CURSOR')
patch_file('glfw/glfw3.h', 'mouse cursor shapes', '\n'.join(f' {x},' for x in glfw_enum))
@@ -94,6 +104,7 @@ def main(args: List[str]=sys.argv) -> None:
f' case {k}: set_glfw_mouse_cursor(w, {v}); break;' for k, v in enum_to_glfw_map.items()))
patch_file('kitty/glfw.c', 'name to glfw', '\n'.join(
f' if (strcmp(name, "{k}") == 0) return {enum_to_glfw_map[v]};' for k, v in kitty_to_enum_map.items()))
patch_file('glfw/cocoa_window.m', 'glfw to cocoa', '\n'.join(f' {x}' for x in glfw_cocoa_map.values()))
subprocess.check_call(['glfw/glfw.py'])

View File

@@ -2505,27 +2505,86 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
return true;
}
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, GLFWCursorShape shape)
{
static NSCursor*
load_hidden_system_cursor(NSString *name, SEL fallback) {
// this implementation comes from SDL_cocoamouse.m
NSString *cursorPath = [@"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors" stringByAppendingPathComponent:name];
NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[cursorPath stringByAppendingPathComponent:@"info.plist"]];
/* we can't do animation atm. :/ */
const int frames = (int)[[info valueForKey:@"frames"] integerValue];
NSCursor *cursor;
NSImage *image = [[NSImage alloc] initWithContentsOfFile:[cursorPath stringByAppendingPathComponent:@"cursor.pdf"]];
if ((image == nil) || (image.isValid == NO)) {
return [NSCursor performSelector:fallback];
}
if (frames > 1) {
#ifdef MAC_OS_VERSION_12_0 /* same value as deprecated symbol. */
const NSCompositingOperation operation = NSCompositingOperationCopy;
#else
const NSCompositingOperation operation = NSCompositeCopy;
#endif
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];
}
[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;
}
cursor = [[NSCursor alloc] initWithImage:image hotSpot:NSMakePoint([[info valueForKey:@"hotx"] doubleValue], [[info valueForKey:@"hoty"] doubleValue])];
return cursor;
}
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, GLFWCursorShape shape) {
#define C(name, val) case name: cursor->ns.object = [NSCursor val]; break;
#define U(name, val) case name: cursor->ns.object = [[NSCursor class] performSelector:@selector(val)]; break;
#define U(name, val) case name: cursor->ns.object = [NSCursor performSelector:@selector(val)]; break;
#define S(name, val, fallback) case name: cursor->ns.object = load_hidden_system_cursor(@#val, @selector(val)); break;
switch(shape) {
C(GLFW_ARROW_CURSOR, arrowCursor);
C(GLFW_IBEAM_CURSOR, IBeamCursor);
/* start glfw to cocoa (auto generated by gen-key-constants.py do not edit) */
C(GLFW_DEFAULT_CURSOR, arrowCursor);
C(GLFW_TEXT_CURSOR, IBeamCursor);
C(GLFW_POINTER_CURSOR, pointingHandCursor);
S(GLFW_HELP_CURSOR, help, arrowCursor);
S(GLFW_WAIT_CURSOR, busybutclickable, arrowCursor);
S(GLFW_PROGRESS_CURSOR, busybutclickable, arrowCursor);
C(GLFW_CROSSHAIR_CURSOR, crosshairCursor);
C(GLFW_HAND_CURSOR, pointingHandCursor);
C(GLFW_HRESIZE_CURSOR, resizeLeftRightCursor);
C(GLFW_VRESIZE_CURSOR, resizeUpDownCursor);
U(GLFW_NW_RESIZE_CURSOR, _windowResizeNorthWestSouthEastCursor);
U(GLFW_NE_RESIZE_CURSOR, _windowResizeNorthEastSouthWestCursor);
U(GLFW_SW_RESIZE_CURSOR, _windowResizeNorthEastSouthWestCursor);
U(GLFW_SE_RESIZE_CURSOR, _windowResizeNorthWestSouthEastCursor);
C(GLFW_VERTICAL_TEXT_CURSOR, IBeamCursorForVerticalLayout);
S(GLFW_MOVE_CURSOR, move, openHandCursor);
C(GLFW_E_RESIZE_CURSOR, resizeRightCursor);
S(GLFW_NE_RESIZE_CURSOR, resizenortheast, _windowResizeNorthEastSouthWestCursor);
S(GLFW_NW_RESIZE_CURSOR, resizenorthwest, _windowResizeNorthWestSouthEastCursor);
C(GLFW_N_RESIZE_CURSOR, resizeUpCursor);
S(GLFW_SE_RESIZE_CURSOR, resizesoutheast, _windowResizeNorthWestSouthEastCursor);
S(GLFW_SW_RESIZE_CURSOR, resizesouthwest, _windowResizeNorthEastSouthWestCursor);
C(GLFW_S_RESIZE_CURSOR, resizeDownCursor);
C(GLFW_W_RESIZE_CURSOR, resizeLeftCursor);
C(GLFW_EW_RESIZE_CURSOR, resizeLeftRightCursor);
C(GLFW_NS_RESIZE_CURSOR, resizeUpDownCursor);
U(GLFW_NESW_RESIZE_CURSOR, _windowResizeNorthEastSouthWestCursor);
U(GLFW_NWSE_RESIZE_CURSOR, _windowResizeNorthWestSouthEastCursor);
S(GLFW_ZOOM_IN_CURSOR, zoomin, arrowCursor);
S(GLFW_ZOOM_OUT_CURSOR, zoomout, arrowCursor);
C(GLFW_ALIAS_CURSOR, dragLinkCursor);
C(GLFW_COPY_CURSOR, dragCopyCursor);
C(GLFW_NOT_ALLOWED_CURSOR, operationNotAllowedCursor);
C(GLFW_NO_DROP_CURSOR, operationNotAllowedCursor);
C(GLFW_GRAB_CURSOR, openHandCursor);
C(GLFW_GRABBING_CURSOR, closedHandCursor);
/* end glfw to cocoa */
case GLFW_INVALID_CURSOR:
return false;
}
#undef C
#undef U
#undef S
if (!cursor->ns.object)
{
_glfwInputError(GLFW_PLATFORM_ERROR,