aquamarine/src/backend/Backend.cpp

212 lines
6.2 KiB
C++
Raw Normal View History

2024-06-18 11:38:26 +02:00
#include <aquamarine/backend/Backend.hpp>
2024-06-18 18:45:05 +02:00
#include <aquamarine/backend/Wayland.hpp>
#include <aquamarine/allocator/GBM.hpp>
2024-06-18 18:45:05 +02:00
#include <sys/poll.h>
#include <thread>
#include <chrono>
2024-06-18 11:38:26 +02:00
using namespace Hyprutils::Memory;
using namespace Aquamarine;
#define SP CSharedPointer
2024-06-18 18:45:05 +02:00
static const char* backendTypeToName(eBackendType type) {
switch (type) {
case AQ_BACKEND_DRM: return "drm";
case AQ_BACKEND_HEADLESS: return "headless";
case AQ_BACKEND_WAYLAND: return "wayland";
default: break;
}
return "invalid";
}
2024-06-18 11:38:26 +02:00
Aquamarine::CBackend::CBackend() {
;
}
2024-06-18 18:45:05 +02:00
Aquamarine::SBackendImplementationOptions::SBackendImplementationOptions() {
backendType = AQ_BACKEND_WAYLAND;
backendRequestMode = AQ_BACKEND_REQUEST_IF_AVAILABLE;
}
Aquamarine::SBackendOptions::SBackendOptions() {
logFunction = nullptr;
}
2024-06-18 11:38:26 +02:00
Hyprutils::Memory::CSharedPointer<CBackend> Aquamarine::CBackend::create(const std::vector<SBackendImplementationOptions>& backends, const SBackendOptions& options) {
auto backend = SP<CBackend>(new CBackend());
2024-06-18 18:45:05 +02:00
backend->options = options;
backend->implementationOptions = backends;
backend->self = backend;
if (backends.size() <= 0)
return nullptr;
2024-06-18 11:38:26 +02:00
2024-06-18 18:45:05 +02:00
backend->log(AQ_LOG_DEBUG, "Creating an Aquamarine backend!");
for (auto& b : backends) {
if (b.backendType == AQ_BACKEND_WAYLAND) {
auto ref = SP<CWaylandBackend>(new CWaylandBackend(backend));
backend->implementations.emplace_back(ref);
ref->self = ref;
} else {
backend->log(AQ_LOG_ERROR, std::format("Unknown backend id: {}", (int)b.backendType));
continue;
}
}
2024-06-18 11:38:26 +02:00
return backend;
}
Aquamarine::CBackend::~CBackend() {
2024-06-18 18:45:05 +02:00
;
2024-06-18 11:38:26 +02:00
}
bool Aquamarine::CBackend::start() {
2024-06-18 18:45:05 +02:00
log(AQ_LOG_DEBUG, "Starting the Aquamarine backend!");
bool fallback = false;
int started = 0;
for (size_t i = 0; i < implementations.size(); ++i) {
const bool ok = implementations.at(i)->start();
if (!ok) {
log(AQ_LOG_ERROR, std::format("Requested backend ({}) could not start, enabling fallbacks", backendTypeToName(implementationOptions.at(i).backendType)));
fallback = true;
if (implementationOptions.at(i).backendRequestMode == AQ_BACKEND_REQUEST_MANDATORY) {
log(AQ_LOG_CRITICAL,
std::format("Requested backend ({}) could not start and it's mandatory, cannot continue!", backendTypeToName(implementationOptions.at(i).backendType)));
implementations.clear();
return false;
}
} else
started++;
}
if (implementations.empty() || started <= 0) {
log(AQ_LOG_CRITICAL, "No backend could be opened. Make sure there was a correct backend passed to CBackend, and that your environment supports at least one of them.");
return false;
}
// erase failed impls
std::erase_if(implementations, [](const auto& i) { return i->pollFD() < 0; });
// TODO: obviously change this when (if) we add different allocators.
for (auto& b : implementations) {
if (b->drmFD() >= 0) {
allocator = CGBMAllocator::create(b->drmFD(), self);
break;
}
}
if (!allocator)
return false;
for (auto& b : implementations) {
b->onReady();
}
2024-06-18 11:38:26 +02:00
return true;
}
void Aquamarine::CBackend::log(eBackendLogLevel level, const std::string& msg) {
if (!options.logFunction)
return;
options.logFunction(level, msg);
}
2024-06-18 18:45:05 +02:00
void Aquamarine::CBackend::enterLoop() {
std::vector<pollfd> pollFDs;
for (auto& i : implementations) {
auto fd = i->pollFD();
pollFDs.emplace_back(pollfd{.fd = fd, .events = POLLIN, .revents = 0});
}
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);
2024-06-18 18:45:05 +02:00
}
return result;
}
int Aquamarine::CBackend::drmFD() {
for (auto& i : implementations) {
int fd = i->drmFD();
if (fd < 0)
continue;
return fd;
}
return -1;
}
void Aquamarine::CBackend::dispatchEventsAsync() {
for (auto& i : implementations) {
i->dispatchEvents();
}
}
bool Aquamarine::CBackend::hasSession() {
// TODO:
return false;
2024-06-18 18:45:05 +02:00
}