From d38c69c05d64d43f430f0b1c4074bf0bf78edf6b Mon Sep 17 00:00:00 2001 From: Luiz Sardinha Date: Sun, 19 Jul 2026 01:51:48 +0200 Subject: [PATCH] fix(mac): ensure background clicks work correctly on macOS 27 --- src/lib/platform/OSXScreen.h | 3 +++ src/lib/platform/OSXScreen.mm | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/lib/platform/OSXScreen.h b/src/lib/platform/OSXScreen.h index 9efd66a01..704d21fde 100644 --- a/src/lib/platform/OSXScreen.h +++ b/src/lib/platform/OSXScreen.h @@ -236,6 +236,9 @@ private: mutable int32_t m_xCursor, m_yCursor; mutable bool m_cursorPosValid; + // mouse event number (needed for macOS 27) + uint32_t m_mouseEventNumber = 0; + /* FIXME: this data structure is explicitly marked mutable due to a need to track the state of buttons since the remote side only lets us know of change events, and because the diff --git a/src/lib/platform/OSXScreen.mm b/src/lib/platform/OSXScreen.mm index 8cbc79531..f4fd158f9 100644 --- a/src/lib/platform/OSXScreen.mm +++ b/src/lib/platform/OSXScreen.mm @@ -547,6 +547,22 @@ void OSXScreen::fakeMouseButton(ButtonID id, bool press) CGEventRef event = CGEventCreateMouseEvent(nullptr, type, pos, static_cast(index)); + if (NSProcessInfo.processInfo.operatingSystemVersion.majorVersion >= 27) { + // Apps that use Carbon APIs on macOS 27 requires an incrementing event number for each synthetic click + if (press) { + if (m_mouseEventNumber == 0) { + // For some unknown reason, the first click event number must be unique and greater (but in the same ballpark) + // than the last event number used by the system. + m_mouseEventNumber = + CGEventSourceCounterForEventType(kCGEventSourceStateHIDSystemState, kCGEventLeftMouseDown) + + CGEventSourceCounterForEventType(kCGEventSourceStateHIDSystemState, kCGEventRightMouseDown) + + CGEventSourceCounterForEventType(kCGEventSourceStateHIDSystemState, kCGEventOtherMouseDown); + } + ++m_mouseEventNumber; + } + CGEventSetIntegerValueField(event, kCGMouseEventNumber, m_mouseEventNumber); + } + CGEventSetIntegerValueField(event, kCGMouseEventClickState, m_clickState); // Fix for sticky keys