From ea642fe9ff17e90a0ccc0ec95d007859c4ab02bc Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Wed, 15 Jan 2025 23:34:20 -0800
Subject: [PATCH] cocoa: clear mouse focus based on NSEventTypeMouseExited
events (#11991)
We can't directly set the mouse focus since we may get spammed by entered/exited events,
but we can process the current focus later in the mouseMoved handler in line with the
mouse motion event sequence.
Fixes https://github.com/libsdl-org/SDL/issues/8188
---
src/video/cocoa/SDL_cocoaevents.m | 2 ++
src/video/cocoa/SDL_cocoamouse.h | 1 +
src/video/cocoa/SDL_cocoamouse.m | 28 ++++++++++++++++++++++------
src/video/cocoa/SDL_cocoawindow.m | 6 ++++++
4 files changed, 31 insertions(+), 6 deletions(-)
diff --git a/src/video/cocoa/SDL_cocoaevents.m b/src/video/cocoa/SDL_cocoaevents.m
index 74c5c192b5681..58cae9955460c 100644
--- a/src/video/cocoa/SDL_cocoaevents.m
+++ b/src/video/cocoa/SDL_cocoaevents.m
@@ -76,6 +76,8 @@ static void Cocoa_DispatchEvent(NSEvent *theEvent)
case NSEventTypeOtherMouseDragged: // usually middle mouse dragged
case NSEventTypeMouseMoved:
case NSEventTypeScrollWheel:
+ case NSEventTypeMouseEntered:
+ case NSEventTypeMouseExited:
Cocoa_HandleMouseEvent(_this, theEvent);
break;
case NSEventTypeKeyDown:
diff --git a/src/video/cocoa/SDL_cocoamouse.h b/src/video/cocoa/SDL_cocoamouse.h
index 7ddef5968a174..70282be9fa0d4 100644
--- a/src/video/cocoa/SDL_cocoamouse.h
+++ b/src/video/cocoa/SDL_cocoamouse.h
@@ -26,6 +26,7 @@
#include "SDL_cocoavideo.h"
extern bool Cocoa_InitMouse(SDL_VideoDevice *_this);
+extern NSWindow *Cocoa_GetMouseFocus();
extern void Cocoa_HandleMouseEvent(SDL_VideoDevice *_this, NSEvent *event);
extern void Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event);
extern void Cocoa_HandleMouseWarp(CGFloat x, CGFloat y);
diff --git a/src/video/cocoa/SDL_cocoamouse.m b/src/video/cocoa/SDL_cocoamouse.m
index 38297f40830fb..77ebf109d02fa 100644
--- a/src/video/cocoa/SDL_cocoamouse.m
+++ b/src/video/cocoa/SDL_cocoamouse.m
@@ -27,7 +27,9 @@
#include "../../events/SDL_mouse_c.h"
-// #define DEBUG_COCOAMOUSE
+#if 0
+#define DEBUG_COCOAMOUSE
+#endif
#ifdef DEBUG_COCOAMOUSE
#define DLog(fmt, ...) printf("%s: " fmt "\n", __func__, ##__VA_ARGS__)
@@ -230,11 +232,11 @@ static bool Cocoa_ShowCursor(SDL_Cursor *cursor)
SDL_VideoDevice *device = SDL_GetVideoDevice();
SDL_Window *window = (device ? device->windows : NULL);
for (; window != NULL; window = window->next) {
- SDL_CocoaWindowData *internal = (__bridge SDL_CocoaWindowData *)window->internal;
- if (internal) {
- [internal.nswindow performSelectorOnMainThread:@selector(invalidateCursorRectsForView:)
- withObject:[internal.nswindow contentView]
- waitUntilDone:NO];
+ SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
+ if (data) {
+ [data.nswindow performSelectorOnMainThread:@selector(invalidateCursorRectsForView:)
+ withObject:[data.nswindow contentView]
+ waitUntilDone:NO];
}
}
return true;
@@ -428,6 +430,13 @@ static void Cocoa_HandleTitleButtonEvent(SDL_VideoDevice *_this, NSEvent *event)
}
}
+static NSWindow *Cocoa_MouseFocus;
+
+NSWindow *Cocoa_GetMouseFocus()
+{
+ return Cocoa_MouseFocus;
+}
+
void Cocoa_HandleMouseEvent(SDL_VideoDevice *_this, NSEvent *event)
{
SDL_MouseID mouseID = SDL_DEFAULT_MOUSE_ID;
@@ -437,7 +446,14 @@ void Cocoa_HandleMouseEvent(SDL_VideoDevice *_this, NSEvent *event)
CGFloat lastMoveX, lastMoveY;
float deltaX, deltaY;
bool seenWarp;
+
switch ([event type]) {
+ case NSEventTypeMouseEntered:
+ Cocoa_MouseFocus = [event window];
+ return;
+ case NSEventTypeMouseExited:
+ Cocoa_MouseFocus = NULL;
+ return;
case NSEventTypeMouseMoved:
case NSEventTypeLeftMouseDragged:
case NSEventTypeRightMouseDragged:
diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m
index 48cc065ec40ff..c58e851b30129 100644
--- a/src/video/cocoa/SDL_cocoawindow.m
+++ b/src/video/cocoa/SDL_cocoawindow.m
@@ -1776,6 +1776,12 @@ - (void)mouseMoved:(NSEvent *)theEvent
return;
}
+ if (!Cocoa_GetMouseFocus()) {
+ // The mouse is no longer over any window in the application
+ SDL_SetMouseFocus(NULL);
+ return;
+ }
+
window = _data.window;
contentView = _data.sdlContentView;
point = [theEvent locationInWindow];