mirror of
https://github.com/hyprwm/aquamarine.git
synced 2024-11-17 03:46:00 +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 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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
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 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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
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();
|
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:
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue