From aaef600ffa538f775a9dfff74d61516a6d8a8013 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 30 Mar 2026 19:08:08 +0100 Subject: [PATCH] fix(mac): Remove Cocoa app loop and fix macOS event tap for Qt threading --- src/lib/deskflow/App.cpp | 4 ---- src/lib/deskflow/ClientApp.cpp | 13 ------------- src/lib/deskflow/ServerApp.cpp | 14 -------------- src/lib/platform/OSXEventQueueBuffer.cpp | 15 +++++---------- src/lib/platform/OSXEventQueueBuffer.h | 1 - src/lib/platform/OSXScreen.h | 3 +++ src/lib/platform/OSXScreen.mm | 24 ++++++++++++++++++++++-- 7 files changed, 30 insertions(+), 44 deletions(-) diff --git a/src/lib/deskflow/App.cpp b/src/lib/deskflow/App.cpp index f6e23b60a..36a00b6f7 100644 --- a/src/lib/deskflow/App.cpp +++ b/src/lib/deskflow/App.cpp @@ -23,7 +23,6 @@ #include #if defined(Q_OS_MAC) -#include "platform/OSXCocoaApp.h" #include #endif @@ -172,7 +171,4 @@ void App::handleScreenError() const void App::runEventsLoop(const void *) { m_events->loop(); -#if defined(Q_OS_MAC) - stopCocoaLoop(); -#endif } diff --git a/src/lib/deskflow/ClientApp.cpp b/src/lib/deskflow/ClientApp.cpp index 7dcbf131b..08cce6d1b 100644 --- a/src/lib/deskflow/ClientApp.cpp +++ b/src/lib/deskflow/ClientApp.cpp @@ -37,9 +37,6 @@ #endif #if defined(Q_OS_MAC) -#include "base/TMethodJob.h" -#include "mt/Thread.h" -#include "platform/OSXCocoaApp.h" #include "platform/OSXScreen.h" #endif @@ -330,17 +327,7 @@ int ClientApp::mainLoop() // later. the timer installed by startClient() will take care of // that. -#if defined(Q_OS_MAC) - Thread thread(new TMethodJob(this, &ClientApp::runEventsLoop, nullptr)); - - // wait until carbon loop is ready - OSXScreen *screen = dynamic_cast(m_clientScreen->getPlatformScreen()); - screen->waitForCarbonLoop(); - - runCocoaApp(); -#else getEvents()->loop(); -#endif // close down LOG_DEBUG("stopping client"); diff --git a/src/lib/deskflow/ServerApp.cpp b/src/lib/deskflow/ServerApp.cpp index 15c27a480..3b5c2e58b 100644 --- a/src/lib/deskflow/ServerApp.cpp +++ b/src/lib/deskflow/ServerApp.cpp @@ -43,9 +43,6 @@ #endif #if defined(Q_OS_MAC) -#include "base/TMethodJob.h" -#include "mt/Thread.h" -#include "platform/OSXCocoaApp.h" #include "platform/OSXScreen.h" #endif @@ -541,18 +538,7 @@ int ServerApp::mainLoop() // later. the timer installed by startServer() will take care of // that. -#if defined(Q_OS_MAC) - - Thread thread(new TMethodJob(this, &ServerApp::runEventsLoop, nullptr)); - - // wait until carbon loop is ready - OSXScreen *screen = dynamic_cast(m_serverScreen->getPlatformScreen()); - screen->waitForCarbonLoop(); - - runCocoaApp(); -#else getEvents()->loop(); -#endif // close down LOG_DEBUG("stopping server"); diff --git a/src/lib/platform/OSXEventQueueBuffer.cpp b/src/lib/platform/OSXEventQueueBuffer.cpp index 9389e4ef9..ada237c92 100644 --- a/src/lib/platform/OSXEventQueueBuffer.cpp +++ b/src/lib/platform/OSXEventQueueBuffer.cpp @@ -58,16 +58,11 @@ IEventQueueBuffer::Type OSXEventQueueBuffer::getEvent(Event &event, uint32_t &da bool OSXEventQueueBuffer::addEvent(uint32_t dataID) { - // Use GCD to dispatch event addition on the main queue - dispatch_async(dispatch_get_main_queue(), ^{ - std::scoped_lock lock{this->m_mutex}; - LOG_DEBUG2("adding user event with dataID: %u", dataID); - this->m_dataQueue.push(dataID); - this->m_cond.notify_one(); - LOG_DEBUG2("user event added to queue, dataID=%u", dataID); - }); - - // Always return true since dispatch_async does not fail under normal conditions + std::scoped_lock lock{m_mutex}; + LOG_DEBUG2("adding user event with dataID: %u", dataID); + m_dataQueue.push(dataID); + m_cond.notify_one(); + LOG_DEBUG2("user event added to queue, dataID=%u", dataID); return true; } diff --git a/src/lib/platform/OSXEventQueueBuffer.h b/src/lib/platform/OSXEventQueueBuffer.h index 0057aa2eb..02efcfed6 100644 --- a/src/lib/platform/OSXEventQueueBuffer.h +++ b/src/lib/platform/OSXEventQueueBuffer.h @@ -12,7 +12,6 @@ #include "base/IEventQueueBuffer.h" #include -#include #include #include diff --git a/src/lib/platform/OSXScreen.h b/src/lib/platform/OSXScreen.h index 87b5f4f5b..37c48da29 100644 --- a/src/lib/platform/OSXScreen.h +++ b/src/lib/platform/OSXScreen.h @@ -21,6 +21,7 @@ #include #include #include +#include #include extern "C" @@ -292,6 +293,8 @@ private: // Quartz input event support CFMachPortRef m_eventTapPort; CFRunLoopSourceRef m_eventTapRLSR; + std::thread m_eventTapThread; + CFRunLoopRef m_eventTapRunLoop = nullptr; // for double click coalescing. double m_lastClickTime; diff --git a/src/lib/platform/OSXScreen.mm b/src/lib/platform/OSXScreen.mm index 32d23273e..bd6b8829d 100644 --- a/src/lib/platform/OSXScreen.mm +++ b/src/lib/platform/OSXScreen.mm @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -686,7 +687,20 @@ void OSXScreen::enable() if (m_eventTapPort) { m_eventTapRLSR = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, m_eventTapPort, 0); if (m_eventTapRLSR) { - CFRunLoopAddSource(CFRunLoopGetCurrent(), m_eventTapRLSR, kCFRunLoopDefaultMode); + // Run the event tap on a dedicated thread with its own CFRunLoop so it fires + // independently of whatever event loop the calling thread runs (e.g. QCoreApplication). + // Use a semaphore to ensure m_eventTapRunLoop is set before enable() returns. + auto sem = dispatch_semaphore_create(0); + m_eventTapThread = std::thread([this, sem]() { + m_eventTapRunLoop = CFRunLoopGetCurrent(); + CFRunLoopAddSource(m_eventTapRunLoop, m_eventTapRLSR, kCFRunLoopDefaultMode); + dispatch_semaphore_signal(sem); + CFRunLoopRun(); + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m_eventTapRLSR, kCFRunLoopDefaultMode); + m_eventTapRunLoop = nullptr; + }); + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + dispatch_release(sem); } else { LOG_ERR("failed to create a CFRunLoopSourceRef for the quartz event tap"); } @@ -701,8 +715,14 @@ void OSXScreen::disable() // FIXME -- stop watching jump zones, stop capturing input + if (m_eventTapRunLoop) { + CFRunLoopStop(m_eventTapRunLoop); + } + if (m_eventTapThread.joinable()) { + m_eventTapThread.join(); + } + if (m_eventTapRLSR) { - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m_eventTapRLSR, kCFRunLoopDefaultMode); CFRelease(m_eventTapRLSR); m_eventTapRLSR = nullptr; }