mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-05 03:50:07 +00:00
Linux: Refactored InternalMessageQueue
This commit simplifies the Linux event loop code and adds methods to allow arbitrary file descriptors to registered. Also adds a method to register external event loops to be used instead of JUCE's internal event loop.
This commit is contained in:
parent
d8e07dca91
commit
fd76cbc70d
4 changed files with 153 additions and 143 deletions
|
|
@ -25,31 +25,23 @@ namespace juce
|
|||
|
||||
namespace LinuxEventLoop
|
||||
{
|
||||
struct CallbackFunctionBase
|
||||
{
|
||||
virtual ~CallbackFunctionBase() {}
|
||||
virtual bool operator()(int fd) = 0;
|
||||
bool active = true;
|
||||
};
|
||||
/** Registers a callback that will be called when a file descriptor is ready for I/O.
|
||||
|
||||
template <typename FdCallbackFunction>
|
||||
struct CallbackFunction : public CallbackFunctionBase
|
||||
{
|
||||
FdCallbackFunction callback;
|
||||
This will add the given file descriptor to the internal set of file descriptors
|
||||
that will be passed to the poll() call. When this file descriptor has data to read
|
||||
the readCallback will be called.
|
||||
|
||||
CallbackFunction (FdCallbackFunction c) : callback (c) {}
|
||||
@param fd the file descriptor to be monitored
|
||||
@param readCallback a callback that will be called when the file descriptor has
|
||||
data to read. The file descriptor will be passed as an argument
|
||||
*/
|
||||
void registerFdCallback (int fd, std::function<void(int)> readCallback);
|
||||
|
||||
bool operator() (int fd) override { return callback (fd); }
|
||||
};
|
||||
/** Unregisters a previously registered file descriptor.
|
||||
|
||||
template <typename FdCallbackFunction>
|
||||
void setWindowSystemFd (int fd, FdCallbackFunction readCallback)
|
||||
{
|
||||
setWindowSystemFdInternal (fd, new CallbackFunction<FdCallbackFunction> (readCallback));
|
||||
}
|
||||
void removeWindowSystemFd() noexcept;
|
||||
|
||||
void setWindowSystemFdInternal (int fd, CallbackFunctionBase* readCallback) noexcept;
|
||||
@see registerFdCallback
|
||||
*/
|
||||
void unregisterFdCallback (int fd);
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -22,13 +22,6 @@
|
|||
|
||||
#include <poll.h>
|
||||
|
||||
enum FdType
|
||||
{
|
||||
INTERNAL_QUEUE_FD,
|
||||
WINDOW_SYSTEM_FD,
|
||||
FD_COUNT,
|
||||
};
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
|
|
@ -38,30 +31,28 @@ class InternalMessageQueue
|
|||
public:
|
||||
InternalMessageQueue()
|
||||
{
|
||||
auto ret = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, fd);
|
||||
ignoreUnused (ret); jassert (ret == 0);
|
||||
auto err = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, msgpipe);
|
||||
jassert (err == 0);
|
||||
ignoreUnused (err);
|
||||
|
||||
auto internalQueueCb = [this] (int _fd)
|
||||
{
|
||||
if (const MessageManager::MessageBase::Ptr msg = this->popNextMessage (_fd))
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
msg->messageCallback();
|
||||
return true;
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
pfds[INTERNAL_QUEUE_FD].fd = getReadHandle();
|
||||
pfds[INTERNAL_QUEUE_FD].events = POLLIN;
|
||||
readCallback[INTERNAL_QUEUE_FD].reset (new LinuxEventLoop::CallbackFunction<decltype(internalQueueCb)> (internalQueueCb));
|
||||
LinuxEventLoop::registerFdCallback (getReadHandle(),
|
||||
[this] (int fd)
|
||||
{
|
||||
if (auto msg = popNextMessage (fd))
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
msg->messageCallback();
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
~InternalMessageQueue()
|
||||
{
|
||||
LinuxEventLoop::unregisterFdCallback (getReadHandle());
|
||||
|
||||
close (getReadHandle());
|
||||
close (getWriteHandle());
|
||||
|
||||
|
|
@ -71,64 +62,14 @@ public:
|
|||
//==============================================================================
|
||||
void postMessage (MessageManager::MessageBase* const msg) noexcept
|
||||
{
|
||||
ScopedLock sl (lock);
|
||||
queue.add (msg);
|
||||
|
||||
const int maxBytesInSocketQueue = 128;
|
||||
|
||||
if (bytesInSocket < maxBytesInSocketQueue)
|
||||
{
|
||||
bytesInSocket++;
|
||||
|
||||
ScopedUnlock ul (lock);
|
||||
const unsigned char x = 0xff;
|
||||
ssize_t bytesWritten = write (getWriteHandle(), &x, 1);
|
||||
ignoreUnused (bytesWritten);
|
||||
}
|
||||
}
|
||||
|
||||
void setWindowSystemFd (int _fd, LinuxEventLoop::CallbackFunctionBase* _readCallback)
|
||||
{
|
||||
jassert (fdCount == 1);
|
||||
|
||||
ScopedLock sl (lock);
|
||||
|
||||
fdCount = 2;
|
||||
pfds[WINDOW_SYSTEM_FD].fd = _fd;
|
||||
pfds[WINDOW_SYSTEM_FD].events = POLLIN;
|
||||
readCallback[WINDOW_SYSTEM_FD].reset (_readCallback);
|
||||
readCallback[WINDOW_SYSTEM_FD]->active = true;
|
||||
}
|
||||
|
||||
void removeWindowSystemFd()
|
||||
{
|
||||
jassert (fdCount == FD_COUNT);
|
||||
|
||||
ScopedLock sl (lock);
|
||||
|
||||
fdCount = 1;
|
||||
readCallback[WINDOW_SYSTEM_FD]->active = false;
|
||||
}
|
||||
|
||||
bool dispatchNextEvent() noexcept
|
||||
{
|
||||
for (int counter = 0; counter < fdCount; counter++)
|
||||
{
|
||||
const int i = loopCount++;
|
||||
loopCount %= fdCount;
|
||||
|
||||
if (readCallback[i] != nullptr && readCallback[i]->active)
|
||||
if ((*readCallback[i]) (pfds[i].fd))
|
||||
return true;
|
||||
ScopedLock sl (lock);
|
||||
queue.add (msg);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sleepUntilEvent (const int timeoutMs)
|
||||
{
|
||||
const int pnum = poll (pfds, static_cast<nfds_t> (fdCount), timeoutMs);
|
||||
return (pnum > 0);
|
||||
unsigned char x = 0xff;
|
||||
auto numBytes = write (getWriteHandle(), &x, 1);
|
||||
ignoreUnused (numBytes);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -137,43 +78,114 @@ public:
|
|||
private:
|
||||
CriticalSection lock;
|
||||
ReferenceCountedArray <MessageManager::MessageBase> queue;
|
||||
int fd[2];
|
||||
pollfd pfds[FD_COUNT];
|
||||
std::unique_ptr<LinuxEventLoop::CallbackFunctionBase> readCallback[FD_COUNT];
|
||||
int fdCount = 1;
|
||||
int loopCount = 0;
|
||||
int bytesInSocket = 0;
|
||||
|
||||
int getWriteHandle() const noexcept { return fd[0]; }
|
||||
int getReadHandle() const noexcept { return fd[1]; }
|
||||
int msgpipe[2];
|
||||
|
||||
MessageManager::MessageBase::Ptr popNextMessage (int _fd) noexcept
|
||||
int getWriteHandle() const noexcept { return msgpipe[0]; }
|
||||
int getReadHandle() const noexcept { return msgpipe[1]; }
|
||||
|
||||
MessageManager::MessageBase::Ptr popNextMessage (int fd) noexcept
|
||||
{
|
||||
unsigned char x;
|
||||
auto numBytes = read (fd, &x, 1);
|
||||
ignoreUnused (numBytes);
|
||||
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (bytesInSocket > 0)
|
||||
{
|
||||
--bytesInSocket;
|
||||
|
||||
const ScopedUnlock ul (lock);
|
||||
unsigned char x;
|
||||
ssize_t numBytes = read (_fd, &x, 1);
|
||||
ignoreUnused (numBytes);
|
||||
}
|
||||
|
||||
return queue.removeAndReturn (0);
|
||||
}
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (InternalMessageQueue)
|
||||
|
||||
//==============================================================================
|
||||
struct InternalRunLoop
|
||||
{
|
||||
public:
|
||||
InternalRunLoop()
|
||||
{
|
||||
fdReadCallbacks.reserve (8);
|
||||
}
|
||||
|
||||
void registerFdCallback (int fd, std::function<void(int)>&& cb)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
fdReadCallbacks.push_back ({ fd, std::move (cb) });
|
||||
pfds.push_back ({ fd, POLLIN, 0 });
|
||||
}
|
||||
|
||||
void unregisterFdCallback (int fd)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
{
|
||||
auto removePredicate = [=] (const std::pair<int, std::function<void(int)>>& cb) { return cb.first == fd; };
|
||||
|
||||
fdReadCallbacks.erase (std::remove_if (std::begin (fdReadCallbacks), std::end (fdReadCallbacks), removePredicate),
|
||||
std::end (fdReadCallbacks));
|
||||
}
|
||||
|
||||
{
|
||||
auto removePredicate = [=] (const pollfd& pfd) { return pfd.fd == fd; };
|
||||
|
||||
pfds.erase (std::remove_if (std::begin (pfds), std::end (pfds), removePredicate),
|
||||
std::end (pfds));
|
||||
}
|
||||
}
|
||||
|
||||
bool dispatchPendingEvents()
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (poll (&pfds.front(), static_cast<nfds_t> (pfds.size()), 0) == 0)
|
||||
return false;
|
||||
|
||||
bool eventWasSent = false;
|
||||
|
||||
for (auto& pfd : pfds)
|
||||
{
|
||||
if (pfd.revents == 0)
|
||||
continue;
|
||||
|
||||
pfd.revents = 0;
|
||||
|
||||
auto fd = pfd.fd;
|
||||
|
||||
for (auto& fdAndCallback : fdReadCallbacks)
|
||||
{
|
||||
if (fdAndCallback.first == fd)
|
||||
{
|
||||
fdAndCallback.second (fd);
|
||||
eventWasSent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return eventWasSent;
|
||||
}
|
||||
|
||||
void sleepUntilNextEvent (int timeoutMs)
|
||||
{
|
||||
poll (&pfds.front(), static_cast<nfds_t> (pfds.size()), timeoutMs);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (InternalRunLoop)
|
||||
|
||||
private:
|
||||
CriticalSection lock;
|
||||
|
||||
std::vector<std::pair<int, std::function<void(int)>>> fdReadCallbacks;
|
||||
std::vector<pollfd> pfds;
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (InternalRunLoop)
|
||||
|
||||
//==============================================================================
|
||||
namespace LinuxErrorHandling
|
||||
{
|
||||
static bool keyboardBreakOccurred = false;
|
||||
|
||||
//==============================================================================
|
||||
void keyboardBreakSignalHandler (int sig)
|
||||
{
|
||||
if (sig == SIGINT)
|
||||
|
|
@ -198,14 +210,14 @@ void MessageManager::doPlatformSpecificInitialisation()
|
|||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
LinuxErrorHandling::installKeyboardBreakHandler();
|
||||
|
||||
// Create the internal message queue
|
||||
auto* queue = InternalMessageQueue::getInstance();
|
||||
ignoreUnused (queue);
|
||||
InternalRunLoop::getInstance();
|
||||
InternalMessageQueue::getInstance();
|
||||
}
|
||||
|
||||
void MessageManager::doPlatformSpecificShutdown()
|
||||
{
|
||||
InternalMessageQueue::deleteInstance();
|
||||
InternalRunLoop::deleteInstance();
|
||||
}
|
||||
|
||||
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
|
||||
|
|
@ -232,16 +244,15 @@ bool MessageManager::dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMes
|
|||
if (LinuxErrorHandling::keyboardBreakOccurred)
|
||||
JUCEApplicationBase::getInstance()->quit();
|
||||
|
||||
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
|
||||
{
|
||||
if (queue->dispatchNextEvent())
|
||||
if (runLoop->dispatchPendingEvents())
|
||||
break;
|
||||
|
||||
if (returnIfNoPendingMessages)
|
||||
return false;
|
||||
|
||||
// wait for 2000ms for next events if necessary
|
||||
queue->sleepUntilEvent (2000);
|
||||
runLoop->sleepUntilNextEvent (2000);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -249,17 +260,16 @@ bool MessageManager::dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMes
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
void LinuxEventLoop::setWindowSystemFdInternal (int fd, LinuxEventLoop::CallbackFunctionBase* readCallback) noexcept
|
||||
void LinuxEventLoop::registerFdCallback (int fd, std::function<void(int)> readCallback)
|
||||
{
|
||||
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
queue->setWindowSystemFd (fd, readCallback);
|
||||
if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
|
||||
runLoop->registerFdCallback (fd, std::move (readCallback));
|
||||
}
|
||||
|
||||
void LinuxEventLoop::removeWindowSystemFd() noexcept
|
||||
void LinuxEventLoop::unregisterFdCallback (int fd)
|
||||
{
|
||||
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
queue->removeWindowSystemFd();
|
||||
if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
|
||||
runLoop->unregisterFdCallback (fd);
|
||||
}
|
||||
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -190,8 +190,8 @@ void XWindowSystem::initialiseXDisplay() noexcept
|
|||
// Setup input event handler
|
||||
int fd = XConnectionNumber (display);
|
||||
|
||||
LinuxEventLoop::setWindowSystemFd (fd,
|
||||
[this](int /*fd*/)
|
||||
LinuxEventLoop::registerFdCallback (fd,
|
||||
[this](int)
|
||||
{
|
||||
do
|
||||
{
|
||||
|
|
@ -201,7 +201,7 @@ void XWindowSystem::initialiseXDisplay() noexcept
|
|||
ScopedXLock xlock (display);
|
||||
|
||||
if (! XPending (display))
|
||||
return false;
|
||||
return;
|
||||
|
||||
XNextEvent (display, &evt);
|
||||
}
|
||||
|
|
@ -218,8 +218,6 @@ void XWindowSystem::initialiseXDisplay() noexcept
|
|||
}
|
||||
|
||||
} while (display != nullptr);
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -229,7 +227,7 @@ void XWindowSystem::destroyXDisplay() noexcept
|
|||
XDestroyWindow (display, juce_messageWindowHandle);
|
||||
juce_messageWindowHandle = 0;
|
||||
XSync (display, True);
|
||||
LinuxEventLoop::removeWindowSystemFd();
|
||||
LinuxEventLoop::unregisterFdCallback (XConnectionNumber (display));
|
||||
}
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (XWindowSystem)
|
||||
|
|
|
|||
|
|
@ -2250,6 +2250,16 @@ private:
|
|||
void timerCallback() override
|
||||
{
|
||||
#if JUCE_USE_XSHM
|
||||
if (XPending (display))
|
||||
{
|
||||
XEvent e;
|
||||
|
||||
ScopedXLock l (display);
|
||||
|
||||
while (XCheckTypedWindowEvent (display, peer.windowH, XShmGetEventBase (display), &e))
|
||||
--shmPaintsPending;
|
||||
}
|
||||
|
||||
if (shmPaintsPending != 0)
|
||||
return;
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue