mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 11:14:58 +00:00

The logic for `EventLoopImplementationQt::pump()` caused calls to `EventLoop::pump(PumpMode::DontWaitForEvents)` to not consume events posted to the Qt event queue. This caused the window to be unresponsive even when polling the event loop, if waiting was not desired.
171 lines
4.8 KiB
C++
171 lines
4.8 KiB
C++
/*
|
|
* 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 <QCoreApplication>
|
|
#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()
|
|
{
|
|
if (is_main_loop())
|
|
return QCoreApplication::exec();
|
|
return m_event_loop.exec();
|
|
}
|
|
|
|
size_t EventLoopImplementationQt::pump(PumpMode mode)
|
|
{
|
|
auto result = Core::ThreadEventQueue::current().process();
|
|
auto qt_mode = mode == PumpMode::WaitForEvents ? QEventLoop::WaitForMoreEvents : QEventLoop::AllEvents;
|
|
if (is_main_loop())
|
|
QCoreApplication::processEvents(qt_mode);
|
|
else
|
|
m_event_loop.processEvents(qt_mode);
|
|
result += Core::ThreadEventQueue::current().process();
|
|
return result;
|
|
}
|
|
|
|
void EventLoopImplementationQt::quit(int code)
|
|
{
|
|
if (is_main_loop())
|
|
QCoreApplication::exit(code);
|
|
else
|
|
m_event_loop.exit(code);
|
|
}
|
|
|
|
void EventLoopImplementationQt::wake()
|
|
{
|
|
if (!is_main_loop())
|
|
m_event_loop.wakeUp();
|
|
}
|
|
|
|
void EventLoopImplementationQt::post_event(Core::Object& receiver, NonnullOwnPtr<Core::Event>&& event)
|
|
{
|
|
m_thread_event_queue.post_event(receiver, move(event));
|
|
if (&m_thread_event_queue != &Core::ThreadEventQueue::current())
|
|
wake();
|
|
}
|
|
|
|
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 EventLoopManagerQt::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;
|
|
qt_timer_fired(timer_id, should_fire_when_not_visible, *object);
|
|
});
|
|
timer->start();
|
|
thread_data.timers.set(timer_id, move(timer));
|
|
return timer_id;
|
|
}
|
|
|
|
bool EventLoopManagerQt::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);
|
|
}
|
|
|
|
static void qt_notifier_activated(Core::Notifier& notifier)
|
|
{
|
|
Core::NotifierActivationEvent event(notifier.fd());
|
|
notifier.dispatch_event(event);
|
|
}
|
|
|
|
void EventLoopManagerQt::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, [¬ifier] {
|
|
qt_notifier_activated(notifier);
|
|
});
|
|
|
|
ThreadData::the().notifiers.set(¬ifier, move(socket_notifier));
|
|
}
|
|
|
|
void EventLoopManagerQt::unregister_notifier(Core::Notifier& notifier)
|
|
{
|
|
ThreadData::the().notifiers.remove(¬ifier);
|
|
}
|
|
|
|
void EventLoopManagerQt::did_post_event()
|
|
{
|
|
m_process_core_events_timer.start();
|
|
}
|
|
|
|
EventLoopManagerQt::EventLoopManagerQt()
|
|
{
|
|
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();
|
|
});
|
|
}
|
|
|
|
EventLoopManagerQt::~EventLoopManagerQt() = default;
|
|
|
|
NonnullOwnPtr<Core::EventLoopImplementation> EventLoopManagerQt::make_implementation()
|
|
{
|
|
return adopt_own(*new EventLoopImplementationQt);
|
|
}
|
|
|
|
}
|