fix(mac): Remove Cocoa app loop and fix macOS event tap for Qt threading

This commit is contained in:
Nick Bolton 2026-03-30 19:08:08 +01:00
parent 29352d97b3
commit aaef600ffa
7 changed files with 30 additions and 44 deletions

View file

@ -23,7 +23,6 @@
#include <stdexcept> #include <stdexcept>
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
#include "platform/OSXCocoaApp.h"
#include <ApplicationServices/ApplicationServices.h> #include <ApplicationServices/ApplicationServices.h>
#endif #endif
@ -172,7 +171,4 @@ void App::handleScreenError() const
void App::runEventsLoop(const void *) void App::runEventsLoop(const void *)
{ {
m_events->loop(); m_events->loop();
#if defined(Q_OS_MAC)
stopCocoaLoop();
#endif
} }

View file

@ -37,9 +37,6 @@
#endif #endif
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
#include "base/TMethodJob.h"
#include "mt/Thread.h"
#include "platform/OSXCocoaApp.h"
#include "platform/OSXScreen.h" #include "platform/OSXScreen.h"
#endif #endif
@ -330,17 +327,7 @@ int ClientApp::mainLoop()
// later. the timer installed by startClient() will take care of // later. the timer installed by startClient() will take care of
// that. // that.
#if defined(Q_OS_MAC)
Thread thread(new TMethodJob<ClientApp>(this, &ClientApp::runEventsLoop, nullptr));
// wait until carbon loop is ready
OSXScreen *screen = dynamic_cast<OSXScreen *>(m_clientScreen->getPlatformScreen());
screen->waitForCarbonLoop();
runCocoaApp();
#else
getEvents()->loop(); getEvents()->loop();
#endif
// close down // close down
LOG_DEBUG("stopping client"); LOG_DEBUG("stopping client");

View file

@ -43,9 +43,6 @@
#endif #endif
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
#include "base/TMethodJob.h"
#include "mt/Thread.h"
#include "platform/OSXCocoaApp.h"
#include "platform/OSXScreen.h" #include "platform/OSXScreen.h"
#endif #endif
@ -541,18 +538,7 @@ int ServerApp::mainLoop()
// later. the timer installed by startServer() will take care of // later. the timer installed by startServer() will take care of
// that. // that.
#if defined(Q_OS_MAC)
Thread thread(new TMethodJob<ServerApp>(this, &ServerApp::runEventsLoop, nullptr));
// wait until carbon loop is ready
OSXScreen *screen = dynamic_cast<OSXScreen *>(m_serverScreen->getPlatformScreen());
screen->waitForCarbonLoop();
runCocoaApp();
#else
getEvents()->loop(); getEvents()->loop();
#endif
// close down // close down
LOG_DEBUG("stopping server"); LOG_DEBUG("stopping server");

View file

@ -58,16 +58,11 @@ IEventQueueBuffer::Type OSXEventQueueBuffer::getEvent(Event &event, uint32_t &da
bool OSXEventQueueBuffer::addEvent(uint32_t dataID) bool OSXEventQueueBuffer::addEvent(uint32_t dataID)
{ {
// Use GCD to dispatch event addition on the main queue std::scoped_lock lock{m_mutex};
dispatch_async(dispatch_get_main_queue(), ^{ LOG_DEBUG2("adding user event with dataID: %u", dataID);
std::scoped_lock lock{this->m_mutex}; m_dataQueue.push(dataID);
LOG_DEBUG2("adding user event with dataID: %u", dataID); m_cond.notify_one();
this->m_dataQueue.push(dataID); LOG_DEBUG2("user event added to queue, dataID=%u", 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
return true; return true;
} }

View file

@ -12,7 +12,6 @@
#include "base/IEventQueueBuffer.h" #include "base/IEventQueueBuffer.h"
#include <condition_variable> #include <condition_variable>
#include <dispatch/dispatch.h>
#include <mutex> #include <mutex>
#include <queue> #include <queue>

View file

@ -21,6 +21,7 @@
#include <bitset> #include <bitset>
#include <map> #include <map>
#include <memory> #include <memory>
#include <thread>
#include <vector> #include <vector>
extern "C" extern "C"
@ -292,6 +293,8 @@ private:
// Quartz input event support // Quartz input event support
CFMachPortRef m_eventTapPort; CFMachPortRef m_eventTapPort;
CFRunLoopSourceRef m_eventTapRLSR; CFRunLoopSourceRef m_eventTapRLSR;
std::thread m_eventTapThread;
CFRunLoopRef m_eventTapRunLoop = nullptr;
// for double click coalescing. // for double click coalescing.
double m_lastClickTime; double m_lastClickTime;

View file

@ -34,6 +34,7 @@
#include <AppKit/NSEvent.h> #include <AppKit/NSEvent.h>
#include <AvailabilityMacros.h> #include <AvailabilityMacros.h>
#include <IOKit/hidsystem/event_status_driver.h> #include <IOKit/hidsystem/event_status_driver.h>
#include <dispatch/dispatch.h>
#include <libproc.h> #include <libproc.h>
#include <mach-o/dyld.h> #include <mach-o/dyld.h>
#include <math.h> #include <math.h>
@ -686,7 +687,20 @@ void OSXScreen::enable()
if (m_eventTapPort) { if (m_eventTapPort) {
m_eventTapRLSR = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, m_eventTapPort, 0); m_eventTapRLSR = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, m_eventTapPort, 0);
if (m_eventTapRLSR) { 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 { } else {
LOG_ERR("failed to create a CFRunLoopSourceRef for the quartz event tap"); 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 // FIXME -- stop watching jump zones, stop capturing input
if (m_eventTapRunLoop) {
CFRunLoopStop(m_eventTapRunLoop);
}
if (m_eventTapThread.joinable()) {
m_eventTapThread.join();
}
if (m_eventTapRLSR) { if (m_eventTapRLSR) {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m_eventTapRLSR, kCFRunLoopDefaultMode);
CFRelease(m_eventTapRLSR); CFRelease(m_eventTapRLSR);
m_eventTapRLSR = nullptr; m_eventTapRLSR = nullptr;
} }