From f66b131b05ad5d0a15f50892960bcb16c44fb38b Mon Sep 17 00:00:00 2001 From: Vaxry Date: Sat, 6 Apr 2024 23:39:48 +0100 Subject: [PATCH] Event loop rework --- src/Compositor.cpp | 6 +- src/managers/AnimationManager.cpp | 13 +- src/managers/AnimationManager.hpp | 3 +- src/managers/eventLoop/EventLoopManager.cpp | 140 ++++++++++++++++++++ src/managers/eventLoop/EventLoopManager.hpp | 46 +++++++ src/managers/eventLoop/EventLoopTimer.cpp | 53 ++++++++ src/managers/eventLoop/EventLoopTimer.hpp | 29 ++++ 7 files changed, 282 insertions(+), 8 deletions(-) create mode 100644 src/managers/eventLoop/EventLoopManager.cpp create mode 100644 src/managers/eventLoop/EventLoopManager.hpp create mode 100644 src/managers/eventLoop/EventLoopTimer.cpp create mode 100644 src/managers/eventLoop/EventLoopTimer.hpp diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 6c0025ea..746011ef 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -2,6 +2,7 @@ #include "helpers/Splashes.hpp" #include "config/ConfigValue.hpp" #include "managers/CursorManager.hpp" +#include "managers/eventLoop/EventLoopManager.hpp" #include #include #include "debug/HyprCtl.hpp" @@ -453,6 +454,9 @@ void CCompositor::cleanup() { void CCompositor::initManagers(eManagersInitStage stage) { switch (stage) { case STAGE_PRIORITY: { + Debug::log(LOG, "Creating the EventLoopManager!"); + g_pEventLoopManager = std::make_unique(); + Debug::log(LOG, "Creating the HookSystem!"); g_pHookSystem = std::make_unique(); @@ -628,7 +632,7 @@ void CCompositor::startCompositor() { // This blocks until we are done. Debug::log(LOG, "Hyprland is ready, running the event loop!"); - wl_display_run(m_sWLDisplay); + g_pEventLoopManager->enterLoop(m_sWLDisplay, m_sWLEventLoop); } CMonitor* CCompositor::getMonitorFromID(const int& id) { diff --git a/src/managers/AnimationManager.cpp b/src/managers/AnimationManager.cpp index ac849b58..7b7d0c28 100644 --- a/src/managers/AnimationManager.cpp +++ b/src/managers/AnimationManager.cpp @@ -4,8 +4,9 @@ #include "macros.hpp" #include "../config/ConfigValue.hpp" #include "../desktop/Window.hpp" +#include "eventLoop/EventLoopManager.hpp" -int wlTick(void* data) { +int wlTick(std::shared_ptr self, void* data) { if (g_pAnimationManager) g_pAnimationManager->onTicked(); @@ -15,7 +16,7 @@ int wlTick(void* data) { EMIT_HOOK_EVENT("tick", nullptr); } - if (g_pAnimationManager && g_pAnimationManager->shouldTickForNext()) + if (g_pAnimationManager) // && g_pAnimationManager->shouldTickForNext() g_pAnimationManager->scheduleTick(); return 0; @@ -25,8 +26,8 @@ CAnimationManager::CAnimationManager() { std::vector points = {Vector2D(0, 0.75f), Vector2D(0.15f, 1.f)}; m_mBezierCurves["default"].setup(&points); - m_pAnimationTick = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, &wlTick, nullptr); - wl_event_source_timer_update(m_pAnimationTick, 1); + m_pAnimationTimer = std::make_unique(std::chrono::microseconds(500), wlTick, nullptr); + g_pEventLoopManager->addTimer(m_pAnimationTimer); } void CAnimationManager::removeAllBeziers() { @@ -545,7 +546,7 @@ void CAnimationManager::scheduleTick() { const auto PMOSTHZ = g_pHyprRenderer->m_pMostHzMonitor; if (!PMOSTHZ) { - wl_event_source_timer_update(m_pAnimationTick, 16); + m_pAnimationTimer->updateTimeout(std::chrono::milliseconds(16)); return; } @@ -555,5 +556,5 @@ void CAnimationManager::scheduleTick() { const auto TOPRES = std::clamp(refreshDelayMs - SINCEPRES, 1.1f, 1000.f); // we can't send 0, that will disarm it - wl_event_source_timer_update(m_pAnimationTick, std::floor(TOPRES)); + m_pAnimationTimer->updateTimeout(std::chrono::milliseconds((int)std::floor(TOPRES))); } diff --git a/src/managers/AnimationManager.hpp b/src/managers/AnimationManager.hpp index 483c9813..23729502 100644 --- a/src/managers/AnimationManager.hpp +++ b/src/managers/AnimationManager.hpp @@ -6,6 +6,7 @@ #include "../helpers/AnimatedVariable.hpp" #include "../helpers/BezierCurve.hpp" #include "../helpers/Timer.hpp" +#include "eventLoop/EventLoopTimer.hpp" class CWindow; @@ -32,7 +33,7 @@ class CAnimationManager { std::vector m_vAnimatedVariables; std::vector m_vActiveAnimatedVariables; - wl_event_source* m_pAnimationTick; + std::shared_ptr m_pAnimationTimer; float m_fLastTickTime; // in ms diff --git a/src/managers/eventLoop/EventLoopManager.cpp b/src/managers/eventLoop/EventLoopManager.cpp new file mode 100644 index 00000000..0dabdcfb --- /dev/null +++ b/src/managers/eventLoop/EventLoopManager.cpp @@ -0,0 +1,140 @@ +#include "EventLoopManager.hpp" +#include "../../debug/Log.hpp" + +#include + +#include + +void CEventLoopManager::enterLoop(wl_display* display, wl_event_loop* wlEventLoop) { + m_sWayland.loop = wlEventLoop; + m_sWayland.display = display; + + pollfd pollfds[] = { + { + .fd = wl_event_loop_get_fd(wlEventLoop), + .events = POLLIN, + }, + }; + + std::thread pollThr([this, &pollfds]() { + while (!m_bTerminate) { + int ret = poll(pollfds, 1, 5000 /* 5 seconds, reasonable. Just in case we need to terminate and the signal fails */); + + if (ret < 0) { + if (errno == EINTR) + continue; + + Debug::log(CRIT, "Polling fds failed with {}", errno); + m_bTerminate = true; + return; + } + + for (size_t i = 0; i < 1; ++i) { + if (pollfds[i].revents & POLLHUP) { + Debug::log(CRIT, "Disconnected from pollfd id {}", i); + m_bTerminate = true; + return; + } + } + + if (ret != 0) { + { + std::lock_guard lg2(m_sLoopState.eventRequestMutex); + std::lock_guard lg(m_sLoopState.loopMutex); + m_sLoopState.event = true; + } + m_sLoopState.cv.notify_all(); + } + } + }); + + std::thread timersThr([this]() { + while (!m_bTerminate) { + // calc nearest thing + + m_sTimers.timersMutex.lock(); + float least = 1000 * 1000 * 10; // 10s in µs + for (auto& t : m_sTimers.timers) { + const auto TIME = std::clamp(t->leftUs(), 0.f, INFINITY); + if (TIME < least) + least = TIME; + } + m_sTimers.timersMutex.unlock(); + + if (least > 0) { + std::unique_lock lk(m_sTimers.timersRqMutex); + m_sTimers.cv.wait_for(lk, std::chrono::microseconds((int)least + 1), [this] { return m_sTimers.event; }); + m_sTimers.event = false; + } + + // notify main + { + std::lock_guard lg2(m_sLoopState.eventRequestMutex); + std::lock_guard lg(m_sLoopState.loopMutex); + m_sLoopState.event = true; + } + + m_sLoopState.cv.notify_all(); + } + }); + + m_sLoopState.event = true; // let it process once + + while (1) { + std::unique_lock lk(m_sLoopState.eventRequestMutex); + m_sLoopState.cv.wait_for(lk, std::chrono::milliseconds(5000), [this] { return m_sLoopState.event; }); + + if (m_bTerminate) + break; + + std::lock_guard lg(m_sLoopState.loopMutex); + + m_sLoopState.event = false; + + if (pollfds[0].revents & POLLIN /* wl event loop */) { + wl_display_flush_clients(m_sWayland.display); + if (wl_event_loop_dispatch(m_sWayland.loop, -1) < 0) { + m_bTerminate = true; + break; + } + } + + // TODO: don't check timers without the timer thread requesting it + // I tried but it didnt work :/ + + m_sTimers.timersMutex.lock(); + auto timerscpy = m_sTimers.timers; + m_sTimers.timersMutex.unlock(); + + for (auto& t : timerscpy) { + if (t->passed() && !t->cancelled()) + t->call(t); + } + + if (m_bTerminate) + break; + } + + Debug::log(LOG, "Kicked off the event loop! :("); + + m_sTimers.event = true; + m_sTimers.cv.notify_all(); +} + +void CEventLoopManager::addTimer(std::shared_ptr timer) { + m_sTimers.timersMutex.lock(); + m_sTimers.timers.push_back(timer); + m_sTimers.timersMutex.unlock(); + nudgeTimers(); +} + +void CEventLoopManager::removeTimer(std::shared_ptr timer) { + m_sTimers.timersMutex.lock(); + std::erase_if(m_sTimers.timers, [timer](const auto& t) { return timer == t; }); + m_sTimers.timersMutex.unlock(); +} + +void CEventLoopManager::nudgeTimers() { + m_sTimers.event = true; + m_sTimers.cv.notify_all(); +} \ No newline at end of file diff --git a/src/managers/eventLoop/EventLoopManager.hpp b/src/managers/eventLoop/EventLoopManager.hpp new file mode 100644 index 00000000..d55b54a9 --- /dev/null +++ b/src/managers/eventLoop/EventLoopManager.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "EventLoopTimer.hpp" + +class CEventLoopManager { + public: + void enterLoop(wl_display* display, wl_event_loop* wlEventLoop); + void addTimer(std::shared_ptr timer); + void removeTimer(std::shared_ptr timer); + + // recalculates timers + void nudgeTimers(); + + private: + struct { + wl_event_loop* loop = nullptr; + wl_display* display = nullptr; + std::thread pollThread; + } m_sWayland; + + struct { + std::mutex timersMutex; + std::mutex timersRqMutex; + std::vector> timers; + std::thread timerThread; + bool event = false; + std::condition_variable cv; + } m_sTimers; + + struct { + std::mutex loopMutex; + std::mutex eventRequestMutex; + bool event = false; + std::condition_variable cv; + } m_sLoopState; + + bool m_bTerminate = false; +}; + +inline std::unique_ptr g_pEventLoopManager; \ No newline at end of file diff --git a/src/managers/eventLoop/EventLoopTimer.cpp b/src/managers/eventLoop/EventLoopTimer.cpp new file mode 100644 index 00000000..ebfb696c --- /dev/null +++ b/src/managers/eventLoop/EventLoopTimer.cpp @@ -0,0 +1,53 @@ +#include "EventLoopTimer.hpp" +#include +#include "EventLoopManager.hpp" + +CEventLoopTimer::CEventLoopTimer(std::optional timeout, std::function self, void* data)> cb_, + void* data_) : + cb(cb_), + data(data_) { + + if (!timeout.has_value()) + expires.reset(); + else + expires = std::chrono::system_clock::now() + *timeout; +} + +void CEventLoopTimer::updateTimeout(std::optional timeout) { + if (!timeout.has_value()) { + expires.reset(); + g_pEventLoopManager->nudgeTimers(); + return; + } + + expires = std::chrono::system_clock::now() + *timeout; + + g_pEventLoopManager->nudgeTimers(); +} + +bool CEventLoopTimer::passed() { + if (!expires.has_value()) + return false; + return std::chrono::system_clock::now() > *expires; +} + +void CEventLoopTimer::cancel() { + wasCancelled = true; + expires.reset(); +} + +bool CEventLoopTimer::cancelled() { + return wasCancelled; +} + +void CEventLoopTimer::call(std::shared_ptr self) { + expires.reset(); + cb(self, data); +} + +float CEventLoopTimer::leftUs() { + if (!expires.has_value()) + return std::numeric_limits::max(); + + return std::chrono::duration_cast(*expires - std::chrono::system_clock::now()).count(); +} diff --git a/src/managers/eventLoop/EventLoopTimer.hpp b/src/managers/eventLoop/EventLoopTimer.hpp new file mode 100644 index 00000000..8865d29f --- /dev/null +++ b/src/managers/eventLoop/EventLoopTimer.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + +class CEventLoopTimer { + public: + CEventLoopTimer(std::optional timeout, std::function self, void* data)> cb_, void* data_); + + // if not specified, disarms. + // if specified, arms. + void updateTimeout(std::optional timeout); + + void cancel(); + bool passed(); + + float leftUs(); + + bool cancelled(); + // resets expires + void call(std::shared_ptr self); + + private: + std::function self, void* data)> cb; + void* data = nullptr; + std::optional expires; + bool wasCancelled = false; +};