mirror of
https://github.com/hyprwm/Hyprland
synced 2024-11-22 15:45:59 +01:00
parent
c0d283016b
commit
863c7b6072
3 changed files with 61 additions and 128 deletions
|
@ -2,139 +2,82 @@
|
||||||
#include "../../debug/Log.hpp"
|
#include "../../debug/Log.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
|
#include <sys/timerfd.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#define TIMESPEC_NSEC_PER_SEC 1000000000L
|
||||||
|
|
||||||
|
CEventLoopManager::CEventLoopManager() {
|
||||||
|
m_sTimers.timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int timerWrite(int fd, uint32_t mask, void* data) {
|
||||||
|
g_pEventLoopManager->onTimerFire();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
void CEventLoopManager::enterLoop(wl_display* display, wl_event_loop* wlEventLoop) {
|
void CEventLoopManager::enterLoop(wl_display* display, wl_event_loop* wlEventLoop) {
|
||||||
m_sWayland.loop = wlEventLoop;
|
m_sWayland.loop = wlEventLoop;
|
||||||
m_sWayland.display = display;
|
m_sWayland.display = display;
|
||||||
|
|
||||||
pollfd pollfds[] = {
|
wl_event_loop_add_fd(wlEventLoop, m_sTimers.timerfd, WL_EVENT_READABLE, timerWrite, nullptr);
|
||||||
{
|
|
||||||
.fd = wl_event_loop_get_fd(wlEventLoop),
|
|
||||||
.events = POLLIN,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
std::thread pollThr([this, &pollfds]() {
|
wl_display_run(display);
|
||||||
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<std::mutex> lg2(m_sLoopState.eventRequestMutex);
|
|
||||||
std::lock_guard<std::mutex> 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<std::mutex> lg2(m_sLoopState.eventRequestMutex);
|
|
||||||
std::lock_guard<std::mutex> 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<std::mutex> 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! :(");
|
Debug::log(LOG, "Kicked off the event loop! :(");
|
||||||
|
}
|
||||||
|
|
||||||
m_sTimers.event = true;
|
void CEventLoopManager::onTimerFire() {
|
||||||
m_sTimers.cv.notify_all();
|
for (auto& t : m_sTimers.timers) {
|
||||||
|
if (t->passed() && !t->cancelled())
|
||||||
|
t->call(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
nudgeTimers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEventLoopManager::addTimer(std::shared_ptr<CEventLoopTimer> timer) {
|
void CEventLoopManager::addTimer(std::shared_ptr<CEventLoopTimer> timer) {
|
||||||
m_sTimers.timersMutex.lock();
|
|
||||||
m_sTimers.timers.push_back(timer);
|
m_sTimers.timers.push_back(timer);
|
||||||
m_sTimers.timersMutex.unlock();
|
|
||||||
nudgeTimers();
|
nudgeTimers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEventLoopManager::removeTimer(std::shared_ptr<CEventLoopTimer> timer) {
|
void CEventLoopManager::removeTimer(std::shared_ptr<CEventLoopTimer> timer) {
|
||||||
m_sTimers.timersMutex.lock();
|
|
||||||
std::erase_if(m_sTimers.timers, [timer](const auto& t) { return timer == t; });
|
std::erase_if(m_sTimers.timers, [timer](const auto& t) { return timer == t; });
|
||||||
m_sTimers.timersMutex.unlock();
|
nudgeTimers();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void timespecAddNs(timespec* pTimespec, int64_t delta) {
|
||||||
|
int delta_ns_low = delta % TIMESPEC_NSEC_PER_SEC;
|
||||||
|
int delta_s_high = delta / TIMESPEC_NSEC_PER_SEC;
|
||||||
|
|
||||||
|
pTimespec->tv_sec += delta_s_high;
|
||||||
|
|
||||||
|
pTimespec->tv_nsec += (long)delta_ns_low;
|
||||||
|
if (pTimespec->tv_nsec >= TIMESPEC_NSEC_PER_SEC) {
|
||||||
|
pTimespec->tv_nsec -= TIMESPEC_NSEC_PER_SEC;
|
||||||
|
++pTimespec->tv_sec;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEventLoopManager::nudgeTimers() {
|
void CEventLoopManager::nudgeTimers() {
|
||||||
m_sTimers.event = true;
|
|
||||||
m_sTimers.cv.notify_all();
|
long nextTimerUs = 10 * 1000 * 1000; // 10s
|
||||||
|
|
||||||
|
for (auto& t : m_sTimers.timers) {
|
||||||
|
if (const auto µs = t->leftUs(); µs < nextTimerUs)
|
||||||
|
nextTimerUs = µs;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextTimerUs = std::clamp(nextTimerUs + 1, 1L, std::numeric_limits<long>::max());
|
||||||
|
|
||||||
|
timespec now;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
timespecAddNs(&now, nextTimerUs * 1000L);
|
||||||
|
|
||||||
|
itimerspec ts = {.it_value = now};
|
||||||
|
|
||||||
|
timerfd_settime(m_sTimers.timerfd, TFD_TIMER_ABSTIME, &ts, nullptr);
|
||||||
}
|
}
|
|
@ -10,10 +10,14 @@
|
||||||
|
|
||||||
class CEventLoopManager {
|
class CEventLoopManager {
|
||||||
public:
|
public:
|
||||||
|
CEventLoopManager();
|
||||||
|
|
||||||
void enterLoop(wl_display* display, wl_event_loop* wlEventLoop);
|
void enterLoop(wl_display* display, wl_event_loop* wlEventLoop);
|
||||||
void addTimer(std::shared_ptr<CEventLoopTimer> timer);
|
void addTimer(std::shared_ptr<CEventLoopTimer> timer);
|
||||||
void removeTimer(std::shared_ptr<CEventLoopTimer> timer);
|
void removeTimer(std::shared_ptr<CEventLoopTimer> timer);
|
||||||
|
|
||||||
|
void onTimerFire();
|
||||||
|
|
||||||
// recalculates timers
|
// recalculates timers
|
||||||
void nudgeTimers();
|
void nudgeTimers();
|
||||||
|
|
||||||
|
@ -21,26 +25,12 @@ class CEventLoopManager {
|
||||||
struct {
|
struct {
|
||||||
wl_event_loop* loop = nullptr;
|
wl_event_loop* loop = nullptr;
|
||||||
wl_display* display = nullptr;
|
wl_display* display = nullptr;
|
||||||
std::thread pollThread;
|
|
||||||
} m_sWayland;
|
} m_sWayland;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
std::mutex timersMutex;
|
|
||||||
std::mutex timersRqMutex;
|
|
||||||
std::vector<std::shared_ptr<CEventLoopTimer>> timers;
|
std::vector<std::shared_ptr<CEventLoopTimer>> timers;
|
||||||
std::thread timerThread;
|
int timerfd = -1;
|
||||||
bool event = false;
|
|
||||||
std::condition_variable cv;
|
|
||||||
} m_sTimers;
|
} 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<CEventLoopManager> g_pEventLoopManager;
|
inline std::unique_ptr<CEventLoopManager> g_pEventLoopManager;
|
|
@ -1,5 +1,5 @@
|
||||||
#include "EventLoopTimer.hpp"
|
#include "EventLoopTimer.hpp"
|
||||||
#include <numeric>
|
#include <limits>
|
||||||
#include "EventLoopManager.hpp"
|
#include "EventLoopManager.hpp"
|
||||||
|
|
||||||
CEventLoopTimer::CEventLoopTimer(std::optional<std::chrono::system_clock::duration> timeout, std::function<void(std::shared_ptr<CEventLoopTimer> self, void* data)> cb_,
|
CEventLoopTimer::CEventLoopTimer(std::optional<std::chrono::system_clock::duration> timeout, std::function<void(std::shared_ptr<CEventLoopTimer> self, void* data)> cb_,
|
||||||
|
|
Loading…
Reference in a new issue