diff options
author | Andreas Kling <kling@serenityos.org> | 2023-04-24 14:51:19 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2023-04-25 14:48:40 +0200 |
commit | 3494c2382d96e23c2791bf7e29bd90ef06de6fac (patch) | |
tree | 5c8d0d18d2c2dc8fc979d261de614ea7700086c0 | |
parent | 31289a8d57bca2ed86d750bad6a4ab36f26ac6f1 (diff) | |
download | serenity-3494c2382d96e23c2791bf7e29bd90ef06de6fac.zip |
Ladybird: Run the Core::EventLoop with a Qt backend
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.
-rw-r--r-- | Ladybird/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Ladybird/EventLoopImplementationQt.cpp | 135 | ||||
-rw-r--r-- | Ladybird/EventLoopImplementationQt.h | 53 | ||||
-rw-r--r-- | Ladybird/WebContent/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Ladybird/WebContent/main.cpp | 11 | ||||
-rw-r--r-- | Ladybird/main.cpp | 10 |
6 files changed, 201 insertions, 10 deletions
diff --git a/Ladybird/CMakeLists.txt b/Ladybird/CMakeLists.txt index 30f4ec6eeb..3ba21e5361 100644 --- a/Ladybird/CMakeLists.txt +++ b/Ladybird/CMakeLists.txt @@ -83,6 +83,7 @@ set(SOURCES ${BROWSER_SOURCE_DIR}/History.cpp BrowserWindow.cpp ConsoleWidget.cpp + EventLoopImplementationQt.cpp HelperProcess.cpp InspectorWidget.cpp LocationEdit.cpp diff --git a/Ladybird/EventLoopImplementationQt.cpp b/Ladybird/EventLoopImplementationQt.cpp new file mode 100644 index 0000000000..26ae64a98b --- /dev/null +++ b/Ladybird/EventLoopImplementationQt.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2022-2023, Andreas Kling <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "EventLoopImplementationQt.h" +#include <AK/IDAllocator.h> +#include <LibCore/Event.h> +#include <LibCore/Notifier.h> +#include <LibCore/Object.h> +#include <LibCore/ThreadEventQueue.h> +#include <QTimer> + +namespace Ladybird { + +struct ThreadData; +static thread_local ThreadData* s_thread_data; + +struct ThreadData { + static ThreadData& the() + { + if (!s_thread_data) { + // FIXME: Don't leak this. + s_thread_data = new ThreadData; + } + return *s_thread_data; + } + + IDAllocator timer_id_allocator; + HashMap<int, NonnullOwnPtr<QTimer>> timers; + HashMap<Core::Notifier*, NonnullOwnPtr<QSocketNotifier>> notifiers; +}; + +EventLoopImplementationQt::EventLoopImplementationQt() +{ +} + +EventLoopImplementationQt::~EventLoopImplementationQt() = default; + +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(); +} + +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(); + return result; +} + +void EventLoopImplementationQt::quit(int code) +{ + m_exit_code = code; +} + +void EventLoopImplementationQt::wake() +{ + m_event_loop.wakeUp(); +} + +void EventLoopImplementationQt::deferred_invoke(Function<void()> function) +{ + VERIFY(function); + QTimer::singleShot(0, [function = move(function)] { + function(); + }); +} + +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 timer = make<QTimer>(); + timer->setInterval(milliseconds); + timer->setSingleShot(!should_reload); + auto timer_id = thread_data.timer_id_allocator.allocate(); + auto weak_object = object.make_weak_ptr(); + QObject::connect(timer, &QTimer::timeout, [timer_id, should_fire_when_not_visible, weak_object = move(weak_object)] { + auto object = weak_object.strong_ref(); + if (!object) + 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)); + }); + timer->start(); + thread_data.timers.set(timer_id, move(timer)); + return timer_id; +} + +bool EventLoopImplementationQt::unregister_timer(int timer_id) +{ + auto& thread_data = ThreadData::the(); + thread_data.timer_id_allocator.deallocate(timer_id); + return thread_data.timers.remove(timer_id); +} + +void EventLoopImplementationQt::register_notifier(Core::Notifier& notifier) +{ + QSocketNotifier::Type type; + switch (notifier.type()) { + case Core::Notifier::Type::Read: + type = QSocketNotifier::Read; + break; + case Core::Notifier::Type::Write: + type = QSocketNotifier::Write; + break; + default: + TODO(); + } + auto socket_notifier = make<QSocketNotifier>(notifier.fd(), type); + QObject::connect(socket_notifier, &QSocketNotifier::activated, [fd = notifier.fd(), ¬ifier] { + Core::ThreadEventQueue::current().post_event(notifier, make<Core::NotifierActivationEvent>(fd)); + }); + + ThreadData::the().notifiers.set(¬ifier, move(socket_notifier)); +} + +void EventLoopImplementationQt::unregister_notifier(Core::Notifier& notifier) +{ + ThreadData::the().notifiers.remove(¬ifier); +} + +} diff --git a/Ladybird/EventLoopImplementationQt.h b/Ladybird/EventLoopImplementationQt.h new file mode 100644 index 0000000000..313abaf02f --- /dev/null +++ b/Ladybird/EventLoopImplementationQt.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022-2023, Andreas Kling <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/HashMap.h> +#include <AK/NonnullOwnPtr.h> +#include <AK/OwnPtr.h> +#include <LibCore/EventLoopImplementation.h> +#include <QEventLoop> +#include <QSocketNotifier> +#include <QTimer> + +namespace Ladybird { + +class EventLoopImplementationQt final : public Core::EventLoopImplementation { +public: + static NonnullOwnPtr<EventLoopImplementationQt> create() { return adopt_own(*new EventLoopImplementationQt); } + + virtual ~EventLoopImplementationQt() override; + + virtual int exec() override; + virtual size_t pump(PumpMode) override; + virtual void quit(int) override; + virtual void wake() override; + + virtual void deferred_invoke(Function<void()>) override; + + virtual int register_timer(Core::Object&, int milliseconds, bool should_reload, Core::TimerShouldFireWhenNotVisible) override; + virtual bool unregister_timer(int timer_id) override; + + virtual void register_notifier(Core::Notifier&) override; + virtual void unregister_notifier(Core::Notifier&) override; + + // FIXME: These APIs only exist for obscure use-cases inside SerenityOS. Try to get rid of them. + virtual void unquit() override { } + virtual bool was_exit_requested() const override { return false; } + virtual void notify_forked_and_in_child() override { } + virtual int register_signal(int, Function<void(int)>) override { return 0; } + virtual void unregister_signal(int) override { } + +protected: + EventLoopImplementationQt(); + +private: + QEventLoop m_event_loop; + Optional<int> m_exit_code; +}; + +} diff --git a/Ladybird/WebContent/CMakeLists.txt b/Ladybird/WebContent/CMakeLists.txt index b2b5864702..e553a105da 100644 --- a/Ladybird/WebContent/CMakeLists.txt +++ b/Ladybird/WebContent/CMakeLists.txt @@ -6,6 +6,7 @@ set(WEBCONTENT_SOURCES ${WEBCONTENT_SOURCE_DIR}/PageHost.cpp ${WEBCONTENT_SOURCE_DIR}/WebContentConsoleClient.cpp ${WEBCONTENT_SOURCE_DIR}/WebDriverConnection.cpp + ../EventLoopImplementationQt.cpp ../EventLoopPluginQt.cpp ../FontPluginQt.cpp ../ImageCodecPluginLadybird.cpp diff --git a/Ladybird/WebContent/main.cpp b/Ladybird/WebContent/main.cpp index e7351f65a5..1b1f2fe2b5 100644 --- a/Ladybird/WebContent/main.cpp +++ b/Ladybird/WebContent/main.cpp @@ -4,6 +4,8 @@ * SPDX-License-Identifier: BSD-2-Clause */ + +#include "../EventLoopImplementationQt.h" #include "../EventLoopPluginQt.h" #include "../FontPluginQt.h" #include "../ImageCodecPluginLadybird.h" @@ -59,12 +61,11 @@ static void proxy_socket_through_notifier(ClientType& client, QSocketNotifier& n ErrorOr<int> serenity_main(Main::Arguments arguments) { - // NOTE: This is only used for the Core::Socket inside the IPC connection. - // FIXME: Refactor things so we can get rid of this somehow. - Core::EventLoop event_loop; - QGuiApplication app(arguments.argc, arguments.argv); + Core::EventLoop::make_implementation = Ladybird::EventLoopImplementationQt::create; + Core::EventLoop event_loop; + platform_init(); Web::Platform::EventLoopPlugin::install(*new Ladybird::EventLoopPluginQt); @@ -109,7 +110,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) proxy_socket_through_notifier(webdriver, webdriver_notifier); }; - return app.exec(); + return event_loop.exec(); } static ErrorOr<void> load_content_filters() diff --git a/Ladybird/main.cpp b/Ladybird/main.cpp index 968a4aa768..ad3b538c91 100644 --- a/Ladybird/main.cpp +++ b/Ladybird/main.cpp @@ -5,6 +5,7 @@ */ #include "BrowserWindow.h" +#include "EventLoopImplementationQt.h" #include "HelperProcess.h" #include "Settings.h" #include "Utilities.h" @@ -51,14 +52,13 @@ static ErrorOr<void> handle_attached_debugger() ErrorOr<int> serenity_main(Main::Arguments arguments) { - // NOTE: This is only used for the Core::Socket inside the IPC connections. - // FIXME: Refactor things so we can get rid of this somehow. + QApplication app(arguments.argc, arguments.argv); + + Core::EventLoop::make_implementation = Ladybird::EventLoopImplementationQt::create; Core::EventLoop event_loop; TRY(handle_attached_debugger()); - QApplication app(arguments.argc, arguments.argv); - platform_init(); // NOTE: We only instantiate this to ensure that Gfx::FontDatabase has its default queries initialized. @@ -110,5 +110,5 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) window.view().load(TRY(get_formatted_url(home_url.bytes_as_string_view()))); } - return app.exec(); + return event_loop.exec(); } |