#include "Subcompositor.hpp" #include "Compositor.hpp" #include CWLSubsurfaceResource::CWLSubsurfaceResource(SP resource_, SP surface_, SP parent_) : m_surface(surface_), m_parent(parent_), m_resource(resource_) { if UNLIKELY (!good()) return; m_resource->setOnDestroy([this](CWlSubsurface* r) { destroy(); }); m_resource->setDestroy([this](CWlSubsurface* r) { destroy(); }); m_resource->setSetPosition([this](CWlSubsurface* r, int32_t x, int32_t y) { m_position = {x, y}; }); m_resource->setSetDesync([this](CWlSubsurface* r) { m_sync = false; }); m_resource->setSetSync([this](CWlSubsurface* r) { m_sync = true; }); m_resource->setPlaceAbove([this](CWlSubsurface* r, wl_resource* surf) { auto SURF = CWLSurfaceResource::fromResource(surf); if (!m_parent) return; auto pushAboveIndex = [this](int idx) -> void { for (auto const& c : m_parent->m_subsurfaces) { if (c->m_zIndex >= idx) c->m_zIndex++; } }; std::erase_if(m_parent->m_subsurfaces, [this](const auto& e) { return e == m_self || !e; }); auto it = std::find(m_parent->m_subsurfaces.begin(), m_parent->m_subsurfaces.end(), SURF); if (it == m_parent->m_subsurfaces.end()) { LOGM(ERR, "Invalid surface reference in placeAbove, likely parent"); pushAboveIndex(1); m_parent->m_subsurfaces.emplace_back(m_self); m_zIndex = 1; } else { pushAboveIndex((*it)->m_zIndex); m_zIndex = (*it)->m_zIndex; m_parent->m_subsurfaces.emplace_back(m_self); } std::sort(m_parent->m_subsurfaces.begin(), m_parent->m_subsurfaces.end(), [](const auto& a, const auto& b) { return a->m_zIndex < b->m_zIndex; }); }); m_resource->setPlaceBelow([this](CWlSubsurface* r, wl_resource* surf) { auto SURF = CWLSurfaceResource::fromResource(surf); if (!m_parent) return; auto pushBelowIndex = [this](int idx) -> void { for (auto const& c : m_parent->m_subsurfaces) { if (c->m_zIndex <= idx) c->m_zIndex--; } }; std::erase_if(m_parent->m_subsurfaces, [this](const auto& e) { return e == m_self || !e; }); auto it = std::find(m_parent->m_subsurfaces.begin(), m_parent->m_subsurfaces.end(), SURF); if (it == m_parent->m_subsurfaces.end()) { LOGM(ERR, "Invalid surface reference in placeBelow, likely parent"); pushBelowIndex(-1); m_parent->m_subsurfaces.emplace_back(m_self); m_zIndex = -1; } else { pushBelowIndex((*it)->m_zIndex); m_zIndex = (*it)->m_zIndex; m_parent->m_subsurfaces.emplace_back(m_self); } std::sort(m_parent->m_subsurfaces.begin(), m_parent->m_subsurfaces.end(), [](const auto& a, const auto& b) { return a->m_zIndex < b->m_zIndex; }); }); m_listeners.commitSurface = m_surface->m_events.commit.registerListener([this](std::any d) { if (m_surface->m_current.texture && !m_surface->m_mapped) { m_surface->map(); m_surface->m_events.map.emit(); return; } if (!m_surface->m_current.texture && m_surface->m_mapped) { m_surface->m_events.unmap.emit(); m_surface->unmap(); return; } }); } CWLSubsurfaceResource::~CWLSubsurfaceResource() { m_events.destroy.emit(); if (m_surface) m_surface->resetRole(); } void CWLSubsurfaceResource::destroy() { if (m_surface && m_surface->m_mapped) { m_surface->m_events.unmap.emit(); m_surface->unmap(); } m_events.destroy.emit(); PROTO::subcompositor->destroyResource(this); } Vector2D CWLSubsurfaceResource::posRelativeToParent() { Vector2D pos = m_position; SP surf = m_parent.lock(); // some apps might create cycles, which I believe _technically_ are not a protocol error // in some cases, notably firefox likes to do that, so we keep track of what // surfaces we've visited and if we hit a surface we've visited we bail out. std::vector> surfacesVisited; while (surf->m_role->role() == SURFACE_ROLE_SUBSURFACE && std::find_if(surfacesVisited.begin(), surfacesVisited.end(), [surf](const auto& other) { return surf == other; }) == surfacesVisited.end()) { surfacesVisited.emplace_back(surf); auto subsurface = ((CSubsurfaceRole*)m_parent->m_role.get())->m_subsurface.lock(); pos += subsurface->m_position; surf = subsurface->m_parent.lock(); } return pos; } bool CWLSubsurfaceResource::good() { return m_resource->resource(); } SP CWLSubsurfaceResource::t1Parent() { SP surf = m_parent.lock(); std::vector> surfacesVisited; while (surf->m_role->role() == SURFACE_ROLE_SUBSURFACE && std::find_if(surfacesVisited.begin(), surfacesVisited.end(), [surf](const auto& other) { return surf == other; }) == surfacesVisited.end()) { surfacesVisited.emplace_back(surf); auto subsurface = ((CSubsurfaceRole*)m_parent->m_role.get())->m_subsurface.lock(); surf = subsurface->m_parent.lock(); } return surf; } CWLSubcompositorResource::CWLSubcompositorResource(SP resource_) : m_resource(resource_) { if UNLIKELY (!good()) return; m_resource->setOnDestroy([this](CWlSubcompositor* r) { PROTO::subcompositor->destroyResource(this); }); m_resource->setDestroy([this](CWlSubcompositor* r) { PROTO::subcompositor->destroyResource(this); }); m_resource->setGetSubsurface([](CWlSubcompositor* r, uint32_t id, wl_resource* surface, wl_resource* parent) { auto SURF = CWLSurfaceResource::fromResource(surface); auto PARENT = CWLSurfaceResource::fromResource(parent); if UNLIKELY (!SURF || !PARENT || SURF == PARENT) { r->error(WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "Invalid surface/parent"); return; } if UNLIKELY (SURF->m_role->role() != SURFACE_ROLE_UNASSIGNED) { r->error(-1, "Surface already has a different role"); return; } SP t1Parent = nullptr; if (PARENT->m_role->role() == SURFACE_ROLE_SUBSURFACE) { auto subsurface = ((CSubsurfaceRole*)PARENT->m_role.get())->m_subsurface.lock(); t1Parent = subsurface->t1Parent(); } else t1Parent = PARENT; if UNLIKELY (t1Parent == SURF) { r->error(WL_SUBCOMPOSITOR_ERROR_BAD_PARENT, "Bad parent, t1 parent == surf"); return; } const auto RESOURCE = PROTO::subcompositor->m_surfaces.emplace_back(makeShared(makeShared(r->client(), r->version(), id), SURF, PARENT)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); PROTO::subcompositor->m_surfaces.pop_back(); return; } RESOURCE->m_self = RESOURCE; SURF->m_role = makeShared(RESOURCE); PARENT->m_subsurfaces.emplace_back(RESOURCE); LOGM(LOG, "New wl_subsurface with id {} at {:x}", id, (uintptr_t)RESOURCE.get()); PARENT->m_events.newSubsurface.emit(RESOURCE); }); } bool CWLSubcompositorResource::good() { return m_resource->resource(); } CWLSubcompositorProtocol::CWLSubcompositorProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { ; } void CWLSubcompositorProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { const auto RESOURCE = m_managers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); m_managers.pop_back(); return; } } void CWLSubcompositorProtocol::destroyResource(CWLSubcompositorResource* resource) { std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; }); } void CWLSubcompositorProtocol::destroyResource(CWLSubsurfaceResource* resource) { std::erase_if(m_surfaces, [&](const auto& other) { return other.get() == resource; }); } CSubsurfaceRole::CSubsurfaceRole(SP sub) : m_subsurface(sub) { ; }