SDL_SetCursor doesn't work very well on Mac external monitors
My app wants to change the cursor when the mouse moves into a view in my Appkit-alike framework when the user has selected an icon in the collection view at the bottom.
On the built-in-to-the-laptop screen, the below code works perfectly every time. On either of the 2 external monitors plugged in, it works maybe 2-5% of the time (and once it's not working it seems to stay not working for the duration of the app). I can't find anything that correlates to it "working" on the external monitors.
- (void) _setCursor
{
NSLog(@"setting cursor");
if (_cursor)
SDL_DestroyCursor(_cursor);
if (_cursorImage == nil)
SDL_Log("no image!");
SDL_SetCursor(_arrowCursor);
else
{
int W = _cursorImage.width * _zoomFactor[_zoom];
int H = _cursorImage.height * _zoomFactor[_zoom];
SDL_Surface *scaled = SDL_CreateSurface(W, H, SDL_PIXELFORMAT_ABGR8888);
id<AZRenderer> azr = AZRenderer.renderer;
SDL_Surface *surface = [azr surfaceFor:_cursorImage.texture];
SDL_Rect srcRect = SDL_RECT(_cursorImage.srcRect);
SDL_BlitSurfaceScaled(surface, &srcRect,
scaled, NULL,
SDL_SCALEMODE_LINEAR);
SDL_DestroySurface(surface);
_cursor = SDL_CreateColorCursor(scaled, 0, 0);
if (!SDL_SetCursor(_cursor))
SDL_Log("setcursor failed: %s", SDL_GetError());
SDL_DestroySurface(scaled);
}
}
The call to SDL_SetCursor always succeeds, and in fact it looks as though the cursor is being set on the external monitor, but as soon as I move the mouse, it is reset to the arrow again - see attached video. I did manage to capture the single frame when the mouse doesn't move for a while, and the cursor stayed set, but that was a large video to upload :)
https://github.com/user-attachments/assets/35286da6-e93b-40b2-833d-2d068d457218
I added logging to every call of SDL_SetCursor in my app, and it never calls anything other than the above. In the debugger, the values always look fine (which again agrees with the set-then-reset theory).
Ok, some new findings...
- Working normally on the MBP screen
- On running the app from Xcode , it launches on the MBP built-in screen.
- Clicking through the options until I get to where the cursor is set, it all works fine while on the MBP screen
- Not working on the 4K external monitor
- On running the app from Xcode , it launches on the MBP built-in screen.
- Dragging the window over to the 4K monitor, and the cursor won't be set
But...
2a) Fixing itself on the 4K external monitor...
- Moving the mouse back to the MBP screen then back over to the 4K screen, and the cursor is now reliably set.
Could there be some display-related state that isn't being set when the window moves to a different screen ?
So my thinking, which I haven't been able to test yet, is that moving to a new screen either isn't calling -[NSView resetCursorRects], or our implementation...
- (void)resetCursorRects
{
[super resetCursorRects];
[self addCursorRect:[self bounds]
cursor:Cocoa_GetDesiredCursor()];
}
...doesn't have the right results from [self bounds] at the time of the call. Maybe it's possible some corner of the app window works, and didn't scale out for the 4K resolution difference?
(also, documentation suggests we should explicitly call [self discardCursorRects]; in here, first, and [super resetCursorRects]; does nothing...so maybe the old cursor rectangle is interfering?)
Tried this on a MacBook Pro with a 4K display plugged into its HDMI port, and I couldn't reproduce the issue with testcustomcursor.c. Is there a build of your project I could try?
is that moving to a new screen either isn't calling -[NSView resetCursorRects]
(This definitely wasn't true, btw. Cocoa is calling this message way more than I expected it to.)