#include #include #include #include #include #include #include #include #include #include #include using namespace Hyprutils::Memory; using namespace Aquamarine; #define SP CSharedPointer #define TIMESPEC_NSEC_PER_SEC 1000000000L 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; } } 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"; } Aquamarine::CBackend::CBackend() { ; } Aquamarine::SBackendImplementationOptions::SBackendImplementationOptions() { backendType = AQ_BACKEND_WAYLAND; backendRequestMode = AQ_BACKEND_REQUEST_IF_AVAILABLE; } Aquamarine::SBackendOptions::SBackendOptions() { logFunction = nullptr; } Hyprutils::Memory::CSharedPointer Aquamarine::CBackend::create(const std::vector& backends, const SBackendOptions& options) { auto backend = SP(new CBackend()); backend->options = options; backend->implementationOptions = backends; backend->self = backend; if (backends.size() <= 0) return nullptr; backend->log(AQ_LOG_DEBUG, "Creating an Aquamarine backend!"); for (auto& b : backends) { if (b.backendType == AQ_BACKEND_WAYLAND) { auto ref = SP(new CWaylandBackend(backend)); backend->implementations.emplace_back(ref); ref->self = ref; } else if (b.backendType == AQ_BACKEND_DRM) { auto ref = CDRMBackend::attempt(backend); if (ref.empty()) { backend->log(AQ_LOG_ERROR, "DRM Backend failed"); continue; } for (auto& r : ref) { backend->implementations.emplace_back(r); } } else if (b.backendType == AQ_BACKEND_HEADLESS) { auto ref = SP(new CHeadlessBackend(backend)); backend->implementations.emplace_back(ref); ref->self = ref; } else { backend->log(AQ_LOG_ERROR, std::format("Unknown backend id: {}", (int)b.backendType)); continue; } } // create a timerfd for idle events backend->idle.fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); return backend; } Aquamarine::CBackend::~CBackend() { ; } bool Aquamarine::CBackend::start() { log(AQ_LOG_DEBUG, "Starting the Aquamarine backend!"); bool fallback = false; int started = 0; auto optionsForType = [this](eBackendType type) -> SBackendImplementationOptions { for (auto& o : implementationOptions) { if (o.backendType == type) return o; } return SBackendImplementationOptions{}; }; 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(implementations.at(i)->type()))); fallback = true; if (optionsForType(implementations.at(i)->type()).backendRequestMode == AQ_BACKEND_REQUEST_MANDATORY) { log(AQ_LOG_CRITICAL, std::format("Requested backend ({}) could not start and it's mandatory, cannot continue!", backendTypeToName(implementations.at(i)->type()))); 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, [this](const auto& i) { bool failed = i->pollFDs().empty(); if (failed) log(AQ_LOG_ERROR, std::format("Implementation {} failed, erasing.", backendTypeToName(i->type()))); return failed; }); // 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; ready = true; for (auto& b : implementations) { b->onReady(); } sessionFDs = session ? session->pollFDs() : std::vector>{}; return true; } void Aquamarine::CBackend::log(eBackendLogLevel level, const std::string& msg) { if (!options.logFunction) return; options.logFunction(level, msg); } std::vector> Aquamarine::CBackend::getPollFDs() { std::vector> result; for (auto& i : implementations) { auto pollfds = i->pollFDs(); for (auto& p : pollfds) { result.emplace_back(p); } } for (auto& sfd : sessionFDs) { result.emplace_back(sfd); } result.emplace_back(makeShared(idle.fd, [this]() { dispatchIdle(); })); return result; } int Aquamarine::CBackend::drmFD() { for (auto& i : implementations) { int fd = i->drmFD(); if (fd < 0) continue; return fd; } return -1; } bool Aquamarine::CBackend::hasSession() { return session; } std::vector Aquamarine::CBackend::getPrimaryRenderFormats() { for (auto& b : implementations) { if (b->type() != AQ_BACKEND_DRM && b->type() != AQ_BACKEND_WAYLAND) continue; return b->getRenderFormats(); } for (auto& b : implementations) { return b->getRenderFormats(); } return {}; } const std::vector>& Aquamarine::CBackend::getImplementations() { return implementations; } void Aquamarine::CBackend::addIdleEvent(SP> fn) { auto r = idle.pending.emplace_back(fn); // update timerfd timespec now; clock_gettime(CLOCK_MONOTONIC, &now); itimerspec ts = {.it_value = now}; if (timerfd_settime(idle.fd, TFD_TIMER_ABSTIME, &ts, nullptr)) log(AQ_LOG_ERROR, std::format("backend: failed to arm timerfd: {}", strerror(errno))); } void Aquamarine::CBackend::removeIdleEvent(SP> pfn) { std::erase(idle.pending, pfn); } void Aquamarine::CBackend::dispatchIdle() { auto cpy = idle.pending; idle.pending.clear(); for (auto& i : cpy) { if (i && *i) (*i)(); } }