summaryrefslogtreecommitdiff
path: root/src/protocols/Fifo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/protocols/Fifo.cpp')
-rw-r--r--src/protocols/Fifo.cpp192
1 files changed, 192 insertions, 0 deletions
diff --git a/src/protocols/Fifo.cpp b/src/protocols/Fifo.cpp
new file mode 100644
index 00000000..63ce8579
--- /dev/null
+++ b/src/protocols/Fifo.cpp
@@ -0,0 +1,192 @@
+#include "Fifo.hpp"
+#include "Compositor.hpp"
+#include "core/Compositor.hpp"
+#include "../managers/HookSystemManager.hpp"
+#include "../helpers/Monitor.hpp"
+
+CFifoResource::CFifoResource(UP<CWpFifoV1>&& resource_, SP<CWLSurfaceResource> surface) : m_resource(std::move(resource_)), m_surface(surface) {
+ if UNLIKELY (!m_resource->resource())
+ return;
+
+ m_resource->setData(this);
+ m_resource->setDestroy([this](CWpFifoV1* r) { PROTO::fifo->destroyResource(this); });
+ m_resource->setOnDestroy([this](CWpFifoV1* r) { PROTO::fifo->destroyResource(this); });
+
+ m_resource->setSetBarrier([this](CWpFifoV1* r) {
+ if (!m_surface) {
+ r->error(WP_FIFO_V1_ERROR_SURFACE_DESTROYED, "Surface was gone");
+ return;
+ }
+
+ m_pending.barrierSet = true;
+ });
+
+ m_resource->setWaitBarrier([this](CWpFifoV1* r) {
+ if (!m_surface) {
+ r->error(WP_FIFO_V1_ERROR_SURFACE_DESTROYED, "Surface was gone");
+ return;
+ }
+
+ if (!m_pending.barrierSet)
+ return;
+
+ m_pending.surfaceLocked = true;
+ });
+
+ m_listeners.surfaceStateCommit = m_surface->m_events.stateCommit.listen([this](auto state) {
+ if (!m_pending.surfaceLocked)
+ return;
+
+ //#TODO:
+ // this feels wrong, but if we have no pending frames, presented might never come because
+ // we are waiting on the barrier to unlock and no damage is around.
+ if (m_surface->m_enteredOutputs.empty() && m_surface->m_hlSurface) {
+ for (auto& m : g_pCompositor->m_monitors) {
+ if (!m || !m->m_enabled)
+ continue;
+
+ auto box = m_surface->m_hlSurface->getSurfaceBoxGlobal();
+ if (box && !box->intersection({m->m_position, m->m_size}).empty()) {
+ if (m->m_tearingState.activelyTearing)
+ return; // dont fifo lock on tearing.
+
+ g_pCompositor->scheduleFrameForMonitor(m, Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME);
+ }
+ }
+ } else {
+ for (auto& m : m_surface->m_enteredOutputs) {
+ if (!m)
+ continue;
+
+ if (m->m_tearingState.activelyTearing)
+ return; // dont fifo lock on tearing.
+
+ g_pCompositor->scheduleFrameForMonitor(m.lock(), Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME);
+ }
+ }
+
+ // only lock once its mapped.
+ if (m_surface->m_mapped)
+ m_surface->m_stateQueue.lock(state, LOCK_REASON_FIFO);
+
+ m_pending = {};
+ });
+}
+
+CFifoResource::~CFifoResource() {
+ ;
+}
+
+bool CFifoResource::good() {
+ return m_resource->resource();
+}
+
+void CFifoResource::presented() {
+ m_surface->m_stateQueue.unlockFirst(LOCK_REASON_FIFO);
+}
+
+CFifoManagerResource::CFifoManagerResource(UP<CWpFifoManagerV1>&& resource_) : m_resource(std::move(resource_)) {
+ if UNLIKELY (!m_resource->resource())
+ return;
+
+ m_resource->setDestroy([this](CWpFifoManagerV1* r) { PROTO::fifo->destroyResource(this); });
+ m_resource->setOnDestroy([this](CWpFifoManagerV1* r) { PROTO::fifo->destroyResource(this); });
+
+ m_resource->setGetFifo([](CWpFifoManagerV1* r, uint32_t id, wl_resource* surfResource) {
+ if (!surfResource) {
+ r->error(-1, "No resource for fifo");
+ return;
+ }
+
+ auto surf = CWLSurfaceResource::fromResource(surfResource);
+
+ if (!surf) {
+ r->error(-1, "No surface for fifo");
+ return;
+ }
+
+ if (surf->m_fifo) {
+ r->error(WP_FIFO_MANAGER_V1_ERROR_ALREADY_EXISTS, "Surface already has a fifo");
+ return;
+ }
+
+ const auto& RESOURCE = PROTO::fifo->m_fifos.emplace_back(makeUnique<CFifoResource>(makeUnique<CWpFifoV1>(r->client(), r->version(), id), surf));
+
+ if (!RESOURCE->good()) {
+ r->noMemory();
+ PROTO::fifo->m_fifos.pop_back();
+ return;
+ }
+
+ surf->m_fifo = RESOURCE;
+ LOGM(LOG, "New fifo at {:x} for surface {:x}", (uintptr_t)RESOURCE, (uintptr_t)surf.get());
+ });
+}
+
+CFifoManagerResource::~CFifoManagerResource() {
+ ;
+}
+
+bool CFifoManagerResource::good() {
+ return m_resource->resource();
+}
+
+CFifoProtocol::CFifoProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
+ static auto P = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any param) {
+ auto M = std::any_cast<PHLMONITOR>(param);
+
+ M->m_events.presented.listenStatic([this, m = PHLMONITORREF{M}]() {
+ if (!m || !PROTO::fifo)
+ return;
+
+ onMonitorPresent(m.lock());
+ });
+ });
+}
+
+void CFifoProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
+ const auto RESOURCE = m_managers.emplace_back(makeUnique<CFifoManagerResource>(makeUnique<CWpFifoManagerV1>(client, ver, id))).get();
+
+ if (!RESOURCE->good()) {
+ wl_client_post_no_memory(client);
+ m_managers.pop_back();
+ return;
+ }
+}
+
+void CFifoProtocol::destroyResource(CFifoManagerResource* res) {
+ std::erase_if(m_managers, [&](const auto& other) { return other.get() == res; });
+}
+
+void CFifoProtocol::destroyResource(CFifoResource* res) {
+ std::erase_if(m_fifos, [&](const auto& other) { return other.get() == res; });
+}
+
+void CFifoProtocol::onMonitorPresent(PHLMONITOR m) {
+ if (m->m_tearingState.activelyTearing)
+ return; // fifo isnt locked on tearing.
+
+ for (const auto& fifo : m_fifos) {
+ if (!fifo->m_surface)
+ continue;
+
+ if (!fifo->m_surface->m_mapped) {
+ fifo->presented();
+ continue;
+ }
+
+ auto it = std::ranges::find_if(fifo->m_surface->m_enteredOutputs, [m](auto& mon) { return mon == m; });
+ if (it != fifo->m_surface->m_enteredOutputs.end()) {
+ fifo->presented();
+ continue;
+ }
+
+ if (fifo->m_surface->m_hlSurface) {
+ auto box = fifo->m_surface->m_hlSurface->getSurfaceBoxGlobal();
+ if (box && !box->intersection({m->m_position, m->m_size}).empty()) {
+ fifo->presented();
+ continue;
+ }
+ }
+ }
+}