From a077cc8e4d8b9b4d1a2c35288faaeae574544d8d Mon Sep 17 00:00:00 2001 From: Sean Ridenour Date: Sun, 5 Feb 2023 18:58:33 -0700 Subject: [PATCH] [SDL3] macOS SetCursor performance fix (fixes #7151) (#7249) * Setting the same mouse cursor twice is a no-op * Cocoa: Call [NSCursor set] to change mouse cursor The previous way, changing the mouse cursor was handled by invalidating the mouse cursor rectangles and then recreating them (with the new cursor) the next event loop. This is extremely slow; sometimes it can take over a millisecond! With [NSCursor set] it happens instantly and very quick performance-wise. The downside is that it sets the cursor for the whole screen, so we have some guards in place to change it to the system cursor if the mouse moves outside the window or the window loses focus. * Cocoa: Remove unneeded resetCursorRects: function --- src/events/SDL_mouse.c | 5 +++++ src/video/cocoa/SDL_cocoamouse.m | 14 +++++++++----- src/video/cocoa/SDL_cocoawindow.m | 16 +--------------- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c index fb8deb89e..10a2b7a43 100644 --- a/src/events/SDL_mouse.c +++ b/src/events/SDL_mouse.c @@ -1260,6 +1260,11 @@ void SDL_SetCursor(SDL_Cursor *cursor) { SDL_Mouse *mouse = SDL_GetMouse(); + /* Return immediately if setting the cursor to the currently set one (fixes #7151) */ + if (cursor == mouse->cur_cursor) { + return; + } + /* Set the new cursor */ if (cursor) { /* Make sure the cursor is still valid for this mouse */ diff --git a/src/video/cocoa/SDL_cocoamouse.m b/src/video/cocoa/SDL_cocoamouse.m index 9fae96684..2dd1c0c92 100644 --- a/src/video/cocoa/SDL_cocoamouse.m +++ b/src/video/cocoa/SDL_cocoamouse.m @@ -219,11 +219,15 @@ static int Cocoa_ShowCursor(SDL_Cursor *cursor) SDL_VideoDevice *device = SDL_GetVideoDevice(); SDL_Window *window = (device ? device->windows : NULL); for (; window != NULL; window = window->next) { - SDL_WindowData *driverdata = window->driverdata; - if (driverdata) { - [driverdata.nswindow performSelectorOnMainThread:@selector(invalidateCursorRectsForView:) - withObject:[driverdata.nswindow contentView] - waitUntilDone:NO]; + SDL_Mouse *mouse = SDL_GetMouse(); + if(mouse->focus) { + if (mouse->cursor_shown && mouse->cur_cursor && !mouse->relative_mode) { + [(__bridge NSCursor*)mouse->cur_cursor->driverdata set]; + } else { + [[NSCursor invisibleCursor] set]; + } + } else { + [[NSCursor arrowCursor] set]; } } return 0; diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m index 3b58e58f7..1b7cdd12e 100644 --- a/src/video/cocoa/SDL_cocoawindow.m +++ b/src/video/cocoa/SDL_cocoawindow.m @@ -1590,21 +1590,6 @@ static int Cocoa_SendMouseButtonClicks(SDL_Mouse *mouse, NSEvent *theEvent, SDL_ return YES; } -- (void)resetCursorRects -{ - SDL_Mouse *mouse; - [super resetCursorRects]; - mouse = SDL_GetMouse(); - - if (mouse->cursor_shown && mouse->cur_cursor && !mouse->relative_mode) { - [self addCursorRect:[self bounds] - cursor:(__bridge NSCursor *)mouse->cur_cursor->driverdata]; - } else { - [self addCursorRect:[self bounds] - cursor:[NSCursor invisibleCursor]]; - } -} - - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent { if (SDL_GetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH)) { @@ -1613,6 +1598,7 @@ static int Cocoa_SendMouseButtonClicks(SDL_Mouse *mouse, NSEvent *theEvent, SDL_ return SDL_GetHintBoolean("SDL_MAC_MOUSE_FOCUS_CLICKTHROUGH", SDL_FALSE); } } + @end static int SetupWindowData(_THIS, SDL_Window *window, NSWindow *nswindow, NSView *nsview, SDL_bool created)