Sfoglia il codice sorgente

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.
Andreas Kling 2 anni fa
parent
commit
c21eb30a2b

+ 48 - 20
Ladybird/EventLoopImplementationQt.cpp

@@ -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,38 +35,48 @@ 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.
-    while (!m_exit_code.has_value()) {
-        pump(PumpMode::WaitForEvents);
-    }
-    return m_exit_code.value();
+    if (is_main_loop())
+        return QCoreApplication::exec();
+    return m_event_loop.exec();
 }
 }
 
 
 size_t EventLoopImplementationQt::pump(PumpMode mode)
 size_t EventLoopImplementationQt::pump(PumpMode mode)
 {
 {
-    bool result = Core::ThreadEventQueue::current().process() != 0;
-    if (mode == PumpMode::WaitForEvents)
-        result |= m_event_loop.processEvents(QEventLoop::WaitForMoreEvents);
-    else
-        result |= m_event_loop.processEvents();
-    Core::ThreadEventQueue::current().process();
+    auto result = Core::ThreadEventQueue::current().process();
+    if (mode == PumpMode::WaitForEvents) {
+        if (is_main_loop())
+            QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
+        else
+            m_event_loop.processEvents(QEventLoop::WaitForMoreEvents);
+    } 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()
 {
 {
-    m_event_loop.wakeUp();
+    if (!is_main_loop())
+        m_event_loop.wakeUp();
 }
 }
 
 
 void EventLoopImplementationQt::deferred_invoke(Function<void()> function)
 void EventLoopImplementationQt::deferred_invoke(Function<void()> function)
@@ -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) {
-            if (!object->is_visible_for_timer_purposes())
-                return;
-        }
-        Core::ThreadEventQueue::current().post_event(*object, make<Core::TimerEvent>(timer_id));
+        qt_timer_fired(timer_id, should_fire_when_not_visible, *object);
     });
     });
     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(), &notifier] {
-        Core::ThreadEventQueue::current().post_event(notifier, make<Core::NotifierActivationEvent>(fd));
+    QObject::connect(socket_notifier, &QSocketNotifier::activated, [&notifier] {
+        qt_notifier_activated(notifier);
     });
     });
 
 
     ThreadData::the().notifiers.set(&notifier, move(socket_notifier));
     ThreadData::the().notifiers.set(&notifier, move(socket_notifier));
@@ -132,4 +155,9 @@ void EventLoopImplementationQt::unregister_notifier(Core::Notifier& notifier)
     ThreadData::the().notifiers.remove(&notifier);
     ThreadData::the().notifiers.remove(&notifier);
 }
 }
 
 
+void EventLoopImplementationQt::did_post_event()
+{
+    m_process_core_events_timer.start();
+}
+
 }
 }

+ 8 - 3
Ladybird/EventLoopImplementationQt.h

@@ -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:
-    EventLoopImplementationQt();
+    void set_main_loop() { m_main_loop = true; }
 
 
 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 };
 };
 };
 
 
 }
 }

+ 1 - 0
Ladybird/main.cpp

@@ -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());
 
 

+ 5 - 0
Userland/Libraries/LibCore/EventLoop.cpp

@@ -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();
+}
+
 }
 }

+ 4 - 0
Userland/Libraries/LibCore/EventLoop.h

@@ -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();

+ 2 - 0
Userland/Libraries/LibCore/EventLoopImplementation.h

@@ -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;

+ 4 - 0
Userland/Libraries/LibCore/EventLoopImplementationUnix.cpp

@@ -522,4 +522,8 @@ void EventLoopImplementationUnix::unregister_notifier(Notifier& notifier)
     ThreadData::the().notifiers.remove(&notifier);
     ThreadData::the().notifiers.remove(&notifier);
 }
 }
 
 
+void EventLoopImplementationUnix::did_post_event()
+{
+}
+
 }
 }

+ 2 - 0
Userland/Libraries/LibCore/EventLoopImplementationUnix.h

@@ -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;

+ 5 - 2
Userland/Libraries/LibCore/ThreadEventQueue.cpp

@@ -62,8 +62,11 @@ 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);
-    m_private->queued_events.empend(receiver, move(event));
+    {
+        Threading::MutexLocker lock(m_private->mutex);
+        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)