mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-02 12:44:01 +02:00
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:
@@ -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'])
|
||||
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user