macOS mouse scroll fix (#15404)

* macOS: fix vertical/horizontal scrolling on GCMouse API
* macOS: use AppKit events for mouse scrolling as GCMouse scroll events are buggy
This commit is contained in:
Marco Burato
2026-04-20 19:53:25 +02:00
committed by GitHub
parent 0231ff03de
commit 85b36937a9
+20 -4
View File
@@ -33,6 +33,8 @@
#define DEBUG_COCOAMOUSE #define DEBUG_COCOAMOUSE
#endif #endif
//#define USE_GCMOUSE_SCROLL
#ifdef DEBUG_COCOAMOUSE #ifdef DEBUG_COCOAMOUSE
#define DLog(fmt, ...) printf("%s: " fmt "\n", SDL_FUNCTION, ##__VA_ARGS__) #define DLog(fmt, ...) printf("%s: " fmt "\n", SDL_FUNCTION, ##__VA_ARGS__)
#else #else
@@ -262,6 +264,9 @@ static id cocoa_mouse_disconnect_observer = nil;
// Atomic for thread-safe access during high-frequency mouse input // Atomic for thread-safe access during high-frequency mouse input
static SDL_AtomicInt cocoa_gcmouse_relative_mode; static SDL_AtomicInt cocoa_gcmouse_relative_mode;
static bool cocoa_has_gcmouse = false; static bool cocoa_has_gcmouse = false;
#ifdef USE_GCMOUSE_SCROLL
static SDL_MouseWheelDirection cocoa_mouse_scroll_direction = SDL_MOUSEWHEEL_NORMAL; static SDL_MouseWheelDirection cocoa_mouse_scroll_direction = SDL_MOUSEWHEEL_NORMAL;
static void Cocoa_UpdateGCMouseScrollDirection(void) static void Cocoa_UpdateGCMouseScrollDirection(void)
@@ -281,6 +286,7 @@ static void Cocoa_UpdateGCMouseScrollDirection(void)
cocoa_mouse_scroll_direction = SDL_MOUSEWHEEL_NORMAL; cocoa_mouse_scroll_direction = SDL_MOUSEWHEEL_NORMAL;
} }
} }
#endif
static bool Cocoa_SetGCMouseRelativeMode(bool enabled) static bool Cocoa_SetGCMouseRelativeMode(bool enabled)
{ {
@@ -348,13 +354,20 @@ static void Cocoa_OnGCMouseConnected(GCMouse *mouse)
} }
}; };
#ifdef USE_GCMOUSE_SCROLL
/*
18/04/2026
There seems to be a bug in the CGMouse API, at least when using some mouse types.
An event is fired only for the first scroll in one direction. Repeated 1-step
scrolls in the same direction do not raise an event.
Observed on macOS 26.3.1 with 2 different USB mice.
*/
mouse.mouseInput.scroll.valueChangedHandler = mouse.mouseInput.scroll.valueChangedHandler =
^(GCControllerDirectionPad *dpad, float xValue, float yValue) { ^(GCControllerDirectionPad *dpad, float xValue, float yValue) {
DLog("GCMouse scroll: %f, %f", xValue, yValue);
Uint64 timestamp = SDL_GetTicksNS(); Uint64 timestamp = SDL_GetTicksNS();
// Raw scroll values: vertical in first axis, horizontal in second. float vertical = yValue;
// Vertical values are inverted compared to SDL conventions. float horizontal = xValue;
float vertical = -xValue;
float horizontal = yValue;
if (cocoa_mouse_scroll_direction == SDL_MOUSEWHEEL_FLIPPED) { if (cocoa_mouse_scroll_direction == SDL_MOUSEWHEEL_FLIPPED) {
vertical = -vertical; vertical = -vertical;
@@ -365,6 +378,7 @@ static void Cocoa_OnGCMouseConnected(GCMouse *mouse)
cocoa_mouse_scroll_direction); cocoa_mouse_scroll_direction);
}; };
Cocoa_UpdateGCMouseScrollDirection(); Cocoa_UpdateGCMouseScrollDirection();
#endif // USE_GCMOUSE_SCROLL
// Use high-priority queue for low-latency input // Use high-priority queue for low-latency input
dispatch_queue_t queue = dispatch_queue_create("org.libsdl.input.mouse", dispatch_queue_t queue = dispatch_queue_create("org.libsdl.input.mouse",
@@ -852,10 +866,12 @@ void Cocoa_HandleMouseEvent(SDL_VideoDevice *_this, NSEvent *event)
void Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event) void Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event)
{ {
#ifdef USE_GCMOUSE_SCROLL
// GCMouse handles scroll events directly, skip NSEvent path to avoid duplicates // GCMouse handles scroll events directly, skip NSEvent path to avoid duplicates
if (Cocoa_HasGCMouse()) { if (Cocoa_HasGCMouse()) {
return; return;
} }
#endif
SDL_MouseID mouseID = SDL_DEFAULT_MOUSE_ID; SDL_MouseID mouseID = SDL_DEFAULT_MOUSE_ID;
SDL_MouseWheelDirection direction; SDL_MouseWheelDirection direction;