summaryrefslogtreecommitdiff
path: root/src/desktop/Subsurface.cpp
diff options
context:
space:
mode:
authorVaxry <vaxry@vaxry.net>2024-02-29 13:03:38 +0000
committerVaxry <vaxry@vaxry.net>2024-02-29 13:03:38 +0000
commitb39dcfa497f84486569bf862092dfcfadbfd8747 (patch)
treec9f7a99c04ed6091ef79eea41a61d53dd04cc7cb /src/desktop/Subsurface.cpp
parent4bff762d9733ba7334cd37b995cf51552cc80be0 (diff)
refactor: move a few things to desktop/
Diffstat (limited to 'src/desktop/Subsurface.cpp')
-rw-r--r--src/desktop/Subsurface.cpp238
1 files changed, 238 insertions, 0 deletions
diff --git a/src/desktop/Subsurface.cpp b/src/desktop/Subsurface.cpp
new file mode 100644
index 00000000..0923e9cc
--- /dev/null
+++ b/src/desktop/Subsurface.cpp
@@ -0,0 +1,238 @@
+#include "Subsurface.hpp"
+#include "../events/Events.hpp"
+#include "../Compositor.hpp"
+
+static void onNewSubsurface(void* owner, void* data);
+
+CSubsurface::CSubsurface(CWindow* pOwner) : m_pWindowParent(pOwner) {
+ initSignals();
+
+ wlr_subsurface* wlrSubsurface;
+ wl_list_for_each(wlrSubsurface, &pOwner->m_pWLSurface.wlr()->current.subsurfaces_below, current.link) {
+ ::onNewSubsurface(this, wlrSubsurface);
+ }
+ wl_list_for_each(wlrSubsurface, &pOwner->m_pWLSurface.wlr()->current.subsurfaces_above, current.link) {
+ ::onNewSubsurface(this, wlrSubsurface);
+ }
+}
+
+CSubsurface::CSubsurface(wlr_subsurface* pSubsurface, CWindow* pOwner) : m_pSubsurface(pSubsurface), m_pWindowParent(pOwner) {
+ m_sWLSurface.assign(pSubsurface->surface);
+ initSignals();
+ initExistingSubsurfaces();
+}
+
+CSubsurface::~CSubsurface() {
+ hyprListener_newSubsurface.removeCallback();
+
+ if (!m_pSubsurface)
+ return;
+
+ hyprListener_commitSubsurface.removeCallback();
+ hyprListener_destroySubsurface.removeCallback();
+
+ const auto COORDS = coordsGlobal();
+
+ CBox box = {COORDS, m_vLastSize};
+ g_pHyprRenderer->damageBox(&box);
+}
+
+static void onNewSubsurface(void* owner, void* data) {
+ const auto PSUBSURFACE = (CSubsurface*)owner;
+ PSUBSURFACE->onNewSubsurface((wlr_subsurface*)data);
+}
+
+static void onDestroySubsurface(void* owner, void* data) {
+ const auto PSUBSURFACE = (CSubsurface*)owner;
+ PSUBSURFACE->onDestroy();
+}
+
+static void onCommitSubsurface(void* owner, void* data) {
+ const auto PSUBSURFACE = (CSubsurface*)owner;
+ PSUBSURFACE->onCommit();
+}
+
+static void onMapSubsurface(void* owner, void* data) {
+ const auto PSUBSURFACE = (CSubsurface*)owner;
+ PSUBSURFACE->onMap();
+}
+
+static void onUnmapSubsurface(void* owner, void* data) {
+ const auto PSUBSURFACE = (CSubsurface*)owner;
+ PSUBSURFACE->onUnmap();
+}
+
+void CSubsurface::initSignals() {
+ if (m_pSubsurface) {
+ hyprListener_commitSubsurface.initCallback(&m_pSubsurface->surface->events.commit, &onCommitSubsurface, this, "CSubsurface");
+ hyprListener_destroySubsurface.initCallback(&m_pSubsurface->events.destroy, &onDestroySubsurface, this, "CSubsurface");
+ hyprListener_newSubsurface.initCallback(&m_pSubsurface->surface->events.new_subsurface, &::onNewSubsurface, this, "CSubsurface");
+ hyprListener_mapSubsurface.initCallback(&m_pSubsurface->surface->events.map, &onMapSubsurface, this, "CSubsurface");
+ hyprListener_newSubsurface.initCallback(&m_pSubsurface->surface->events.unmap, &onUnmapSubsurface, this, "CSubsurface");
+ } else {
+ if (m_pWindowParent)
+ hyprListener_newSubsurface.initCallback(&m_pWindowParent->m_pWLSurface.wlr()->events.new_subsurface, &::onNewSubsurface, this, "CSubsurface");
+ else
+ RASSERT(false, "CSubsurface::initSignals empty subsurface");
+ }
+}
+
+void CSubsurface::checkSiblingDamage() {
+ if (!m_pParent)
+ return; // ??????????
+
+ const double SCALE = m_pWindowParent && m_pWindowParent->m_bIsX11 ? 1.0 / m_pWindowParent->m_fX11SurfaceScaledBy : 1.0;
+
+ for (auto& n : m_pParent->m_vChildren) {
+ if (n.get() == this)
+ continue;
+
+ const auto COORDS = n->coordsGlobal();
+ g_pHyprRenderer->damageSurface(n->m_sWLSurface.wlr(), COORDS.x, COORDS.y, SCALE);
+ }
+}
+
+void CSubsurface::recheckDamageForSubsurfaces() {
+ const double SCALE = m_pWindowParent && m_pWindowParent->m_bIsX11 ? 1.0 / m_pWindowParent->m_fX11SurfaceScaledBy : 1.0;
+
+ for (auto& n : m_vChildren) {
+ const auto COORDS = n->coordsGlobal();
+ g_pHyprRenderer->damageSurface(n->m_sWLSurface.wlr(), COORDS.x, COORDS.y, SCALE);
+ }
+}
+
+void CSubsurface::onCommit() {
+ // no damaging if it's not visible
+ if (!g_pHyprRenderer->shouldRenderWindow(m_pWindowParent)) {
+ m_vLastSize = Vector2D{m_sWLSurface.wlr()->current.width, m_sWLSurface.wlr()->current.height};
+
+ static auto* const PLOGDAMAGE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("debug:log_damage");
+ if (**PLOGDAMAGE)
+ Debug::log(LOG, "Refusing to commit damage from a subsurface of {} because it's invisible.", m_pWindowParent);
+ return;
+ }
+
+ const auto COORDS = coordsGlobal();
+
+ const double SCALE = m_pWindowParent && m_pWindowParent->m_bIsX11 ? 1.0 / m_pWindowParent->m_fX11SurfaceScaledBy : 1.0;
+
+ g_pHyprRenderer->damageSurface(m_sWLSurface.wlr(), COORDS.x, COORDS.y, SCALE);
+
+ // I do not think this is correct, but it solves a lot of issues with some apps (e.g. firefox)
+ checkSiblingDamage();
+
+ if (m_vLastSize != Vector2D{m_sWLSurface.wlr()->current.width, m_sWLSurface.wlr()->current.height}) {
+ CBox box{COORDS, m_vLastSize};
+ g_pHyprRenderer->damageBox(&box);
+ m_vLastSize = Vector2D{m_sWLSurface.wlr()->current.width, m_sWLSurface.wlr()->current.height};
+ box = {COORDS, m_vLastSize};
+ g_pHyprRenderer->damageBox(&box);
+ }
+
+ if (m_pWindowParent) {
+ if (m_pWindowParent->m_bIsX11)
+ m_pWindowParent->m_vReportedSize = m_pWindowParent->m_vPendingReportedSize; // apply pending size. We pinged, the window ponged.
+
+ // tearing: if solitary, redraw it. This still might be a single surface window
+ const auto PMONITOR = g_pCompositor->getMonitorFromID(m_pWindowParent->m_iMonitorID);
+ if (PMONITOR && PMONITOR->solitaryClient == m_pWindowParent && m_pWindowParent->canBeTorn() && PMONITOR->tearingState.canTear &&
+ m_sWLSurface.wlr()->current.committed & WLR_SURFACE_STATE_BUFFER) {
+ CRegion damageBox{&m_sWLSurface.wlr()->buffer_damage};
+
+ if (!damageBox.empty()) {
+ if (PMONITOR->tearingState.busy) {
+ PMONITOR->tearingState.frameScheduledWhileBusy = true;
+ } else {
+ PMONITOR->tearingState.nextRenderTorn = true;
+ g_pHyprRenderer->renderMonitor(PMONITOR);
+ }
+ }
+ }
+ }
+}
+
+void CSubsurface::onDestroy() {
+ // destroy children
+ m_vChildren.clear();
+
+ if (!m_pSubsurface)
+ return; // dummy node, nothing to do, it's the parent dying
+
+ // kill ourselves
+ std::erase_if(m_pParent->m_vChildren, [this](const auto& other) { return other.get() == this; });
+}
+
+void CSubsurface::onNewSubsurface(wlr_subsurface* pSubsurface) {
+ CSubsurface* PSUBSURFACE = nullptr;
+
+ if (m_pWindowParent)
+ PSUBSURFACE = m_vChildren.emplace_back(std::make_unique<CSubsurface>(pSubsurface, m_pWindowParent)).get();
+ PSUBSURFACE->m_pParent = this;
+
+ ASSERT(PSUBSURFACE);
+}
+
+void CSubsurface::onMap() {
+ m_vLastSize = {m_sWLSurface.wlr()->current.width, m_sWLSurface.wlr()->current.height};
+
+ const auto COORDS = coordsGlobal();
+ CBox box{COORDS, m_vLastSize};
+ g_pHyprRenderer->damageBox(&box);
+
+ if (m_pWindowParent)
+ m_pWindowParent->updateSurfaceScaleTransformDetails();
+}
+
+void CSubsurface::onUnmap() {
+ const auto COORDS = coordsGlobal();
+ CBox box{COORDS, m_vLastSize};
+ g_pHyprRenderer->damageBox(&box);
+
+ if (m_sWLSurface.wlr() == g_pCompositor->m_pLastFocus)
+ g_pInputManager->releaseAllMouseButtons();
+
+ if (m_pWindowParent)
+ m_pWindowParent->updateSurfaceScaleTransformDetails();
+
+ g_pInputManager->simulateMouseMovement();
+
+ // TODO: should this remove children? Currently it won't, only on .destroy
+}
+
+Vector2D CSubsurface::coordsRelativeToParent() {
+ Vector2D offset;
+
+ CSubsurface* current = this;
+
+ while (current->m_pParent) {
+
+ offset += {current->m_sWLSurface.wlr()->current.dx, current->m_sWLSurface.wlr()->current.dy};
+ offset += {current->m_pSubsurface->current.x, current->m_pSubsurface->current.y};
+
+ current = current->m_pParent;
+ }
+
+ return offset;
+}
+
+Vector2D CSubsurface::coordsGlobal() {
+ Vector2D coords = coordsRelativeToParent();
+
+ if (m_pWindowParent)
+ coords += m_pWindowParent->m_vRealPosition.vec();
+
+ return coords;
+}
+
+void CSubsurface::initExistingSubsurfaces() {
+ if (m_pWindowParent)
+ return;
+
+ wlr_subsurface* wlrSubsurface;
+ wl_list_for_each(wlrSubsurface, &m_sWLSurface.wlr()->current.subsurfaces_below, current.link) {
+ ::onNewSubsurface(this, wlrSubsurface);
+ }
+ wl_list_for_each(wlrSubsurface, &m_sWLSurface.wlr()->current.subsurfaces_above, current.link) {
+ ::onNewSubsurface(this, wlrSubsurface);
+ }
+} \ No newline at end of file