Headless: Add backend

This commit is contained in:
Vaxry 2024-07-01 14:33:05 +02:00
parent b8fc53e69d
commit 51cd6f4374
10 changed files with 291 additions and 8 deletions

View file

@ -75,6 +75,7 @@ namespace Aquamarine {
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;
virtual bool createOutput() = 0;
}; };
class CBackend { class CBackend {

View file

@ -289,6 +289,7 @@ namespace Aquamarine {
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();
virtual bool createOutput();
Hyprutils::Memory::CWeakPointer<CDRMBackend> self; Hyprutils::Memory::CWeakPointer<CDRMBackend> self;

View file

@ -0,0 +1,77 @@
#pragma once
#include "./Backend.hpp"
#include "../allocator/Swapchain.hpp"
#include "../output/Output.hpp"
#include <hyprutils/memory/WeakPtr.hpp>
namespace Aquamarine {
class CBackend;
class CHeadlessBackend;
class CHeadlessOutput : public IOutput {
public:
virtual ~CHeadlessOutput();
virtual bool commit();
virtual bool test();
virtual Hyprutils::Memory::CSharedPointer<IBackendImplementation> getBackend();
virtual void scheduleFrame();
virtual bool destroy();
Hyprutils::Memory::CWeakPointer<CHeadlessOutput> self;
private:
CHeadlessOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer<CHeadlessBackend> backend_);
Hyprutils::Memory::CWeakPointer<CHeadlessBackend> backend;
Hyprutils::Memory::CSharedPointer<std::function<void()>> framecb;
bool frameScheduled = false;
friend class CHeadlessBackend;
};
class CHeadlessBackend : public IBackendImplementation {
public:
virtual ~CHeadlessBackend();
virtual eBackendType type();
virtual bool start();
virtual std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> pollFDs();
virtual int drmFD();
virtual bool dispatchEvents();
virtual uint32_t capabilities();
virtual bool setCursor(Hyprutils::Memory::CSharedPointer<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot);
virtual void onReady();
virtual std::vector<SDRMFormat> getRenderFormats();
virtual std::vector<SDRMFormat> getCursorFormats();
virtual bool createOutput();
Hyprutils::Memory::CWeakPointer<CHeadlessBackend> self;
private:
CHeadlessBackend(Hyprutils::Memory::CSharedPointer<CBackend> backend_);
Hyprutils::Memory::CWeakPointer<CBackend> backend;
std::vector<Hyprutils::Memory::CSharedPointer<CHeadlessOutput>> outputs;
size_t outputIDCounter = 0;
class CTimer {
public:
std::chrono::steady_clock::time_point when;
std::function<void(void)> what;
bool expired();
};
struct {
int timerfd = -1;
std::vector<CTimer> timers;
} timers;
void dispatchTimers();
void updateTimerFD();
friend class CBackend;
friend class CHeadlessOutput;
};
};

View file

@ -48,6 +48,7 @@ namespace Aquamarine {
virtual void moveCursor(const Hyprutils::Math::Vector2D& coord); virtual void moveCursor(const Hyprutils::Math::Vector2D& coord);
virtual void scheduleFrame(); virtual void scheduleFrame();
virtual Hyprutils::Math::Vector2D cursorPlaneSize(); virtual Hyprutils::Math::Vector2D cursorPlaneSize();
virtual bool destroy();
Hyprutils::Memory::CWeakPointer<CWaylandOutput> self; Hyprutils::Memory::CWeakPointer<CWaylandOutput> self;
@ -131,6 +132,7 @@ namespace Aquamarine {
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();
virtual bool createOutput();
Hyprutils::Memory::CWeakPointer<CWaylandBackend> self; Hyprutils::Memory::CWeakPointer<CWaylandBackend> self;
@ -153,6 +155,9 @@ namespace Aquamarine {
Hyprutils::Memory::CWeakPointer<CWaylandOutput> focusedOutput; Hyprutils::Memory::CWeakPointer<CWaylandOutput> focusedOutput;
uint32_t lastEnterSerial = 0; uint32_t lastEnterSerial = 0;
// state
size_t lastOutputID = 0;
// dmabuf formats // dmabuf formats
std::vector<SDRMFormat> dmabufFormats; std::vector<SDRMFormat> dmabufFormats;

View file

@ -86,6 +86,7 @@ namespace Aquamarine {
friend class IOutput; friend class IOutput;
friend class CWaylandOutput; friend class CWaylandOutput;
friend class CDRMOutput; friend class CDRMOutput;
friend class CHeadlessOutput;
}; };
class IOutput { class IOutput {
@ -104,6 +105,7 @@ namespace Aquamarine {
virtual Hyprutils::Math::Vector2D cursorPlaneSize(); // -1, -1 means no set size, 0, 0 means error virtual Hyprutils::Math::Vector2D cursorPlaneSize(); // -1, -1 means no set size, 0, 0 means error
virtual void scheduleFrame(); virtual void scheduleFrame();
virtual size_t getGammaSize(); virtual size_t getGammaSize();
virtual bool destroy(); // not all backends allow this!!!
std::string name, description, make, model, serial; std::string name, description, make, model, serial;
Hyprutils::Math::Vector2D physicalSize; Hyprutils::Math::Vector2D physicalSize;

View file

@ -1,5 +1,6 @@
#include <aquamarine/backend/Backend.hpp> #include <aquamarine/backend/Backend.hpp>
#include <aquamarine/backend/Wayland.hpp> #include <aquamarine/backend/Wayland.hpp>
#include <aquamarine/backend/Headless.hpp>
#include <aquamarine/backend/DRM.hpp> #include <aquamarine/backend/DRM.hpp>
#include <aquamarine/allocator/GBM.hpp> #include <aquamarine/allocator/GBM.hpp>
#include <sys/poll.h> #include <sys/poll.h>
@ -78,7 +79,10 @@ Hyprutils::Memory::CSharedPointer<CBackend> Aquamarine::CBackend::create(const s
for (auto& r : ref) { for (auto& r : ref) {
backend->implementations.emplace_back(r); backend->implementations.emplace_back(r);
} }
} else if (b.backendType == AQ_BACKEND_HEADLESS) {
auto ref = SP<CHeadlessBackend>(new CHeadlessBackend(backend));
backend->implementations.emplace_back(ref);
ref->self = ref;
} else { } else {
backend->log(AQ_LOG_ERROR, std::format("Unknown backend id: {}", (int)b.backendType)); backend->log(AQ_LOG_ERROR, std::format("Unknown backend id: {}", (int)b.backendType));
continue; continue;

175
src/backend/Headless.cpp Normal file
View file

@ -0,0 +1,175 @@
#include <aquamarine/backend/Headless.hpp>
#include <fcntl.h>
#include <time.h>
#include <sys/timerfd.h>
#include <string.h>
using namespace Aquamarine;
using namespace Hyprutils::Memory;
using namespace Hyprutils::Math;
#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;
}
}
Aquamarine::CHeadlessOutput::CHeadlessOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer<CHeadlessBackend> backend_) : backend(backend_) {
name = name_;
framecb = makeShared<std::function<void()>>([this]() {
frameScheduled = false;
events.frame.emit();
});
}
Aquamarine::CHeadlessOutput::~CHeadlessOutput() {
backend->backend->removeIdleEvent(framecb);
events.destroy.emit();
}
bool Aquamarine::CHeadlessOutput::commit() {
events.commit.emit();
state->onCommit();
return true;
}
bool Aquamarine::CHeadlessOutput::test() {
return true;
}
Hyprutils::Memory::CSharedPointer<IBackendImplementation> Aquamarine::CHeadlessOutput::getBackend() {
return backend.lock();
}
void Aquamarine::CHeadlessOutput::scheduleFrame() {
// FIXME: limit fps to the committed framerate.
if (frameScheduled)
return;
frameScheduled = true;
backend->backend->addIdleEvent(framecb);
}
bool Aquamarine::CHeadlessOutput::destroy() {
std::erase(backend->outputs, self.lock());
return true;
}
Aquamarine::CHeadlessBackend::~CHeadlessBackend() {
;
}
Aquamarine::CHeadlessBackend::CHeadlessBackend(SP<CBackend> backend_) : backend(backend_) {
timers.timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
}
eBackendType Aquamarine::CHeadlessBackend::type() {
return eBackendType::AQ_BACKEND_HEADLESS;
}
bool Aquamarine::CHeadlessBackend::start() {
return true;
}
std::vector<SP<SPollFD>> Aquamarine::CHeadlessBackend::pollFDs() {
return {makeShared<SPollFD>(timers.timerfd, [this]() { dispatchTimers(); })};
}
int Aquamarine::CHeadlessBackend::drmFD() {
return -1;
}
bool Aquamarine::CHeadlessBackend::dispatchEvents() {
return true;
}
uint32_t Aquamarine::CHeadlessBackend::capabilities() {
return 0;
}
bool Aquamarine::CHeadlessBackend::setCursor(SP<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot) {
return false;
}
void Aquamarine::CHeadlessBackend::onReady() {
;
}
std::vector<SDRMFormat> Aquamarine::CHeadlessBackend::getRenderFormats() {
// FIXME: allow any modifier. Maybe set INVALID to mean that? or a special value?
return {SDRMFormat{.drmFormat = DRM_FORMAT_RGBA8888, .modifiers = {DRM_FORMAT_MOD_LINEAR}},
SDRMFormat{.drmFormat = DRM_FORMAT_RGBA1010102, .modifiers = {DRM_FORMAT_MOD_LINEAR}}};
}
std::vector<SDRMFormat> Aquamarine::CHeadlessBackend::getCursorFormats() {
return {}; // No cursor support
}
bool Aquamarine::CHeadlessBackend::createOutput() {
auto output = SP<CHeadlessOutput>(new CHeadlessOutput(std::format("HEADLESS-{}", ++outputIDCounter), self.lock()));
outputs.emplace_back(output);
output->swapchain = CSwapchain::create(backend->allocator, self.lock());
output->self = output;
backend->events.newOutput.emit(SP<IOutput>(output));
return true;
}
void Aquamarine::CHeadlessBackend::dispatchTimers() {
std::vector<CTimer> toFire;
for (size_t i = 0; i < timers.timers.size(); ++i) {
if (timers.timers.at(i).expired()) {
toFire.emplace_back(timers.timers.at(i));
timers.timers.erase(timers.timers.begin() + i);
i--;
continue;
}
}
for (auto& copy : toFire) {
if (copy.what)
copy.what();
}
updateTimerFD();
}
void Aquamarine::CHeadlessBackend::updateTimerFD() {
long long lowest = 42069133769LL;
const auto clocknow = std::chrono::steady_clock::now();
for (auto& t : timers.timers) {
auto delta = std::chrono::duration_cast<std::chrono::microseconds>(t.when - clocknow).count();
if (delta < lowest)
lowest = delta;
}
if (lowest < 0)
lowest = 0;
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
timespecAddNs(&now, lowest * 1000); // µs -> ns => * 1000
itimerspec ts = {.it_value = now};
if (timerfd_settime(timers.timerfd, TFD_TIMER_ABSTIME, &ts, nullptr))
backend->log(AQ_LOG_ERROR, std::format("headless: failed to arm timerfd: {}", strerror(errno)));
}
bool Aquamarine::CHeadlessBackend::CTimer::expired() {
return std::chrono::steady_clock::now() > when;
}

View file

@ -125,7 +125,7 @@ bool Aquamarine::CWaylandBackend::start() {
dispatchEvents(); dispatchEvents();
createOutput("WAYLAND1"); createOutput();
return true; return true;
} }
@ -431,6 +431,11 @@ std::vector<SDRMFormat> Aquamarine::CWaylandBackend::getCursorFormats() {
return dmabufFormats; return dmabufFormats;
} }
bool Aquamarine::CWaylandBackend::createOutput() {
createOutput(std::format("WAYLAND-{}", ++lastOutputID));
return true;
}
Aquamarine::CWaylandOutput::CWaylandOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_) : backend(backend_) { Aquamarine::CWaylandOutput::CWaylandOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_) : backend(backend_) {
name = name_; name = name_;
@ -469,12 +474,7 @@ Aquamarine::CWaylandOutput::CWaylandOutput(const std::string& name_, Hyprutils::
sendFrameAndSetCallback(); sendFrameAndSetCallback();
}); });
waylandState.xdgToplevel->setClose([this](CCXdgToplevel* r) { waylandState.xdgToplevel->setClose([this](CCXdgToplevel* r) { destroy(); });
events.destroy.emit();
waylandState.surface->sendAttach(nullptr, 0, 0);
waylandState.surface->sendCommit();
std::erase(backend->outputs, self.lock());
});
auto inputRegion = makeShared<CCWlRegion>(backend->waylandState.compositor->sendCreateRegion()); auto inputRegion = makeShared<CCWlRegion>(backend->waylandState.compositor->sendCreateRegion());
inputRegion->sendAdd(0, 0, INT32_MAX, INT32_MAX); inputRegion->sendAdd(0, 0, INT32_MAX, INT32_MAX);
@ -489,6 +489,7 @@ Aquamarine::CWaylandOutput::CWaylandOutput(const std::string& name_, Hyprutils::
} }
Aquamarine::CWaylandOutput::~CWaylandOutput() { Aquamarine::CWaylandOutput::~CWaylandOutput() {
backend->idleCallbacks.clear(); // FIXME: mega hack to avoid a UAF in frame events
if (waylandState.xdgToplevel) if (waylandState.xdgToplevel)
waylandState.xdgToplevel->sendDestroy(); waylandState.xdgToplevel->sendDestroy();
if (waylandState.xdgSurface) if (waylandState.xdgSurface)
@ -497,6 +498,15 @@ Aquamarine::CWaylandOutput::~CWaylandOutput() {
waylandState.surface->sendDestroy(); waylandState.surface->sendDestroy();
} }
bool Aquamarine::CWaylandOutput::destroy() {
events.destroy.emit();
waylandState.surface->sendAttach(nullptr, 0, 0);
waylandState.surface->sendCommit();
waylandState.frameCallback.reset();
std::erase(backend->outputs, self.lock());
return true;
}
bool Aquamarine::CWaylandOutput::test() { bool Aquamarine::CWaylandOutput::test() {
return true; // TODO: return true; // TODO:
} }

View file

@ -651,6 +651,10 @@ std::vector<SDRMFormat> Aquamarine::CDRMBackend::getCursorFormats() {
return {}; return {};
} }
bool Aquamarine::CDRMBackend::createOutput() {
return false;
}
bool Aquamarine::SDRMPlane::init(drmModePlane* plane) { bool Aquamarine::SDRMPlane::init(drmModePlane* plane) {
id = plane->plane_id; id = plane->plane_id;

View file

@ -35,6 +35,10 @@ size_t Aquamarine::IOutput::getGammaSize() {
return 0; return 0;
} }
bool Aquamarine::IOutput::destroy() {
return false;
}
const Aquamarine::COutputState::SInternalState& Aquamarine::COutputState::state() { const Aquamarine::COutputState::SInternalState& Aquamarine::COutputState::state() {
return internalState; return internalState;
} }