From 920353370bba555010506a1c0b204675c60362fe Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Mon, 8 Dec 2025 15:04:40 +0000 Subject: desktop: cleanup, unify desktop elements as views (#12563) --- src/desktop/view/LayerSurface.cpp | 462 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 462 insertions(+) create mode 100644 src/desktop/view/LayerSurface.cpp (limited to 'src/desktop/view/LayerSurface.cpp') diff --git a/src/desktop/view/LayerSurface.cpp b/src/desktop/view/LayerSurface.cpp new file mode 100644 index 00000000..3192321e --- /dev/null +++ b/src/desktop/view/LayerSurface.cpp @@ -0,0 +1,462 @@ +#include "LayerSurface.hpp" +#include "../state/FocusState.hpp" +#include "../../Compositor.hpp" +#include "../../protocols/LayerShell.hpp" +#include "../../protocols/core/Compositor.hpp" +#include "../../managers/SeatManager.hpp" +#include "../../managers/animation/AnimationManager.hpp" +#include "../../managers/animation/DesktopAnimationManager.hpp" +#include "../../render/Renderer.hpp" +#include "../../config/ConfigManager.hpp" +#include "../../helpers/Monitor.hpp" +#include "../../managers/input/InputManager.hpp" +#include "../../managers/HookSystemManager.hpp" +#include "../../managers/EventManager.hpp" + +using namespace Desktop; +using namespace Desktop::View; + +PHLLS CLayerSurface::create(SP resource) { + PHLLS pLS = SP(new CLayerSurface(resource)); + + auto pMonitor = resource->m_monitor.empty() ? Desktop::focusState()->monitor() : g_pCompositor->getMonitorFromName(resource->m_monitor); + + pLS->m_wlSurface->assign(resource->m_surface.lock(), pLS); + + if (!pMonitor) { + Debug::log(ERR, "New LS has no monitor??"); + return pLS; + } + + if (pMonitor->m_mirrorOf) + pMonitor = g_pCompositor->m_monitors.front(); + + pLS->m_self = pLS; + + pLS->m_namespace = resource->m_layerNamespace; + + pLS->m_layer = resource->m_current.layer; + pLS->m_popupHead = CPopup::create(pLS); + pLS->m_monitor = pMonitor; + pMonitor->m_layerSurfaceLayers[resource->m_current.layer].emplace_back(pLS); + + pLS->m_ruleApplicator = makeUnique(pLS); + + g_pAnimationManager->createAnimation(0.f, pLS->m_alpha, g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn"), pLS, AVARDAMAGE_ENTIRE); + g_pAnimationManager->createAnimation(Vector2D(0, 0), pLS->m_realPosition, g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE); + g_pAnimationManager->createAnimation(Vector2D(0, 0), pLS->m_realSize, g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE); + + pLS->registerCallbacks(); + + pLS->m_alpha->setValueAndWarp(0.f); + + Debug::log(LOG, "LayerSurface {:x} (namespace {} layer {}) created on monitor {}", rc(resource.get()), resource->m_layerNamespace, sc(pLS->m_layer), + pMonitor->m_name); + + return pLS; +} + +PHLLS CLayerSurface::fromView(SP v) { + if (!v || v->type() != VIEW_TYPE_LAYER_SURFACE) + return nullptr; + return dynamicPointerCast(v); +} + +void CLayerSurface::registerCallbacks() { + m_alpha->setUpdateCallback([this](auto) { + if (m_ruleApplicator->dimAround().valueOrDefault() && m_monitor) + g_pHyprRenderer->damageMonitor(m_monitor.lock()); + }); +} + +CLayerSurface::CLayerSurface(SP resource_) : IView(CWLSurface::create()), m_layerSurface(resource_) { + m_listeners.commit = m_layerSurface->m_events.commit.listen([this] { onCommit(); }); + m_listeners.map = m_layerSurface->m_events.map.listen([this] { onMap(); }); + m_listeners.unmap = m_layerSurface->m_events.unmap.listen([this] { onUnmap(); }); + m_listeners.destroy = m_layerSurface->m_events.destroy.listen([this] { onDestroy(); }); +} + +CLayerSurface::~CLayerSurface() { + if (!g_pHyprOpenGL) + return; + + if (m_wlSurface) + m_wlSurface->unassign(); + g_pHyprRenderer->makeEGLCurrent(); + std::erase_if(g_pHyprOpenGL->m_layerFramebuffers, [&](const auto& other) { return other.first.expired() || other.first.lock() == m_self.lock(); }); + + for (auto const& mon : g_pCompositor->m_realMonitors) { + for (auto& lsl : mon->m_layerSurfaceLayers) { + std::erase_if(lsl, [this](auto& ls) { return ls.expired() || ls.get() == this; }); + } + } +} + +eViewType CLayerSurface::type() const { + return VIEW_TYPE_LAYER_SURFACE; +} + +bool CLayerSurface::visible() const { + return (m_mapped && m_layerSurface && m_layerSurface->m_mapped && m_wlSurface && m_wlSurface->resource()) || (m_fadingOut && m_alpha->value() > 0.F); +} + +std::optional CLayerSurface::logicalBox() const { + return surfaceLogicalBox(); +} + +std::optional CLayerSurface::surfaceLogicalBox() const { + if (!visible()) + return std::nullopt; + + return CBox{m_realPosition->value(), m_realSize->value()}; +} + +bool CLayerSurface::desktopComponent() const { + return true; +} + +void CLayerSurface::onDestroy() { + Debug::log(LOG, "LayerSurface {:x} destroyed", rc(m_layerSurface.get())); + + const auto PMONITOR = m_monitor.lock(); + + if (!PMONITOR) + Debug::log(WARN, "Layersurface destroyed on an invalid monitor (removed?)"); + + if (!m_fadingOut) { + if (m_mapped) { + Debug::log(LOG, "Forcing an unmap of a LS that did a straight destroy!"); + onUnmap(); + } else { + Debug::log(LOG, "Removing LayerSurface that wasn't mapped."); + if (m_alpha) + g_pDesktopAnimationManager->startAnimation(m_self.lock(), CDesktopAnimationManager::ANIMATION_TYPE_OUT); + m_fadingOut = true; + g_pCompositor->addToFadingOutSafe(m_self.lock()); + } + } + + m_popupHead.reset(); + + m_noProcess = true; + + // rearrange to fix the reserved areas + if (PMONITOR) { + g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id); + PMONITOR->m_scheduledRecalc = true; + + // and damage + CBox geomFixed = {m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y, m_geometry.width, m_geometry.height}; + g_pHyprRenderer->damageBox(geomFixed); + } + + m_readyToDelete = true; + m_layerSurface.reset(); + if (m_wlSurface) + m_wlSurface->unassign(); + + m_listeners.unmap.reset(); + m_listeners.destroy.reset(); + m_listeners.map.reset(); + m_listeners.commit.reset(); +} + +void CLayerSurface::onMap() { + Debug::log(LOG, "LayerSurface {:x} mapped", rc(m_layerSurface.get())); + + m_mapped = true; + m_interactivity = m_layerSurface->m_current.interactivity; + + m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ALL); + + m_layerSurface->m_surface->map(); + + // this layer might be re-mapped. + m_fadingOut = false; + g_pCompositor->removeFromFadingOutSafe(m_self.lock()); + + // fix if it changed its mon + const auto PMONITOR = m_monitor.lock(); + + if (!PMONITOR) + return; + + PMONITOR->m_scheduledRecalc = true; + + g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id); + + m_wlSurface->resource()->enter(PMONITOR->m_self.lock()); + + const bool ISEXCLUSIVE = m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE; + + if (ISEXCLUSIVE) + g_pInputManager->m_exclusiveLSes.push_back(m_self); + + const bool GRABSFOCUS = ISEXCLUSIVE || + (m_layerSurface->m_current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE && + // don't focus if constrained + (g_pSeatManager->m_mouse.expired() || !g_pInputManager->isConstrained())); + + if (GRABSFOCUS) { + // TODO: use the new superb really very cool grab + if (g_pSeatManager->m_seatGrab && !g_pSeatManager->m_seatGrab->accepts(m_wlSurface->resource())) + g_pSeatManager->setGrab(nullptr); + + g_pInputManager->releaseAllMouseButtons(); + Desktop::focusState()->rawSurfaceFocus(m_wlSurface->resource()); + + const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y); + g_pSeatManager->setPointerFocus(m_wlSurface->resource(), LOCAL); + g_pInputManager->m_emptyFocusCursorSet = false; + } + + m_position = Vector2D(m_geometry.x, m_geometry.y); + + CBox geomFixed = {m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y, m_geometry.width, m_geometry.height}; + g_pHyprRenderer->damageBox(geomFixed); + + g_pDesktopAnimationManager->startAnimation(m_self.lock(), CDesktopAnimationManager::ANIMATION_TYPE_IN); + + m_readyToDelete = false; + m_fadingOut = false; + + g_pEventManager->postEvent(SHyprIPCEvent{.event = "openlayer", .data = m_namespace}); + EMIT_HOOK_EVENT("openLayer", m_self.lock()); + + g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), PMONITOR->m_scale); + g_pCompositor->setPreferredTransformForSurface(m_wlSurface->resource(), PMONITOR->m_transform); +} + +void CLayerSurface::onUnmap() { + Debug::log(LOG, "LayerSurface {:x} unmapped", rc(m_layerSurface.get())); + + g_pEventManager->postEvent(SHyprIPCEvent{.event = "closelayer", .data = m_layerSurface->m_layerNamespace}); + EMIT_HOOK_EVENT("closeLayer", m_self.lock()); + + std::erase_if(g_pInputManager->m_exclusiveLSes, [this](const auto& other) { return !other || other == m_self; }); + + if (!m_monitor || g_pCompositor->m_unsafeState) { + Debug::log(WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring."); + + g_pCompositor->addToFadingOutSafe(m_self.lock()); + + m_mapped = false; + if (m_layerSurface && m_layerSurface->m_surface) + m_layerSurface->m_surface->unmap(); + + g_pDesktopAnimationManager->startAnimation(m_self.lock(), CDesktopAnimationManager::ANIMATION_TYPE_OUT); + return; + } + + // end any pending animations so that snapshot has right dimensions + m_realPosition->warp(); + m_realSize->warp(); + + // make a snapshot and start fade + g_pHyprRenderer->makeSnapshot(m_self.lock()); + + g_pDesktopAnimationManager->startAnimation(m_self.lock(), CDesktopAnimationManager::ANIMATION_TYPE_OUT); + + m_fadingOut = true; + + m_mapped = false; + if (m_layerSurface && m_layerSurface->m_surface) + m_layerSurface->m_surface->unmap(); + + g_pCompositor->addToFadingOutSafe(m_self.lock()); + + const auto PMONITOR = m_monitor.lock(); + + const bool WASLASTFOCUS = g_pSeatManager->m_state.keyboardFocus == m_wlSurface->resource() || g_pSeatManager->m_state.pointerFocus == m_wlSurface->resource(); + + if (!PMONITOR) + return; + + // refocus if needed + // vvvvvvvvvvvvv if there is a last focus and the last focus is not keyboard focusable, fallback to window + if (WASLASTFOCUS || + (Desktop::focusState()->surface() && Desktop::focusState()->surface()->m_hlSurface && !Desktop::focusState()->surface()->m_hlSurface->keyboardFocusable())) { + if (!g_pInputManager->refocusLastWindow(PMONITOR)) + g_pInputManager->refocus(); + } else if (Desktop::focusState()->surface() && Desktop::focusState()->surface() != m_wlSurface->resource()) + g_pSeatManager->setKeyboardFocus(Desktop::focusState()->surface()); + + CBox geomFixed = {m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y, m_geometry.width, m_geometry.height}; + g_pHyprRenderer->damageBox(geomFixed); + + geomFixed = {m_geometry.x + sc(PMONITOR->m_position.x), m_geometry.y + sc(PMONITOR->m_position.y), sc(m_layerSurface->m_surface->m_current.size.x), + sc(m_layerSurface->m_surface->m_current.size.y)}; + g_pHyprRenderer->damageBox(geomFixed); + + g_pInputManager->simulateMouseMovement(); + + g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id); +} + +void CLayerSurface::onCommit() { + if (!m_layerSurface) + return; + + if (!m_mapped) { + // we're re-mapping if this is the case + if (m_layerSurface->m_surface && !m_layerSurface->m_surface->m_current.texture) { + m_fadingOut = false; + m_geometry = {}; + g_pHyprRenderer->arrangeLayersForMonitor(monitorID()); + } + + return; + } + + const auto PMONITOR = m_monitor.lock(); + + if (!PMONITOR) + return; + + if (m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) + g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); // so that blur is recalc'd + + CBox geomFixed = {m_geometry.x, m_geometry.y, m_geometry.width, m_geometry.height}; + g_pHyprRenderer->damageBox(geomFixed); + + if (m_layerSurface->m_current.committed != 0) { + if (m_layerSurface->m_current.committed & CLayerShellResource::eCommittedState::STATE_LAYER && m_layerSurface->m_current.layer != m_layer) { + + for (auto it = PMONITOR->m_layerSurfaceLayers[m_layer].begin(); it != PMONITOR->m_layerSurfaceLayers[m_layer].end(); it++) { + if (*it == m_self) { + PMONITOR->m_layerSurfaceLayers[m_layerSurface->m_current.layer].emplace_back(*it); + PMONITOR->m_layerSurfaceLayers[m_layer].erase(it); + break; + } + } + + // update alpha when window is in fullscreen + auto PWORKSPACE = PMONITOR->m_activeSpecialWorkspace ? PMONITOR->m_activeSpecialWorkspace : PMONITOR->m_activeWorkspace; + if (PWORKSPACE && PWORKSPACE->m_fullscreenMode == FSMODE_FULLSCREEN) { + // warp if switching render layer so we don't see glitches and have clean fade + if ((m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) && + (m_layerSurface->m_current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP || m_layerSurface->m_current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY)) + m_alpha->setValueAndWarp(0.f); + + // from overlay to top + if (m_layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY && m_layerSurface->m_current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP) + *m_alpha = 0.f; + // to overlay + if (m_layerSurface->m_current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) + *m_alpha = 1.f; + } + + m_layer = m_layerSurface->m_current.layer; + + if (m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) + g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); // so that blur is recalc'd + } + + g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id); + + PMONITOR->m_scheduledRecalc = true; + } else { + m_position = Vector2D(m_geometry.x, m_geometry.y); + + // update geom if it changed + if (m_layerSurface->m_surface->m_current.scale == 1 && PMONITOR->m_scale != 1.f && m_layerSurface->m_surface->m_current.viewport.hasDestination) { + // fractional scaling. Dirty hack. + m_geometry = {m_geometry.pos(), m_layerSurface->m_surface->m_current.viewport.destination}; + } else { + // this is because some apps like e.g. rofi-lbonn can't fucking use the protocol correctly. + m_geometry = {m_geometry.pos(), m_layerSurface->m_surface->m_current.size}; + } + } + + if (m_realPosition->goal() != m_geometry.pos()) { + if (m_realPosition->isBeingAnimated()) + *m_realPosition = m_geometry.pos(); + else + m_realPosition->setValueAndWarp(m_geometry.pos()); + } + if (m_realSize->goal() != m_geometry.size()) { + if (m_realSize->isBeingAnimated()) + *m_realSize = m_geometry.size(); + else + m_realSize->setValueAndWarp(m_geometry.size()); + } + + if (m_mapped && (m_layerSurface->m_current.committed & CLayerShellResource::eCommittedState::STATE_INTERACTIVITY)) { + bool WASLASTFOCUS = false; + m_layerSurface->m_surface->breadthfirst( + [&WASLASTFOCUS](SP surf, const Vector2D& offset, void* data) { WASLASTFOCUS = WASLASTFOCUS || g_pSeatManager->m_state.keyboardFocus == surf; }, + nullptr); + if (!WASLASTFOCUS && m_popupHead) { + m_popupHead->breadthfirst( + [&WASLASTFOCUS](WP popup, void* data) { + WASLASTFOCUS = WASLASTFOCUS || (popup->wlSurface() && g_pSeatManager->m_state.keyboardFocus == popup->wlSurface()->resource()); + }, + nullptr); + } + const bool WASEXCLUSIVE = m_interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE; + const bool ISEXCLUSIVE = m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE; + + if (!WASEXCLUSIVE && ISEXCLUSIVE) + g_pInputManager->m_exclusiveLSes.push_back(m_self); + else if (WASEXCLUSIVE && !ISEXCLUSIVE) + std::erase_if(g_pInputManager->m_exclusiveLSes, [this](const auto& other) { return !other.lock() || other.lock() == m_self.lock(); }); + + // if the surface was focused and interactive but now isn't, refocus + if (WASLASTFOCUS && m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE) { + // moveMouseUnified won't focus non interactive layers but it won't unfocus them either, + // so unfocus the surface here. + Desktop::focusState()->rawSurfaceFocus(nullptr); + g_pInputManager->refocusLastWindow(m_monitor.lock()); + } else if (WASLASTFOCUS && WASEXCLUSIVE && m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) { + g_pInputManager->simulateMouseMovement(); + } else if (!WASEXCLUSIVE && ISEXCLUSIVE) { + // if now exclusive and not previously + g_pSeatManager->setGrab(nullptr); + g_pInputManager->releaseAllMouseButtons(); + Desktop::focusState()->rawSurfaceFocus(m_wlSurface->resource()); + + const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y); + g_pSeatManager->setPointerFocus(m_wlSurface->resource(), LOCAL); + g_pInputManager->m_emptyFocusCursorSet = false; + } + } + + m_interactivity = m_layerSurface->m_current.interactivity; + + g_pHyprRenderer->damageSurface(m_wlSurface->resource(), m_position.x, m_position.y); + + g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), PMONITOR->m_scale); + g_pCompositor->setPreferredTransformForSurface(m_wlSurface->resource(), PMONITOR->m_transform); +} + +bool CLayerSurface::isFadedOut() { + if (!m_fadingOut) + return false; + + return !m_realPosition->isBeingAnimated() && !m_realSize->isBeingAnimated() && !m_alpha->isBeingAnimated(); +} + +int CLayerSurface::popupsCount() { + if (!m_layerSurface || !m_mapped || m_fadingOut) + return 0; + + int no = -1; // we have one dummy + m_popupHead->breadthfirst([](WP p, void* data) { *sc(data) += 1; }, &no); + return no; +} + +MONITORID CLayerSurface::monitorID() { + return m_monitor ? m_monitor->m_id : MONITOR_INVALID; +} + +pid_t CLayerSurface::getPID() { + pid_t PID = -1; + + if (!m_layerSurface || !m_layerSurface->m_surface || !m_layerSurface->m_surface->getResource() || !m_layerSurface->m_surface->getResource()->resource() || + !m_layerSurface->m_surface->getResource()->client()) + return -1; + + wl_client_get_credentials(m_layerSurface->m_surface->getResource()->client(), &PID, nullptr, nullptr); + + return PID; +} -- cgit v1.2.3