Previously, a QTimer was used to start processing of our event queue in
the main Qt event loop. Unfortunately, QTimers are not thread-safe, and
disallow starting of a timer from a different thread than it was
created in.
Instead, use a dummy QObject to post a custom QEvent to the main loop
from whatever thread we like, and process our event queue when it is
received by our dummy object.
This shouldn't have been moved to EventLoopManager, as the manager is
global and one-per-process, and the implementation is one-per-loop.
This makes cross-thread event posting work again, and unbreaks
SoundPlayer (and probably other things as well.)
Things such as timers and notifiers aren't specific to one instance of
Core::EventLoop, so let's not tie them down to EventLoopImplementation.
Instead, move those APIs + signals & a few other things to a new
EventLoopManager interface. EventLoopManager also knows how to create a
new EventLoopImplementation object.
Using QEventLoop works for everything but it breaks *one* little feature
that we care about: automatically quitting the app when all windows have
been closed.
That only works if you drive the outermost main event loop with a
QCoreApplication instead of a QEventLoop. This is unfortunate, as it
complicates our API a little bit, but I'm sure we can think of a way to
make this nicer someday.
In order for QCoreApplication::exec() to process our own
ThreadEventQueue, we now have a zero-timer that we kick whenever new
events are posted to the thread queue.
This patch adds EventLoopImplementationQt which is a full replacement
for the Core::EventLoopImplementationUnix that uses Qt's event loop
as a backend instead.
This means that Core::Timer, Core::Notifier, and Core::Event delivery
are all driven by Qt primitives in the Ladybird UI and WC processes.