From 4cf62c114e8fd2cc4fa47d07459e00c52f6dc1d3 Mon Sep 17 00:00:00 2001
From: Virt <41426325+VirtCode@users.noreply.github.com>
Date: Fri, 25 Apr 2025 16:38:31 +0200
Subject: [PATCH] layerrules: add abovelock to render above lockscreen (#9793)

---
 src/Compositor.cpp                  |  7 +++++--
 src/Compositor.hpp                  |  2 +-
 src/desktop/LayerRule.cpp           |  6 ++++--
 src/desktop/LayerRule.hpp           |  3 ++-
 src/desktop/LayerSurface.cpp        | 13 ++++++++++---
 src/desktop/LayerSurface.hpp        | 16 +++++++++-------
 src/managers/input/InputManager.cpp | 27 +++++++++++++++++++++------
 src/render/Renderer.cpp             | 19 ++++++++++++++++++-
 src/render/Renderer.hpp             |  2 +-
 9 files changed, 71 insertions(+), 24 deletions(-)

diff --git a/src/Compositor.cpp b/src/Compositor.cpp
index d4e5bc435..a32ba056f 100644
--- a/src/Compositor.cpp
+++ b/src/Compositor.cpp
@@ -1286,9 +1286,12 @@ SP<CWLSurfaceResource> CCompositor::vectorToLayerPopupSurface(const Vector2D& po
     return nullptr;
 }
 
-SP<CWLSurfaceResource> CCompositor::vectorToLayerSurface(const Vector2D& pos, std::vector<PHLLSREF>* layerSurfaces, Vector2D* sCoords, PHLLS* ppLayerSurfaceFound) {
+SP<CWLSurfaceResource> CCompositor::vectorToLayerSurface(const Vector2D& pos, std::vector<PHLLSREF>* layerSurfaces, Vector2D* sCoords, PHLLS* ppLayerSurfaceFound,
+                                                         bool aboveLockscreen) {
+
     for (auto const& ls : *layerSurfaces | std::views::reverse) {
-        if (!ls->m_mapped || ls->m_fadingOut || !ls->m_layerSurface || (ls->m_layerSurface && !ls->m_layerSurface->surface->mapped) || ls->m_alpha->value() == 0.f)
+        if (!ls->m_mapped || ls->m_fadingOut || !ls->m_layerSurface || (ls->m_layerSurface && !ls->m_layerSurface->surface->mapped) || ls->m_alpha->value() == 0.f ||
+            (aboveLockscreen && (!ls->m_aboveLockscreen || !ls->m_aboveLockscreenInteractable)))
             continue;
 
         auto [surf, local] = ls->m_layerSurface->surface->at(pos - ls->m_geometry.pos(), true);
diff --git a/src/Compositor.hpp b/src/Compositor.hpp
index 32e652860..7c81c7779 100644
--- a/src/Compositor.hpp
+++ b/src/Compositor.hpp
@@ -85,7 +85,7 @@ class CCompositor {
     void                   focusSurface(SP<CWLSurfaceResource>, PHLWINDOW pWindowOwner = nullptr);
     bool                   monitorExists(PHLMONITOR);
     PHLWINDOW              vectorToWindowUnified(const Vector2D&, uint8_t properties, PHLWINDOW pIgnoreWindow = nullptr);
-    SP<CWLSurfaceResource> vectorToLayerSurface(const Vector2D&, std::vector<PHLLSREF>*, Vector2D*, PHLLS*);
+    SP<CWLSurfaceResource> vectorToLayerSurface(const Vector2D&, std::vector<PHLLSREF>*, Vector2D*, PHLLS*, bool aboveLockscreen = false);
     SP<CWLSurfaceResource> vectorToLayerPopupSurface(const Vector2D&, PHLMONITOR monitor, Vector2D*, PHLLS*);
     SP<CWLSurfaceResource> vectorWindowToSurface(const Vector2D&, PHLWINDOW, Vector2D& sl);
     Vector2D               vectorToSurfaceLocal(const Vector2D&, PHLWINDOW, SP<CWLSurfaceResource>);
diff --git a/src/desktop/LayerRule.cpp b/src/desktop/LayerRule.cpp
index c03d89685..9b5f9da24 100644
--- a/src/desktop/LayerRule.cpp
+++ b/src/desktop/LayerRule.cpp
@@ -5,7 +5,7 @@
 #include "../debug/Log.hpp"
 
 static const auto RULES        = std::unordered_set<std::string>{"noanim", "blur", "blurpopups", "dimaround"};
-static const auto RULES_PREFIX = std::unordered_set<std::string>{"ignorealpha", "ignorezero", "xray", "animation", "order"};
+static const auto RULES_PREFIX = std::unordered_set<std::string>{"ignorealpha", "ignorezero", "xray", "animation", "order", "abovelock"};
 
 CLayerRule::CLayerRule(const std::string& rule_, const std::string& ns_) : m_targetNamespace(ns_), m_rule(rule_) {
     const bool VALID = RULES.contains(m_rule) || std::any_of(RULES_PREFIX.begin(), RULES_PREFIX.end(), [&rule_](const auto& prefix) { return rule_.starts_with(prefix); });
@@ -31,8 +31,10 @@ CLayerRule::CLayerRule(const std::string& rule_, const std::string& ns_) : m_tar
         m_ruleType = RULE_ANIMATION;
     else if (m_rule.starts_with("order"))
         m_ruleType = RULE_ORDER;
+    else if (m_rule.starts_with("abovelock"))
+        m_ruleType = RULE_ABOVELOCK;
     else {
         Debug::log(ERR, "CLayerRule: didn't match a rule that was found valid?!");
         m_ruleType = RULE_INVALID;
     }
-}
\ No newline at end of file
+}
diff --git a/src/desktop/LayerRule.hpp b/src/desktop/LayerRule.hpp
index 46f843a62..0463196e2 100644
--- a/src/desktop/LayerRule.hpp
+++ b/src/desktop/LayerRule.hpp
@@ -14,6 +14,7 @@ class CLayerRule {
         RULE_BLUR,
         RULE_BLURPOPUPS,
         RULE_DIMAROUND,
+        RULE_ABOVELOCK,
         RULE_IGNOREALPHA,
         RULE_IGNOREZERO,
         RULE_XRAY,
@@ -28,4 +29,4 @@ class CLayerRule {
     const std::string   m_rule;
 
     CRuleRegexContainer m_targetNamespaceRegex;
-};
\ No newline at end of file
+};
diff --git a/src/desktop/LayerSurface.cpp b/src/desktop/LayerSurface.cpp
index b44f652b4..96c6200ed 100644
--- a/src/desktop/LayerSurface.cpp
+++ b/src/desktop/LayerSurface.cpp
@@ -421,9 +421,8 @@ void CLayerSurface::applyRules() {
             }
             case CLayerRule::RULE_XRAY: {
                 CVarList vars{rule->m_rule, 0, ' '};
-                try {
-                    m_xray = configStringToInt(vars[1]).value_or(false);
-                } catch (...) {}
+                m_xray = configStringToInt(vars[1]).value_or(false);
+
                 break;
             }
             case CLayerRule::RULE_ANIMATION: {
@@ -438,6 +437,14 @@ void CLayerSurface::applyRules() {
                 } catch (...) { Debug::log(ERR, "Invalid value passed to order"); }
                 break;
             }
+            case CLayerRule::RULE_ABOVELOCK: {
+                m_aboveLockscreen = true;
+
+                CVarList vars{rule->m_rule, 0, ' '};
+                m_aboveLockscreenInteractable = configStringToInt(vars[1]).value_or(false);
+
+                break;
+            }
             default: break;
         }
     }
diff --git a/src/desktop/LayerSurface.hpp b/src/desktop/LayerSurface.hpp
index 6282d55f1..b80d88de0 100644
--- a/src/desktop/LayerSurface.hpp
+++ b/src/desktop/LayerSurface.hpp
@@ -43,13 +43,15 @@ class CLayerSurface {
     bool                       m_noProcess     = false;
     bool                       m_noAnimations  = false;
 
-    bool                       m_forceBlur        = false;
-    bool                       m_forceBlurPopups  = false;
-    int64_t                    m_xray             = -1;
-    bool                       m_ignoreAlpha      = false;
-    float                      m_ignoreAlphaValue = 0.f;
-    bool                       m_dimAround        = false;
-    int64_t                    m_order            = 0;
+    bool                       m_forceBlur                   = false;
+    bool                       m_forceBlurPopups             = false;
+    int64_t                    m_xray                        = -1;
+    bool                       m_ignoreAlpha                 = false;
+    float                      m_ignoreAlphaValue            = 0.f;
+    bool                       m_dimAround                   = false;
+    int64_t                    m_order                       = 0;
+    bool                       m_aboveLockscreen             = false;
+    bool                       m_aboveLockscreenInteractable = false;
 
     std::optional<std::string> m_animationStyle;
 
diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp
index c51b8e1c2..58ae6e933 100644
--- a/src/managers/input/InputManager.cpp
+++ b/src/managers/input/InputManager.cpp
@@ -2,6 +2,7 @@
 #include "../../Compositor.hpp"
 #include <aquamarine/output/Output.hpp>
 #include <cstdint>
+#include <hyprutils/math/Vector2D.hpp>
 #include <ranges>
 #include "../../config/ConfigValue.hpp"
 #include "../../config/ConfigManager.hpp"
@@ -249,15 +250,29 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse) {
         g_pCompositor->setActiveMonitor(PMONITOR);
 
     if (g_pSessionLockManager->isSessionLocked()) {
+
+        // set keyboard focus on session lock surface regardless of layers
         const auto PSESSIONLOCKSURFACE = g_pSessionLockManager->getSessionLockSurfaceForMonitor(PMONITOR->ID);
-        surfacePos                     = PMONITOR->vecPosition;
+        const auto foundLockSurface    = PSESSIONLOCKSURFACE ? PSESSIONLOCKSURFACE->surface->surface() : nullptr;
 
-        foundSurface = PSESSIONLOCKSURFACE ? PSESSIONLOCKSURFACE->surface->surface() : nullptr;
-        g_pCompositor->focusSurface(foundSurface);
+        g_pCompositor->focusSurface(foundLockSurface);
+
+        // search for interactable abovelock surfaces for pointer focus, or use session lock surface if not found
+        for (auto& lsl : PMONITOR->m_aLayerSurfaceLayers | std::views::reverse) {
+            foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &lsl, &surfaceCoords, &pFoundLayerSurface, true);
+
+            if (foundSurface)
+                break;
+        }
+
+        if (!foundSurface) {
+            surfaceCoords = mouseCoords - PMONITOR->vecPosition;
+            foundSurface  = foundLockSurface;
+        }
+
+        g_pSeatManager->setPointerFocus(foundSurface, surfaceCoords);
+        g_pSeatManager->sendPointerMotion(time, surfaceCoords);
 
-        const auto SURFACELOCAL = mouseCoords - surfacePos;
-        g_pSeatManager->setPointerFocus(foundSurface, SURFACELOCAL);
-        g_pSeatManager->sendPointerMotion(time, SURFACELOCAL);
         return;
     }
 
diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp
index 364f7fdeb..fd2310275 100644
--- a/src/render/Renderer.cpp
+++ b/src/render/Renderer.cpp
@@ -690,10 +690,14 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T
     g_pHyprOpenGL->m_RenderData.currentWindow.reset();
 }
 
-void CHyprRenderer::renderLayer(PHLLS pLayer, PHLMONITOR pMonitor, const Time::steady_tp& time, bool popups) {
+void CHyprRenderer::renderLayer(PHLLS pLayer, PHLMONITOR pMonitor, const Time::steady_tp& time, bool popups, bool lockscreen) {
     if (!pLayer)
         return;
 
+    // skip rendering based on abovelock rule and make sure to not render abovelock layers twice
+    if ((pLayer->m_aboveLockscreen && !lockscreen && g_pSessionLockManager->isSessionLocked()) || (lockscreen && !pLayer->m_aboveLockscreen))
+        return;
+
     static auto PDIMAROUND = CConfigValue<Hyprlang::FLOAT>("decoration:dim_around");
 
     if (*PDIMAROUND && pLayer->m_dimAround && !m_bRenderingSnapshot && !popups) {
@@ -998,6 +1002,19 @@ void CHyprRenderer::renderLockscreen(PHLMONITOR pMonitor, const Time::steady_tp&
                 renderSessionLockMissing(pMonitor);
         } else {
             renderSessionLockSurface(PSLS, pMonitor, now);
+
+            // render layers and then their popups for abovelock rule
+            for (auto const& lsl : pMonitor->m_aLayerSurfaceLayers) {
+                for (auto const& ls : lsl) {
+                    renderLayer(ls.lock(), pMonitor, now, false, true);
+                }
+            }
+            for (auto const& lsl : pMonitor->m_aLayerSurfaceLayers) {
+                for (auto const& ls : lsl) {
+                    renderLayer(ls.lock(), pMonitor, now, true, true);
+                }
+            }
+
             g_pSessionLockManager->onLockscreenRenderedOnMonitor(pMonitor->ID);
         }
     }
diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp
index 1d0df2a5b..553965a46 100644
--- a/src/render/Renderer.hpp
+++ b/src/render/Renderer.hpp
@@ -120,7 +120,7 @@ class CHyprRenderer {
     void renderWorkspaceWindowsFullscreen(PHLMONITOR, PHLWORKSPACE, const Time::steady_tp&); // renders workspace windows (fullscreen) (tiled, floating, pinned, but no special)
     void renderWorkspaceWindows(PHLMONITOR, PHLWORKSPACE, const Time::steady_tp&);           // renders workspace windows (no fullscreen) (tiled, floating, pinned, but no special)
     void renderWindow(PHLWINDOW, PHLMONITOR, const Time::steady_tp&, bool, eRenderPassMode, bool ignorePosition = false, bool standalone = false);
-    void renderLayer(PHLLS, PHLMONITOR, const Time::steady_tp&, bool popups = false);
+    void renderLayer(PHLLS, PHLMONITOR, const Time::steady_tp&, bool popups = false, bool lockscreen = false);
     void renderSessionLockSurface(WP<SSessionLockSurface>, PHLMONITOR, const Time::steady_tp&);
     void renderDragIcon(PHLMONITOR, const Time::steady_tp&);
     void renderIMEPopup(CInputPopup*, PHLMONITOR, const Time::steady_tp&);