backend: rework event loop api

This commit is contained in:
Vaxry 2024-06-27 00:07:59 +02:00
parent 461d33583a
commit 511600133c
9 changed files with 77 additions and 137 deletions

View file

@ -51,6 +51,11 @@ namespace Aquamarine {
std::function<void(eBackendLogLevel, std::string)> logFunction;
};
struct SPollFD {
int fd = -1;
std::function<void(void)> onSignal; /* call this when signaled */
};
class IBackendImplementation {
public:
virtual ~IBackendImplementation() {
@ -63,7 +68,7 @@ namespace Aquamarine {
virtual eBackendType type() = 0;
virtual bool start() = 0;
virtual int pollFD() = 0;
virtual std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> pollFDs() = 0;
virtual int drmFD() = 0;
virtual bool dispatchEvents() = 0;
virtual uint32_t capabilities() = 0;
@ -84,15 +89,8 @@ namespace Aquamarine {
void log(eBackendLogLevel level, const std::string& msg);
/* Enters the event loop synchronously. For simple clients, this is probably what you want. For more complex ones,
see the async methods further below */
void enterLoop();
/* Gets all the FDs you have to poll. When any single one fires, call dispatchEventsAsync */
std::vector<int> getPollFDs();
/* Dispatches all pending events on all queues then returns */
void dispatchEventsAsync();
/* Gets all the FDs you have to poll. When any single one fires, call its onPoll */
std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> getPollFDs();
/* Checks if the backend has a session - iow if it's a DRM backend */
bool hasSession();
@ -126,7 +124,7 @@ namespace Aquamarine {
std::vector<Hyprutils::Memory::CSharedPointer<IBackendImplementation>> implementations;
SBackendOptions options;
Hyprutils::Memory::CWeakPointer<CBackend> self;
std::vector<int> sessionFDs;
std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> sessionFDs;
//
struct {

View file

@ -255,7 +255,7 @@ namespace Aquamarine {
virtual ~CDRMBackend();
virtual eBackendType type();
virtual bool start();
virtual int pollFD();
virtual std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> pollFDs();
virtual int drmFD();
virtual bool dispatchEvents();
virtual uint32_t capabilities();

View file

@ -18,6 +18,7 @@ namespace Aquamarine {
class CBackend;
class CSession;
class CLibinputDevice;
struct SPollFD;
class CSessionDevice {
public:
@ -118,7 +119,7 @@ namespace Aquamarine {
libseat* libseatHandle = nullptr;
libinput* libinputHandle = nullptr;
std::vector<int> pollFDs();
std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> pollFDs();
void dispatchPendingEventsAsync();
bool switchVT(uint32_t vt);
void onReady();
@ -135,9 +136,11 @@ namespace Aquamarine {
private:
Hyprutils::Memory::CWeakPointer<CBackend> backend;
std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> polls;
void dispatchUdevEvents();
void dispatchLibinputEvents();
void dispatchLibseatEvents();
void handleLibinputEvent(libinput_event* e);
friend class CSessionDevice;

View file

@ -123,7 +123,7 @@ namespace Aquamarine {
virtual ~CWaylandBackend();
virtual eBackendType type();
virtual bool start();
virtual int pollFD();
virtual std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> pollFDs();
virtual int drmFD();
virtual bool dispatchEvents();
virtual uint32_t capabilities();

View file

@ -100,7 +100,7 @@ bool Aquamarine::CBackend::start() {
// erase failed impls
std::erase_if(implementations, [this](const auto& i) {
bool failed = i->pollFD() < 0;
bool failed = i->pollFDs().empty();
if (failed)
log(AQ_LOG_ERROR, std::format("Implementation {} failed, erasing.", backendTypeToName(i->type())));
return failed;
@ -122,7 +122,7 @@ bool Aquamarine::CBackend::start() {
b->onReady();
}
sessionFDs = session ? session->pollFDs() : std::vector<int>{};
sessionFDs = session ? session->pollFDs() : std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>>{};
return true;
}
@ -134,76 +134,17 @@ void Aquamarine::CBackend::log(eBackendLogLevel level, const std::string& msg) {
options.logFunction(level, msg);
}
void Aquamarine::CBackend::enterLoop() {
std::vector<pollfd> pollFDs;
std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> Aquamarine::CBackend::getPollFDs() {
std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> result;
for (auto& i : implementations) {
auto fd = i->pollFD();
pollFDs.emplace_back(pollfd{.fd = fd, .events = POLLIN, .revents = 0});
auto pollfds = i->pollFDs();
for (auto& p : pollfds) {
result.emplace_back(p);
}
std::thread pollThr([this, &pollFDs]() {
int ret = 0;
while (1) {
ret = poll(pollFDs.data(), pollFDs.size(), 5000 /* 5 seconds, reasonable. It's because we might need to terminate */);
if (ret < 0) {
log(AQ_LOG_CRITICAL, std::format("Polling fds failed with {}", errno));
terminate = true;
exit(1);
}
for (size_t i = 0; i < pollFDs.size(); ++i) {
if (pollFDs[i].revents & POLLHUP) {
log(AQ_LOG_CRITICAL, std::format("disconnected from pollfd {}", i));
terminate = true;
exit(1);
}
}
if (terminate)
break;
if (ret != 0) {
std::lock_guard<std::mutex> lg(m_sEventLoopInternals.loopRequestMutex);
m_sEventLoopInternals.shouldProcess = true;
m_sEventLoopInternals.loopSignal.notify_all();
}
}
});
while (1) {
m_sEventLoopInternals.loopRequestMutex.unlock(); // unlock, we are ready to take events
std::unique_lock lk(m_sEventLoopInternals.loopMutex);
if (m_sEventLoopInternals.shouldProcess == false) // avoid a lock if a thread managed to request something already since we .unlock()ed
m_sEventLoopInternals.loopSignal.wait_for(lk, std::chrono::seconds(5), [this] { return m_sEventLoopInternals.shouldProcess == true; }); // wait for events
m_sEventLoopInternals.loopRequestMutex.lock(); // lock incoming events
if (terminate)
break;
m_sEventLoopInternals.shouldProcess = false;
std::lock_guard<std::mutex> lg(m_sEventLoopInternals.eventLock);
dispatchEventsAsync();
}
}
std::vector<int> Aquamarine::CBackend::getPollFDs() {
std::vector<int> result;
for (auto& i : implementations) {
int fd = i->pollFD();
if (fd < 0)
continue;
result.push_back(fd);
}
for (auto& sfd : sessionFDs) {
result.push_back(sfd);
result.emplace_back(sfd);
}
return result;
@ -220,15 +161,6 @@ int Aquamarine::CBackend::drmFD() {
return -1;
}
void Aquamarine::CBackend::dispatchEventsAsync() {
for (auto& i : implementations) {
i->dispatchEvents();
}
if (session)
session->dispatchPendingEventsAsync();
}
bool Aquamarine::CBackend::hasSession() {
return session;
}

View file

@ -353,19 +353,25 @@ void Aquamarine::CSession::dispatchLibinputEvents() {
}
}
void Aquamarine::CSession::dispatchPendingEventsAsync() {
void Aquamarine::CSession::dispatchLibseatEvents() {
if (libseat_dispatch(libseatHandle, 0) == -1)
backend->log(AQ_LOG_ERROR, "Couldn't dispatch libseat events");
}
void Aquamarine::CSession::dispatchPendingEventsAsync() {
dispatchLibseatEvents();
dispatchUdevEvents();
dispatchLibinputEvents();
}
std::vector<int> Aquamarine::CSession::pollFDs() {
if (!libseatHandle || !udevMonitor || !udevHandle)
return {};
return {libseat_get_fd(libseatHandle), udev_monitor_get_fd(udevMonitor), libinput_get_fd(libinputHandle)};
std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> Aquamarine::CSession::pollFDs() {
// clang-format off
return {
makeShared<SPollFD>(libseat_get_fd(libseatHandle), [this](){ dispatchLibseatEvents(); }),
makeShared<SPollFD>(udev_monitor_get_fd(udevMonitor), [this](){ dispatchUdevEvents(); }),
makeShared<SPollFD>(libinput_get_fd(libinputHandle), [this](){ dispatchLibinputEvents(); })
};
// clang-format on
}
bool Aquamarine::CSession::switchVT(uint32_t vt) {

View file

@ -140,11 +140,11 @@ void Aquamarine::CWaylandBackend::createOutput(const std::string& szName) {
idleCallbacks.emplace_back([this, o]() { backend->events.newOutput.emit(SP<IOutput>(o)); });
}
int Aquamarine::CWaylandBackend::pollFD() {
std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> Aquamarine::CWaylandBackend::pollFDs() {
if (!waylandState.display)
return -1;
return {};
return wl_display_get_fd(waylandState.display);
return {makeShared<SPollFD>(wl_display_get_fd(waylandState.display), [this]() { dispatchEvents(); })};
}
bool Aquamarine::CWaylandBackend::dispatchEvents() {

View file

@ -476,8 +476,8 @@ bool Aquamarine::CDRMBackend::start() {
return true;
}
int Aquamarine::CDRMBackend::pollFD() {
return gpu->fd;
std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> Aquamarine::CDRMBackend::pollFDs() {
return {makeShared<SPollFD>(gpu->fd, [this]() { dispatchEvents(); })};
}
int Aquamarine::CDRMBackend::drmFD() {

View file

@ -90,7 +90,8 @@ int main(int argc, char** argv, char** envp) {
return 1;
}
aqBackend->enterLoop();
// FIXME: write an event loop.
// aqBackend->enterLoop();
return 0;
}