Ladybird: Use only the Qt event loop to speed everything up :^)

This patch removes the dual-event-loop setup, leaving only the Qt event
loop. We teach LibWeb how to drive Qt by installing an EventLoopPlugin.

This removes the 50ms latency on all UI interactions (and network
requests, etc.)
This commit is contained in:
Andreas Kling 2022-09-07 20:33:15 +02:00 committed by Andrew Kaster
parent dcab11f5e9
commit 37d844fd66
9 changed files with 209 additions and 28 deletions

View file

@ -10,7 +10,6 @@
#include "Settings.h" #include "Settings.h"
#include "SettingsDialog.h" #include "SettingsDialog.h"
#include "WebView.h" #include "WebView.h"
#include <LibCore/EventLoop.h>
#include <QAction> #include <QAction>
#include <QDialog> #include <QDialog>
#include <QPlainTextEdit> #include <QPlainTextEdit>
@ -18,8 +17,7 @@
extern String s_serenity_resource_root; extern String s_serenity_resource_root;
extern Browser::Settings* s_settings; extern Browser::Settings* s_settings;
BrowserWindow::BrowserWindow(Core::EventLoop& event_loop) BrowserWindow::BrowserWindow()
: m_event_loop(event_loop)
{ {
m_tabs_container = new QTabWidget; m_tabs_container = new QTabWidget;
m_tabs_container->setElideMode(Qt::TextElideMode::ElideRight); m_tabs_container->setElideMode(Qt::TextElideMode::ElideRight);
@ -257,13 +255,3 @@ void BrowserWindow::tab_favicon_changed(int index, QIcon icon)
m_tabs_container->setTabIcon(index, icon); m_tabs_container->setTabIcon(index, icon);
setWindowIcon(icon); setWindowIcon(icon);
} }
void BrowserWindow::closeEvent(QCloseEvent* event)
{
QWidget::closeEvent(event);
// FIXME: Ladybird only supports one window at the moment. When we support
// multiple windows, we'll only want to fire off the quit event when
// all of the browser windows have closed.
m_event_loop.quit(0);
}

View file

@ -22,14 +22,12 @@ class WebView;
class BrowserWindow : public QMainWindow { class BrowserWindow : public QMainWindow {
Q_OBJECT Q_OBJECT
public: public:
explicit BrowserWindow(Core::EventLoop&); explicit BrowserWindow();
WebView& view() const { return m_current_tab->view(); } WebView& view() const { return m_current_tab->view(); }
int tab_index(Tab*); int tab_index(Tab*);
virtual void closeEvent(QCloseEvent*) override;
public slots: public slots:
void tab_title_changed(int index, QString const&); void tab_title_changed(int index, QString const&);
void tab_favicon_changed(int index, QIcon icon); void tab_favicon_changed(int index, QIcon icon);
@ -44,6 +42,4 @@ private:
QTabBar* m_tabs_bar { nullptr }; QTabBar* m_tabs_bar { nullptr };
NonnullOwnPtrVector<Tab> m_tabs; NonnullOwnPtrVector<Tab> m_tabs;
Tab* m_current_tab { nullptr }; Tab* m_current_tab { nullptr };
Core::EventLoop& m_event_loop;
}; };

View file

@ -49,6 +49,7 @@ set(SOURCES
ConsoleClient.cpp ConsoleClient.cpp
ConsoleGlobalObject.cpp ConsoleGlobalObject.cpp
CookieJar.cpp CookieJar.cpp
EventLoopPluginQt.cpp
RequestManagerQt.cpp RequestManagerQt.cpp
main.cpp main.cpp
WebView.cpp WebView.cpp
@ -56,6 +57,7 @@ set(SOURCES
Settings.cpp Settings.cpp
SettingsDialog.cpp SettingsDialog.cpp
Tab.cpp Tab.cpp
TimerQt.cpp
) )
qt_add_executable(ladybird ${SOURCES} qt_add_executable(ladybird ${SOURCES}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#define AK_DONT_REPLACE_STD
#include "EventLoopPluginQt.h"
#include "TimerQt.h"
#include <AK/Function.h>
#include <AK/NonnullRefPtr.h>
#include <QCoreApplication>
#include <QTimer>
namespace Ladybird {
EventLoopPluginQt::EventLoopPluginQt() = default;
EventLoopPluginQt::~EventLoopPluginQt() = default;
void EventLoopPluginQt::spin_until(Function<bool()> goal_condition)
{
while (!goal_condition())
QCoreApplication::processEvents();
}
void EventLoopPluginQt::deferred_invoke(Function<void()> function)
{
VERIFY(function);
QTimer::singleShot(0, [function = move(function)] {
function();
});
}
NonnullRefPtr<Web::Platform::Timer> EventLoopPluginQt::create_timer()
{
return TimerQt::create();
}
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/Platform/EventLoopPlugin.h>
namespace Ladybird {
class EventLoopPluginQt final : public Web::Platform::EventLoopPlugin {
public:
EventLoopPluginQt();
virtual ~EventLoopPluginQt() override;
virtual void spin_until(Function<bool()> goal_condition) override;
virtual void deferred_invoke(Function<void()>) override;
virtual NonnullRefPtr<Web::Platform::Timer> create_timer() override;
};
}

94
Ladybird/TimerQt.cpp Normal file
View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#define AK_DONT_REPLACE_STD
#include "TimerQt.h"
#include <AK/NonnullRefPtr.h>
#include <QTimer>
namespace Ladybird {
NonnullRefPtr<TimerQt> TimerQt::create()
{
return adopt_ref(*new TimerQt);
}
TimerQt::TimerQt()
{
m_timer = new QTimer;
QObject::connect(m_timer, &QTimer::timeout, [this] {
if (on_timeout)
on_timeout();
});
}
TimerQt::~TimerQt()
{
delete m_timer;
}
void TimerQt::start()
{
m_timer->start();
}
void TimerQt::start(int interval_ms)
{
m_timer->start(interval_ms);
}
void TimerQt::restart()
{
restart(interval());
}
void TimerQt::restart(int interval_ms)
{
if (is_active())
stop();
start(interval_ms);
}
void TimerQt::stop()
{
m_timer->stop();
}
void TimerQt::set_active(bool active)
{
if (active)
m_timer->start();
else
m_timer->stop();
}
bool TimerQt::is_active() const
{
return m_timer->isActive();
}
int TimerQt::interval() const
{
return m_timer->interval();
}
void TimerQt::set_interval(int interval_ms)
{
m_timer->setInterval(interval_ms);
}
bool TimerQt::is_single_shot() const
{
return m_timer->isSingleShot();
}
void TimerQt::set_single_shot(bool single_shot)
{
m_timer->setSingleShot(single_shot);
}
}

42
Ladybird/TimerQt.h Normal file
View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/Platform/Timer.h>
class QTimer;
namespace Ladybird {
class TimerQt final : public Web::Platform::Timer {
public:
static NonnullRefPtr<TimerQt> create();
virtual ~TimerQt();
virtual void start() override;
virtual void start(int interval_ms) override;
virtual void restart() override;
virtual void restart(int interval_ms) override;
virtual void stop() override;
virtual void set_active(bool) override;
virtual bool is_active() const override;
virtual int interval() const override;
virtual void set_interval(int interval_ms) override;
virtual bool is_single_shot() const override;
virtual void set_single_shot(bool) override;
private:
TimerQt();
QTimer* m_timer { nullptr };
};
}

View file

@ -10,6 +10,7 @@
#include "WebView.h" #include "WebView.h"
#include "ConsoleClient.h" #include "ConsoleClient.h"
#include "CookieJar.h" #include "CookieJar.h"
#include "EventLoopPluginQt.h"
#include "RequestManagerQt.h" #include "RequestManagerQt.h"
#include <AK/Assertions.h> #include <AK/Assertions.h>
#include <AK/ByteBuffer.h> #include <AK/ByteBuffer.h>
@ -47,6 +48,7 @@
#include <LibWeb/Page/Page.h> #include <LibWeb/Page/Page.h>
#include <LibWeb/Painting/PaintableBox.h> #include <LibWeb/Painting/PaintableBox.h>
#include <LibWeb/Painting/StackingContext.h> #include <LibWeb/Painting/StackingContext.h>
#include <LibWeb/Platform/EventLoopPlugin.h>
#include <LibWeb/WebSockets/WebSocket.h> #include <LibWeb/WebSockets/WebSocket.h>
#include <LibWebSocket/ConnectionInfo.h> #include <LibWebSocket/ConnectionInfo.h>
#include <LibWebSocket/Message.h> #include <LibWebSocket/Message.h>
@ -819,6 +821,8 @@ static void platform_init()
void initialize_web_engine() void initialize_web_engine()
{ {
Web::Platform::EventLoopPlugin::install(*new Ladybird::EventLoopPluginQt);
Web::ImageDecoding::Decoder::initialize(HeadlessImageDecoderClient::create()); Web::ImageDecoding::Decoder::initialize(HeadlessImageDecoderClient::create());
Web::ResourceLoader::initialize(RequestManagerQt::create()); Web::ResourceLoader::initialize(RequestManagerQt::create());
Web::WebSockets::WebSocketClientManager::initialize(HeadlessWebSocketClientManager::create()); Web::WebSockets::WebSocketClientManager::initialize(HeadlessWebSocketClientManager::create());

View file

@ -8,7 +8,6 @@
#include "Settings.h" #include "Settings.h"
#include "WebView.h" #include "WebView.h"
#include <LibCore/ArgsParser.h> #include <LibCore/ArgsParser.h>
#include <LibCore/EventLoop.h>
#include <LibCore/Timer.h> #include <LibCore/Timer.h>
#include <LibMain/Main.h> #include <LibMain/Main.h>
#include <QApplication> #include <QApplication>
@ -29,22 +28,15 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
s_settings = new Browser::Settings(); s_settings = new Browser::Settings();
Core::EventLoop event_loop;
QApplication app(arguments.argc, arguments.argv); QApplication app(arguments.argc, arguments.argv);
BrowserWindow window(event_loop); BrowserWindow window;
window.setWindowTitle("Ladybird"); window.setWindowTitle("Ladybird");
window.resize(800, 600); window.resize(800, 600);
window.show(); window.show();
auto qt_event_loop_driver = Core::Timer::create_repeating(50, [&] {
app.processEvents();
});
qt_event_loop_driver->start();
if (!url.is_empty()) { if (!url.is_empty()) {
window.view().load(url); window.view().load(url);
} }
return event_loop.exec(); return app.exec();
} }