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; std::function<void(eBackendLogLevel, std::string)> logFunction;
}; };
struct SPollFD {
int fd = -1;
std::function<void(void)> onSignal; /* call this when signaled */
};
class IBackendImplementation { class IBackendImplementation {
public: public:
virtual ~IBackendImplementation() { virtual ~IBackendImplementation() {
@ -61,15 +66,15 @@ namespace Aquamarine {
AQ_BACKEND_CAPABILITY_POINTER = (1 << 0), AQ_BACKEND_CAPABILITY_POINTER = (1 << 0),
}; };
virtual eBackendType type() = 0; virtual eBackendType type() = 0;
virtual bool start() = 0; virtual bool start() = 0;
virtual int pollFD() = 0; virtual std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> pollFDs() = 0;
virtual int drmFD() = 0; virtual int drmFD() = 0;
virtual bool dispatchEvents() = 0; virtual bool dispatchEvents() = 0;
virtual uint32_t capabilities() = 0; virtual uint32_t capabilities() = 0;
virtual void onReady() = 0; virtual void onReady() = 0;
virtual std::vector<SDRMFormat> getRenderFormats() = 0; virtual std::vector<SDRMFormat> getRenderFormats() = 0;
virtual std::vector<SDRMFormat> getCursorFormats() = 0; virtual std::vector<SDRMFormat> getCursorFormats() = 0;
}; };
class CBackend { class CBackend {
@ -84,15 +89,8 @@ namespace Aquamarine {
void log(eBackendLogLevel level, const std::string& msg); 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, /* Gets all the FDs you have to poll. When any single one fires, call its onPoll */
see the async methods further below */ std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> getPollFDs();
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();
/* Checks if the backend has a session - iow if it's a DRM backend */ /* Checks if the backend has a session - iow if it's a DRM backend */
bool hasSession(); bool hasSession();
@ -126,7 +124,7 @@ namespace Aquamarine {
std::vector<Hyprutils::Memory::CSharedPointer<IBackendImplementation>> implementations; std::vector<Hyprutils::Memory::CSharedPointer<IBackendImplementation>> implementations;
SBackendOptions options; SBackendOptions options;
Hyprutils::Memory::CWeakPointer<CBackend> self; Hyprutils::Memory::CWeakPointer<CBackend> self;
std::vector<int> sessionFDs; std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> sessionFDs;
// //
struct { struct {

View file

@ -253,23 +253,23 @@ namespace Aquamarine {
class CDRMBackend : public IBackendImplementation { class CDRMBackend : public IBackendImplementation {
public: public:
virtual ~CDRMBackend(); virtual ~CDRMBackend();
virtual eBackendType type(); virtual eBackendType type();
virtual bool start(); virtual bool start();
virtual int pollFD(); virtual std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> pollFDs();
virtual int drmFD(); virtual int drmFD();
virtual bool dispatchEvents(); virtual bool dispatchEvents();
virtual uint32_t capabilities(); virtual uint32_t capabilities();
virtual bool setCursor(Hyprutils::Memory::CSharedPointer<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot); virtual bool setCursor(Hyprutils::Memory::CSharedPointer<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot);
virtual void onReady(); virtual void onReady();
virtual std::vector<SDRMFormat> getRenderFormats(); virtual std::vector<SDRMFormat> getRenderFormats();
virtual std::vector<SDRMFormat> getCursorFormats(); virtual std::vector<SDRMFormat> getCursorFormats();
Hyprutils::Memory::CWeakPointer<CDRMBackend> self; Hyprutils::Memory::CWeakPointer<CDRMBackend> self;
void log(eBackendLogLevel, const std::string&); void log(eBackendLogLevel, const std::string&);
bool sessionActive(); bool sessionActive();
std::vector<FIdleCallback> idleCallbacks; std::vector<FIdleCallback> idleCallbacks;
private: private:
CDRMBackend(Hyprutils::Memory::CSharedPointer<CBackend> backend); CDRMBackend(Hyprutils::Memory::CSharedPointer<CBackend> backend);

View file

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

View file

@ -121,18 +121,18 @@ namespace Aquamarine {
class CWaylandBackend : public IBackendImplementation { class CWaylandBackend : public IBackendImplementation {
public: public:
virtual ~CWaylandBackend(); virtual ~CWaylandBackend();
virtual eBackendType type(); virtual eBackendType type();
virtual bool start(); virtual bool start();
virtual int pollFD(); virtual std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> pollFDs();
virtual int drmFD(); virtual int drmFD();
virtual bool dispatchEvents(); virtual bool dispatchEvents();
virtual uint32_t capabilities(); virtual uint32_t capabilities();
virtual bool setCursor(Hyprutils::Memory::CSharedPointer<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot); virtual bool setCursor(Hyprutils::Memory::CSharedPointer<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot);
virtual void onReady(); virtual void onReady();
virtual std::vector<SDRMFormat> getRenderFormats(); virtual std::vector<SDRMFormat> getRenderFormats();
virtual std::vector<SDRMFormat> getCursorFormats(); virtual std::vector<SDRMFormat> getCursorFormats();
Hyprutils::Memory::CWeakPointer<CWaylandBackend> self; Hyprutils::Memory::CWeakPointer<CWaylandBackend> self;
private: private:
CWaylandBackend(Hyprutils::Memory::CSharedPointer<CBackend> backend); CWaylandBackend(Hyprutils::Memory::CSharedPointer<CBackend> backend);

View file

@ -100,7 +100,7 @@ bool Aquamarine::CBackend::start() {
// erase failed impls // erase failed impls
std::erase_if(implementations, [this](const auto& i) { std::erase_if(implementations, [this](const auto& i) {
bool failed = i->pollFD() < 0; bool failed = i->pollFDs().empty();
if (failed) if (failed)
log(AQ_LOG_ERROR, std::format("Implementation {} failed, erasing.", backendTypeToName(i->type()))); log(AQ_LOG_ERROR, std::format("Implementation {} failed, erasing.", backendTypeToName(i->type())));
return failed; return failed;
@ -122,7 +122,7 @@ bool Aquamarine::CBackend::start() {
b->onReady(); b->onReady();
} }
sessionFDs = session ? session->pollFDs() : std::vector<int>{}; sessionFDs = session ? session->pollFDs() : std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>>{};
return true; return true;
} }
@ -134,76 +134,17 @@ void Aquamarine::CBackend::log(eBackendLogLevel level, const std::string& msg) {
options.logFunction(level, msg); options.logFunction(level, msg);
} }
void Aquamarine::CBackend::enterLoop() { std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> Aquamarine::CBackend::getPollFDs() {
std::vector<pollfd> pollFDs; std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> result;
for (auto& i : implementations) { for (auto& i : implementations) {
auto fd = i->pollFD(); auto pollfds = i->pollFDs();
for (auto& p : pollfds) {
pollFDs.emplace_back(pollfd{.fd = fd, .events = POLLIN, .revents = 0}); 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) { for (auto& sfd : sessionFDs) {
result.push_back(sfd); result.emplace_back(sfd);
} }
return result; return result;
@ -220,15 +161,6 @@ int Aquamarine::CBackend::drmFD() {
return -1; return -1;
} }
void Aquamarine::CBackend::dispatchEventsAsync() {
for (auto& i : implementations) {
i->dispatchEvents();
}
if (session)
session->dispatchPendingEventsAsync();
}
bool Aquamarine::CBackend::hasSession() { bool Aquamarine::CBackend::hasSession() {
return session; 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) if (libseat_dispatch(libseatHandle, 0) == -1)
backend->log(AQ_LOG_ERROR, "Couldn't dispatch libseat events"); backend->log(AQ_LOG_ERROR, "Couldn't dispatch libseat events");
}
void Aquamarine::CSession::dispatchPendingEventsAsync() {
dispatchLibseatEvents();
dispatchUdevEvents(); dispatchUdevEvents();
dispatchLibinputEvents(); dispatchLibinputEvents();
} }
std::vector<int> Aquamarine::CSession::pollFDs() { std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> Aquamarine::CSession::pollFDs() {
if (!libseatHandle || !udevMonitor || !udevHandle) // clang-format off
return {}; return {
makeShared<SPollFD>(libseat_get_fd(libseatHandle), [this](){ dispatchLibseatEvents(); }),
return {libseat_get_fd(libseatHandle), udev_monitor_get_fd(udevMonitor), libinput_get_fd(libinputHandle)}; 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) { 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)); }); 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) 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() { bool Aquamarine::CWaylandBackend::dispatchEvents() {

View file

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

View file

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