mirror of
https://github.com/RGBCube/serenity
synced 2025-05-25 20:55:07 +00:00
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.
This commit is contained in:
parent
31289a8d57
commit
3494c2382d
6 changed files with 201 additions and 10 deletions
|
@ -83,6 +83,7 @@ set(SOURCES
|
||||||
${BROWSER_SOURCE_DIR}/History.cpp
|
${BROWSER_SOURCE_DIR}/History.cpp
|
||||||
BrowserWindow.cpp
|
BrowserWindow.cpp
|
||||||
ConsoleWidget.cpp
|
ConsoleWidget.cpp
|
||||||
|
EventLoopImplementationQt.cpp
|
||||||
HelperProcess.cpp
|
HelperProcess.cpp
|
||||||
InspectorWidget.cpp
|
InspectorWidget.cpp
|
||||||
LocationEdit.cpp
|
LocationEdit.cpp
|
||||||
|
|
135
Ladybird/EventLoopImplementationQt.cpp
Normal file
135
Ladybird/EventLoopImplementationQt.cpp
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
53
Ladybird/EventLoopImplementationQt.h
Normal file
53
Ladybird/EventLoopImplementationQt.h
Normal file
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ set(WEBCONTENT_SOURCES
|
||||||
${WEBCONTENT_SOURCE_DIR}/PageHost.cpp
|
${WEBCONTENT_SOURCE_DIR}/PageHost.cpp
|
||||||
${WEBCONTENT_SOURCE_DIR}/WebContentConsoleClient.cpp
|
${WEBCONTENT_SOURCE_DIR}/WebContentConsoleClient.cpp
|
||||||
${WEBCONTENT_SOURCE_DIR}/WebDriverConnection.cpp
|
${WEBCONTENT_SOURCE_DIR}/WebDriverConnection.cpp
|
||||||
|
../EventLoopImplementationQt.cpp
|
||||||
../EventLoopPluginQt.cpp
|
../EventLoopPluginQt.cpp
|
||||||
../FontPluginQt.cpp
|
../FontPluginQt.cpp
|
||||||
../ImageCodecPluginLadybird.cpp
|
../ImageCodecPluginLadybird.cpp
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "../EventLoopImplementationQt.h"
|
||||||
#include "../EventLoopPluginQt.h"
|
#include "../EventLoopPluginQt.h"
|
||||||
#include "../FontPluginQt.h"
|
#include "../FontPluginQt.h"
|
||||||
#include "../ImageCodecPluginLadybird.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)
|
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);
|
QGuiApplication app(arguments.argc, arguments.argv);
|
||||||
|
|
||||||
|
Core::EventLoop::make_implementation = Ladybird::EventLoopImplementationQt::create;
|
||||||
|
Core::EventLoop event_loop;
|
||||||
|
|
||||||
platform_init();
|
platform_init();
|
||||||
|
|
||||||
Web::Platform::EventLoopPlugin::install(*new Ladybird::EventLoopPluginQt);
|
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);
|
proxy_socket_through_notifier(webdriver, webdriver_notifier);
|
||||||
};
|
};
|
||||||
|
|
||||||
return app.exec();
|
return event_loop.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
static ErrorOr<void> load_content_filters()
|
static ErrorOr<void> load_content_filters()
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "BrowserWindow.h"
|
#include "BrowserWindow.h"
|
||||||
|
#include "EventLoopImplementationQt.h"
|
||||||
#include "HelperProcess.h"
|
#include "HelperProcess.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "Utilities.h"
|
#include "Utilities.h"
|
||||||
|
@ -51,14 +52,13 @@ static ErrorOr<void> handle_attached_debugger()
|
||||||
|
|
||||||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
// NOTE: This is only used for the Core::Socket inside the IPC connections.
|
QApplication app(arguments.argc, arguments.argv);
|
||||||
// FIXME: Refactor things so we can get rid of this somehow.
|
|
||||||
|
Core::EventLoop::make_implementation = Ladybird::EventLoopImplementationQt::create;
|
||||||
Core::EventLoop event_loop;
|
Core::EventLoop event_loop;
|
||||||
|
|
||||||
TRY(handle_attached_debugger());
|
TRY(handle_attached_debugger());
|
||||||
|
|
||||||
QApplication app(arguments.argc, arguments.argv);
|
|
||||||
|
|
||||||
platform_init();
|
platform_init();
|
||||||
|
|
||||||
// NOTE: We only instantiate this to ensure that Gfx::FontDatabase has its default queries initialized.
|
// 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())));
|
window.view().load(TRY(get_formatted_url(home_url.bytes_as_string_view())));
|
||||||
}
|
}
|
||||||
|
|
||||||
return app.exec();
|
return event_loop.exec();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue