mirror of
https://github.com/hyprwm/aquamarine.git
synced 2024-12-22 14:59:48 +01:00
Headless: Add backend
This commit is contained in:
parent
b8fc53e69d
commit
51cd6f4374
10 changed files with 291 additions and 8 deletions
|
@ -75,6 +75,7 @@ namespace Aquamarine {
|
|||
virtual void onReady() = 0;
|
||||
virtual std::vector<SDRMFormat> getRenderFormats() = 0;
|
||||
virtual std::vector<SDRMFormat> getCursorFormats() = 0;
|
||||
virtual bool createOutput() = 0;
|
||||
};
|
||||
|
||||
class CBackend {
|
||||
|
|
|
@ -289,6 +289,7 @@ namespace Aquamarine {
|
|||
virtual void onReady();
|
||||
virtual std::vector<SDRMFormat> getRenderFormats();
|
||||
virtual std::vector<SDRMFormat> getCursorFormats();
|
||||
virtual bool createOutput();
|
||||
|
||||
Hyprutils::Memory::CWeakPointer<CDRMBackend> self;
|
||||
|
||||
|
|
77
include/aquamarine/backend/Headless.hpp
Normal file
77
include/aquamarine/backend/Headless.hpp
Normal 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;
|
||||
};
|
||||
};
|
|
@ -48,6 +48,7 @@ namespace Aquamarine {
|
|||
virtual void moveCursor(const Hyprutils::Math::Vector2D& coord);
|
||||
virtual void scheduleFrame();
|
||||
virtual Hyprutils::Math::Vector2D cursorPlaneSize();
|
||||
virtual bool destroy();
|
||||
|
||||
Hyprutils::Memory::CWeakPointer<CWaylandOutput> self;
|
||||
|
||||
|
@ -131,6 +132,7 @@ namespace Aquamarine {
|
|||
virtual void onReady();
|
||||
virtual std::vector<SDRMFormat> getRenderFormats();
|
||||
virtual std::vector<SDRMFormat> getCursorFormats();
|
||||
virtual bool createOutput();
|
||||
|
||||
Hyprutils::Memory::CWeakPointer<CWaylandBackend> self;
|
||||
|
||||
|
@ -153,6 +155,9 @@ namespace Aquamarine {
|
|||
Hyprutils::Memory::CWeakPointer<CWaylandOutput> focusedOutput;
|
||||
uint32_t lastEnterSerial = 0;
|
||||
|
||||
// state
|
||||
size_t lastOutputID = 0;
|
||||
|
||||
// dmabuf formats
|
||||
std::vector<SDRMFormat> dmabufFormats;
|
||||
|
||||
|
|
|
@ -86,6 +86,7 @@ namespace Aquamarine {
|
|||
friend class IOutput;
|
||||
friend class CWaylandOutput;
|
||||
friend class CDRMOutput;
|
||||
friend class CHeadlessOutput;
|
||||
};
|
||||
|
||||
class IOutput {
|
||||
|
@ -104,6 +105,7 @@ namespace Aquamarine {
|
|||
virtual Hyprutils::Math::Vector2D cursorPlaneSize(); // -1, -1 means no set size, 0, 0 means error
|
||||
virtual void scheduleFrame();
|
||||
virtual size_t getGammaSize();
|
||||
virtual bool destroy(); // not all backends allow this!!!
|
||||
|
||||
std::string name, description, make, model, serial;
|
||||
Hyprutils::Math::Vector2D physicalSize;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <aquamarine/backend/Backend.hpp>
|
||||
#include <aquamarine/backend/Wayland.hpp>
|
||||
#include <aquamarine/backend/Headless.hpp>
|
||||
#include <aquamarine/backend/DRM.hpp>
|
||||
#include <aquamarine/allocator/GBM.hpp>
|
||||
#include <sys/poll.h>
|
||||
|
@ -78,7 +79,10 @@ Hyprutils::Memory::CSharedPointer<CBackend> Aquamarine::CBackend::create(const s
|
|||
for (auto& r : ref) {
|
||||
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 {
|
||||
backend->log(AQ_LOG_ERROR, std::format("Unknown backend id: {}", (int)b.backendType));
|
||||
continue;
|
||||
|
|
175
src/backend/Headless.cpp
Normal file
175
src/backend/Headless.cpp
Normal 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;
|
||||
}
|
|
@ -125,7 +125,7 @@ bool Aquamarine::CWaylandBackend::start() {
|
|||
|
||||
dispatchEvents();
|
||||
|
||||
createOutput("WAYLAND1");
|
||||
createOutput();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -431,6 +431,11 @@ std::vector<SDRMFormat> Aquamarine::CWaylandBackend::getCursorFormats() {
|
|||
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_) {
|
||||
name = name_;
|
||||
|
||||
|
@ -469,12 +474,7 @@ Aquamarine::CWaylandOutput::CWaylandOutput(const std::string& name_, Hyprutils::
|
|||
sendFrameAndSetCallback();
|
||||
});
|
||||
|
||||
waylandState.xdgToplevel->setClose([this](CCXdgToplevel* r) {
|
||||
events.destroy.emit();
|
||||
waylandState.surface->sendAttach(nullptr, 0, 0);
|
||||
waylandState.surface->sendCommit();
|
||||
std::erase(backend->outputs, self.lock());
|
||||
});
|
||||
waylandState.xdgToplevel->setClose([this](CCXdgToplevel* r) { destroy(); });
|
||||
|
||||
auto inputRegion = makeShared<CCWlRegion>(backend->waylandState.compositor->sendCreateRegion());
|
||||
inputRegion->sendAdd(0, 0, INT32_MAX, INT32_MAX);
|
||||
|
@ -489,6 +489,7 @@ Aquamarine::CWaylandOutput::CWaylandOutput(const std::string& name_, Hyprutils::
|
|||
}
|
||||
|
||||
Aquamarine::CWaylandOutput::~CWaylandOutput() {
|
||||
backend->idleCallbacks.clear(); // FIXME: mega hack to avoid a UAF in frame events
|
||||
if (waylandState.xdgToplevel)
|
||||
waylandState.xdgToplevel->sendDestroy();
|
||||
if (waylandState.xdgSurface)
|
||||
|
@ -497,6 +498,15 @@ Aquamarine::CWaylandOutput::~CWaylandOutput() {
|
|||
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() {
|
||||
return true; // TODO:
|
||||
}
|
||||
|
|
|
@ -651,6 +651,10 @@ std::vector<SDRMFormat> Aquamarine::CDRMBackend::getCursorFormats() {
|
|||
return {};
|
||||
}
|
||||
|
||||
bool Aquamarine::CDRMBackend::createOutput() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Aquamarine::SDRMPlane::init(drmModePlane* plane) {
|
||||
id = plane->plane_id;
|
||||
|
||||
|
|
|
@ -35,6 +35,10 @@ size_t Aquamarine::IOutput::getGammaSize() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool Aquamarine::IOutput::destroy() {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Aquamarine::COutputState::SInternalState& Aquamarine::COutputState::state() {
|
||||
return internalState;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue