mirror of
https://github.com/RGBCube/serenity
synced 2025-05-20 16:55:08 +00:00
Ladybird+LibCore: Use QCoreApplication to drive the main Qt event loop
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 commit is contained in:
parent
0f22dfa634
commit
c21eb30a2b
9 changed files with 79 additions and 25 deletions
|
@ -10,6 +10,7 @@
|
||||||
#include <LibCore/Notifier.h>
|
#include <LibCore/Notifier.h>
|
||||||
#include <LibCore/Object.h>
|
#include <LibCore/Object.h>
|
||||||
#include <LibCore/ThreadEventQueue.h>
|
#include <LibCore/ThreadEventQueue.h>
|
||||||
|
#include <QCoreApplication>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
namespace Ladybird {
|
namespace Ladybird {
|
||||||
|
@ -34,37 +35,47 @@ struct ThreadData {
|
||||||
|
|
||||||
EventLoopImplementationQt::EventLoopImplementationQt()
|
EventLoopImplementationQt::EventLoopImplementationQt()
|
||||||
{
|
{
|
||||||
|
m_process_core_events_timer.setSingleShot(true);
|
||||||
|
m_process_core_events_timer.setInterval(0);
|
||||||
|
QObject::connect(&m_process_core_events_timer, &QTimer::timeout, [] {
|
||||||
|
Core::ThreadEventQueue::current().process();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
EventLoopImplementationQt::~EventLoopImplementationQt() = default;
|
EventLoopImplementationQt::~EventLoopImplementationQt() = default;
|
||||||
|
|
||||||
int EventLoopImplementationQt::exec()
|
int EventLoopImplementationQt::exec()
|
||||||
{
|
{
|
||||||
// NOTE: We don't use QEventLoop::exec() here since it wouldn't process the Core::ThreadEventQueue.
|
if (is_main_loop())
|
||||||
while (!m_exit_code.has_value()) {
|
return QCoreApplication::exec();
|
||||||
pump(PumpMode::WaitForEvents);
|
return m_event_loop.exec();
|
||||||
}
|
|
||||||
return m_exit_code.value();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t EventLoopImplementationQt::pump(PumpMode mode)
|
size_t EventLoopImplementationQt::pump(PumpMode mode)
|
||||||
{
|
{
|
||||||
bool result = Core::ThreadEventQueue::current().process() != 0;
|
auto result = Core::ThreadEventQueue::current().process();
|
||||||
if (mode == PumpMode::WaitForEvents)
|
if (mode == PumpMode::WaitForEvents) {
|
||||||
result |= m_event_loop.processEvents(QEventLoop::WaitForMoreEvents);
|
if (is_main_loop())
|
||||||
|
QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
|
||||||
else
|
else
|
||||||
result |= m_event_loop.processEvents();
|
m_event_loop.processEvents(QEventLoop::WaitForMoreEvents);
|
||||||
Core::ThreadEventQueue::current().process();
|
} else {
|
||||||
|
}
|
||||||
|
result += Core::ThreadEventQueue::current().process();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventLoopImplementationQt::quit(int code)
|
void EventLoopImplementationQt::quit(int code)
|
||||||
{
|
{
|
||||||
m_exit_code = code;
|
if (is_main_loop())
|
||||||
|
QCoreApplication::exit(code);
|
||||||
|
else
|
||||||
|
m_event_loop.exit(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventLoopImplementationQt::wake()
|
void EventLoopImplementationQt::wake()
|
||||||
{
|
{
|
||||||
|
if (!is_main_loop())
|
||||||
m_event_loop.wakeUp();
|
m_event_loop.wakeUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +87,16 @@ void EventLoopImplementationQt::deferred_invoke(Function<void()> function)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qt_timer_fired(int timer_id, Core::TimerShouldFireWhenNotVisible should_fire_when_not_visible, Core::Object& object)
|
||||||
|
{
|
||||||
|
if (should_fire_when_not_visible == Core::TimerShouldFireWhenNotVisible::No) {
|
||||||
|
if (!object.is_visible_for_timer_purposes())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Core::TimerEvent event(timer_id);
|
||||||
|
object.dispatch_event(event);
|
||||||
|
}
|
||||||
|
|
||||||
int EventLoopImplementationQt::register_timer(Core::Object& object, int milliseconds, bool should_reload, Core::TimerShouldFireWhenNotVisible should_fire_when_not_visible)
|
int EventLoopImplementationQt::register_timer(Core::Object& object, int milliseconds, bool should_reload, Core::TimerShouldFireWhenNotVisible should_fire_when_not_visible)
|
||||||
{
|
{
|
||||||
auto& thread_data = ThreadData::the();
|
auto& thread_data = ThreadData::the();
|
||||||
|
@ -88,11 +109,7 @@ int EventLoopImplementationQt::register_timer(Core::Object& object, int millisec
|
||||||
auto object = weak_object.strong_ref();
|
auto object = weak_object.strong_ref();
|
||||||
if (!object)
|
if (!object)
|
||||||
return;
|
return;
|
||||||
if (should_fire_when_not_visible == Core::TimerShouldFireWhenNotVisible::No) {
|
qt_timer_fired(timer_id, should_fire_when_not_visible, *object);
|
||||||
if (!object->is_visible_for_timer_purposes())
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Core::ThreadEventQueue::current().post_event(*object, make<Core::TimerEvent>(timer_id));
|
|
||||||
});
|
});
|
||||||
timer->start();
|
timer->start();
|
||||||
thread_data.timers.set(timer_id, move(timer));
|
thread_data.timers.set(timer_id, move(timer));
|
||||||
|
@ -106,6 +123,12 @@ bool EventLoopImplementationQt::unregister_timer(int timer_id)
|
||||||
return thread_data.timers.remove(timer_id);
|
return thread_data.timers.remove(timer_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qt_notifier_activated(Core::Notifier& notifier)
|
||||||
|
{
|
||||||
|
Core::NotifierActivationEvent event(notifier.fd());
|
||||||
|
notifier.dispatch_event(event);
|
||||||
|
}
|
||||||
|
|
||||||
void EventLoopImplementationQt::register_notifier(Core::Notifier& notifier)
|
void EventLoopImplementationQt::register_notifier(Core::Notifier& notifier)
|
||||||
{
|
{
|
||||||
QSocketNotifier::Type type;
|
QSocketNotifier::Type type;
|
||||||
|
@ -120,8 +143,8 @@ void EventLoopImplementationQt::register_notifier(Core::Notifier& notifier)
|
||||||
TODO();
|
TODO();
|
||||||
}
|
}
|
||||||
auto socket_notifier = make<QSocketNotifier>(notifier.fd(), type);
|
auto socket_notifier = make<QSocketNotifier>(notifier.fd(), type);
|
||||||
QObject::connect(socket_notifier, &QSocketNotifier::activated, [fd = notifier.fd(), ¬ifier] {
|
QObject::connect(socket_notifier, &QSocketNotifier::activated, [¬ifier] {
|
||||||
Core::ThreadEventQueue::current().post_event(notifier, make<Core::NotifierActivationEvent>(fd));
|
qt_notifier_activated(notifier);
|
||||||
});
|
});
|
||||||
|
|
||||||
ThreadData::the().notifiers.set(¬ifier, move(socket_notifier));
|
ThreadData::the().notifiers.set(¬ifier, move(socket_notifier));
|
||||||
|
@ -132,4 +155,9 @@ void EventLoopImplementationQt::unregister_notifier(Core::Notifier& notifier)
|
||||||
ThreadData::the().notifiers.remove(¬ifier);
|
ThreadData::the().notifiers.remove(¬ifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EventLoopImplementationQt::did_post_event()
|
||||||
|
{
|
||||||
|
m_process_core_events_timer.start();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,8 @@ public:
|
||||||
virtual void register_notifier(Core::Notifier&) override;
|
virtual void register_notifier(Core::Notifier&) override;
|
||||||
virtual void unregister_notifier(Core::Notifier&) override;
|
virtual void unregister_notifier(Core::Notifier&) override;
|
||||||
|
|
||||||
|
virtual void did_post_event() override;
|
||||||
|
|
||||||
// FIXME: These APIs only exist for obscure use-cases inside SerenityOS. Try to get rid of them.
|
// FIXME: These APIs only exist for obscure use-cases inside SerenityOS. Try to get rid of them.
|
||||||
virtual void unquit() override { }
|
virtual void unquit() override { }
|
||||||
virtual bool was_exit_requested() const override { return false; }
|
virtual bool was_exit_requested() const override { return false; }
|
||||||
|
@ -42,12 +44,15 @@ public:
|
||||||
virtual int register_signal(int, Function<void(int)>) override { return 0; }
|
virtual int register_signal(int, Function<void(int)>) override { return 0; }
|
||||||
virtual void unregister_signal(int) override { }
|
virtual void unregister_signal(int) override { }
|
||||||
|
|
||||||
protected:
|
void set_main_loop() { m_main_loop = true; }
|
||||||
EventLoopImplementationQt();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
EventLoopImplementationQt();
|
||||||
|
bool is_main_loop() const { return m_main_loop; }
|
||||||
|
|
||||||
QEventLoop m_event_loop;
|
QEventLoop m_event_loop;
|
||||||
Optional<int> m_exit_code;
|
QTimer m_process_core_events_timer;
|
||||||
|
bool m_main_loop { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
|
|
||||||
Core::EventLoop::make_implementation = Ladybird::EventLoopImplementationQt::create;
|
Core::EventLoop::make_implementation = Ladybird::EventLoopImplementationQt::create;
|
||||||
Core::EventLoop event_loop;
|
Core::EventLoop event_loop;
|
||||||
|
static_cast<Ladybird::EventLoopImplementationQt&>(event_loop.impl()).set_main_loop();
|
||||||
|
|
||||||
TRY(handle_attached_debugger());
|
TRY(handle_attached_debugger());
|
||||||
|
|
||||||
|
|
|
@ -170,4 +170,9 @@ bool EventLoop::was_exit_requested() const
|
||||||
return m_impl->was_exit_requested();
|
return m_impl->was_exit_requested();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EventLoop::did_post_event(Badge<Core::ThreadEventQueue>)
|
||||||
|
{
|
||||||
|
m_impl->did_post_event();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
class EventLoopImplementation;
|
class EventLoopImplementation;
|
||||||
|
class ThreadEventQueue;
|
||||||
|
|
||||||
// The event loop enables asynchronous (not parallel or multi-threaded) computing by efficiently handling events from various sources.
|
// The event loop enables asynchronous (not parallel or multi-threaded) computing by efficiently handling events from various sources.
|
||||||
// Event loops are most important for GUI programs, where the various GUI updates and action callbacks run on the EventLoop,
|
// Event loops are most important for GUI programs, where the various GUI updates and action callbacks run on the EventLoop,
|
||||||
|
@ -95,6 +96,9 @@ public:
|
||||||
|
|
||||||
static Function<NonnullOwnPtr<EventLoopImplementation>()> make_implementation;
|
static Function<NonnullOwnPtr<EventLoopImplementation>()> make_implementation;
|
||||||
|
|
||||||
|
void did_post_event(Badge<ThreadEventQueue>);
|
||||||
|
EventLoopImplementation& impl() { return *m_impl; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void wait_for_event(WaitMode);
|
void wait_for_event(WaitMode);
|
||||||
Optional<Time> get_next_timer_expiration();
|
Optional<Time> get_next_timer_expiration();
|
||||||
|
|
|
@ -37,6 +37,8 @@ public:
|
||||||
virtual void register_notifier(Notifier&) = 0;
|
virtual void register_notifier(Notifier&) = 0;
|
||||||
virtual void unregister_notifier(Notifier&) = 0;
|
virtual void unregister_notifier(Notifier&) = 0;
|
||||||
|
|
||||||
|
virtual void did_post_event() = 0;
|
||||||
|
|
||||||
// FIXME: These APIs only exist for obscure use-cases inside SerenityOS. Try to get rid of them.
|
// FIXME: These APIs only exist for obscure use-cases inside SerenityOS. Try to get rid of them.
|
||||||
virtual void unquit() = 0;
|
virtual void unquit() = 0;
|
||||||
virtual bool was_exit_requested() const = 0;
|
virtual bool was_exit_requested() const = 0;
|
||||||
|
|
|
@ -522,4 +522,8 @@ void EventLoopImplementationUnix::unregister_notifier(Notifier& notifier)
|
||||||
ThreadData::the().notifiers.remove(¬ifier);
|
ThreadData::the().notifiers.remove(¬ifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EventLoopImplementationUnix::did_post_event()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ public:
|
||||||
virtual void register_notifier(Notifier&) override;
|
virtual void register_notifier(Notifier&) override;
|
||||||
virtual void unregister_notifier(Notifier&) override;
|
virtual void unregister_notifier(Notifier&) override;
|
||||||
|
|
||||||
|
virtual void did_post_event() override;
|
||||||
|
|
||||||
virtual void unquit() override;
|
virtual void unquit() override;
|
||||||
virtual bool was_exit_requested() const override;
|
virtual bool was_exit_requested() const override;
|
||||||
virtual void notify_forked_and_in_child() override;
|
virtual void notify_forked_and_in_child() override;
|
||||||
|
|
|
@ -61,10 +61,13 @@ ThreadEventQueue::ThreadEventQueue()
|
||||||
ThreadEventQueue::~ThreadEventQueue() = default;
|
ThreadEventQueue::~ThreadEventQueue() = default;
|
||||||
|
|
||||||
void ThreadEventQueue::post_event(Core::Object& receiver, NonnullOwnPtr<Core::Event> event)
|
void ThreadEventQueue::post_event(Core::Object& receiver, NonnullOwnPtr<Core::Event> event)
|
||||||
|
{
|
||||||
{
|
{
|
||||||
Threading::MutexLocker lock(m_private->mutex);
|
Threading::MutexLocker lock(m_private->mutex);
|
||||||
m_private->queued_events.empend(receiver, move(event));
|
m_private->queued_events.empend(receiver, move(event));
|
||||||
}
|
}
|
||||||
|
Core::EventLoop::current().did_post_event({});
|
||||||
|
}
|
||||||
|
|
||||||
void ThreadEventQueue::add_job(NonnullRefPtr<Promise<NonnullRefPtr<Object>>> promise)
|
void ThreadEventQueue::add_job(NonnullRefPtr<Promise<NonnullRefPtr<Object>>> promise)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue