diff --git a/CMakeLists.txt b/CMakeLists.txt
index c1ecb14cdf2..c6d1f4e62d1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -290,7 +290,7 @@ endfunction()
 
 target_link_libraries(Hyprland OpenGL::EGL OpenGL::GL Threads::Threads)
 
-pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.4.0)
+pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.0)
 if(hyprland_protocols_dep_FOUND)
   pkg_get_variable(HYPRLAND_PROTOCOLS hyprland-protocols pkgdatadir)
   message(STATUS "hyprland-protocols dependency set to ${HYPRLAND_PROTOCOLS}")
@@ -321,6 +321,7 @@ protocolnew("protocols" "frog-color-management-v1" true)
 protocolnew("protocols" "wayland-drm" true)
 protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-ctm-control-v1" true)
 protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-surface-v1" true)
+protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-lock-notify-v1" true)
 
 protocolnew("staging/tearing-control" "tearing-control-v1" false)
 protocolnew("staging/fractional-scale" "fractional-scale-v1" false)
diff --git a/flake.lock b/flake.lock
index f16ecaa7f7c..7b85bd176bc 100644
--- a/flake.lock
+++ b/flake.lock
@@ -128,11 +128,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1735774328,
-        "narHash": "sha256-vIRwLS9w+N99EU1aJ+XNOU6mJTxrUBa31i1r82l0V7s=",
+        "lastModified": 1737127640,
+        "narHash": "sha256-mIQ3/axCZ4g8ySwWRbW4fJcyC9v55uAii3cqlJRtW8g=",
         "owner": "hyprwm",
         "repo": "hyprland-protocols",
-        "rev": "e3b6af97ddcfaafbda8e2828c719a5af84f662cb",
+        "rev": "455c055883d9639d4fcbfcedb4c6d12ce313791e",
         "type": "github"
       },
       "original": {
diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp
index b7d61f67d88..8e32bdecd42 100644
--- a/src/managers/ProtocolManager.cpp
+++ b/src/managers/ProtocolManager.cpp
@@ -20,6 +20,7 @@
 #include "../protocols/OutputPower.hpp"
 #include "../protocols/XDGActivation.hpp"
 #include "../protocols/IdleNotify.hpp"
+#include "../protocols/LockNotify.hpp"
 #include "../protocols/SessionLock.hpp"
 #include "../protocols/InputMethodV2.hpp"
 #include "../protocols/VirtualKeyboard.hpp"
@@ -145,6 +146,7 @@ CProtocolManager::CProtocolManager() {
     PROTO::outputPower         = std::make_unique<COutputPowerProtocol>(&zwlr_output_power_manager_v1_interface, 1, "OutputPower");
     PROTO::activation          = std::make_unique<CXDGActivationProtocol>(&xdg_activation_v1_interface, 1, "XDGActivation");
     PROTO::idle                = std::make_unique<CIdleNotifyProtocol>(&ext_idle_notifier_v1_interface, 1, "IdleNotify");
+    PROTO::lockNotify          = std::make_unique<CLockNotifyProtocol>(&hyprland_lock_notifier_v1_interface, 1, "IdleNotify");
     PROTO::sessionLock         = std::make_unique<CSessionLockProtocol>(&ext_session_lock_manager_v1_interface, 1, "SessionLock");
     PROTO::ime                 = std::make_unique<CInputMethodV2Protocol>(&zwp_input_method_manager_v2_interface, 1, "IMEv2");
     PROTO::virtualKeyboard     = std::make_unique<CVirtualKeyboardProtocol>(&zwp_virtual_keyboard_manager_v1_interface, 1, "VirtualKeyboard");
@@ -224,6 +226,7 @@ CProtocolManager::~CProtocolManager() {
     PROTO::outputPower.reset();
     PROTO::activation.reset();
     PROTO::idle.reset();
+    PROTO::lockNotify.reset();
     PROTO::sessionLock.reset();
     PROTO::ime.reset();
     PROTO::virtualKeyboard.reset();
@@ -296,7 +299,7 @@ bool CProtocolManager::isGlobalPrivileged(const wl_global* global) {
         PROTO::xdgDialog->getGlobal(),
         PROTO::singlePixel->getGlobal(),
         PROTO::primarySelection->getGlobal(),
-				PROTO::hyprlandSurface->getGlobal(),
+			  PROTO::hyprlandSurface->getGlobal(),
         PROTO::sync     ? PROTO::sync->getGlobal()      : nullptr,
         PROTO::mesaDRM  ? PROTO::mesaDRM->getGlobal()   : nullptr,
         PROTO::linuxDma ? PROTO::linuxDma->getGlobal()  : nullptr,
diff --git a/src/protocols/LockNotify.cpp b/src/protocols/LockNotify.cpp
new file mode 100644
index 00000000000..033a6fff369
--- /dev/null
+++ b/src/protocols/LockNotify.cpp
@@ -0,0 +1,88 @@
+#include "LockNotify.hpp"
+
+CHyprlandLockNotification::CHyprlandLockNotification(SP<CHyprlandLockNotificationV1> resource_) : m_resource(resource_) {
+    if (!m_resource->resource())
+        return;
+
+    m_resource->setDestroy([this](CHyprlandLockNotificationV1* r) { PROTO::lockNotify->destroyNotification(this); });
+    m_resource->setOnDestroy([this](CHyprlandLockNotificationV1* r) { PROTO::lockNotify->destroyNotification(this); });
+}
+
+bool CHyprlandLockNotification::good() {
+    return m_resource->resource();
+}
+
+void CHyprlandLockNotification::onLocked() {
+    if (!m_locked)
+        m_resource->sendLocked();
+
+    m_locked = true;
+}
+
+void CHyprlandLockNotification::onUnlocked() {
+    if (m_locked)
+        m_resource->sendUnlocked();
+
+    m_locked = false;
+}
+
+CLockNotifyProtocol::CLockNotifyProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
+    ;
+}
+
+void CLockNotifyProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
+    const auto RESOURCE = m_managers.emplace_back(std::make_unique<CHyprlandLockNotifierV1>(client, ver, id)).get();
+    RESOURCE->setOnDestroy([this](CHyprlandLockNotifierV1* p) { this->onManagerResourceDestroy(p->resource()); });
+
+    RESOURCE->setDestroy([this](CHyprlandLockNotifierV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); });
+    RESOURCE->setGetLockNotification([this](CHyprlandLockNotifierV1* pMgr, uint32_t id) { this->onGetNotification(pMgr, id); });
+}
+
+void CLockNotifyProtocol::onManagerResourceDestroy(wl_resource* res) {
+    std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; });
+}
+
+void CLockNotifyProtocol::destroyNotification(CHyprlandLockNotification* notif) {
+    std::erase_if(m_notifications, [&](const auto& other) { return other.get() == notif; });
+}
+
+void CLockNotifyProtocol::onGetNotification(CHyprlandLockNotifierV1* pMgr, uint32_t id) {
+    const auto CLIENT   = pMgr->client();
+    const auto RESOURCE = m_notifications.emplace_back(makeShared<CHyprlandLockNotification>(makeShared<CHyprlandLockNotificationV1>(CLIENT, pMgr->version(), id))).get();
+
+    if (!RESOURCE->good()) {
+        pMgr->noMemory();
+        m_notifications.pop_back();
+        return;
+    }
+
+    // Already locked?? Send locked right away
+    if (m_isLocked)
+        m_notifications.back()->onLocked();
+}
+
+void CLockNotifyProtocol::onLocked() {
+    if (m_isLocked) {
+        LOGM(ERR, "Not sending lock notification. Already locked!");
+        return;
+    }
+
+    for (auto const& n : m_notifications) {
+        n->onLocked();
+    }
+
+    m_isLocked = true;
+}
+
+void CLockNotifyProtocol::onUnlocked() {
+    if (!m_isLocked) {
+        LOGM(ERR, "Not sending unlock notification. Not locked!");
+        return;
+    }
+
+    for (auto const& n : m_notifications) {
+        n->onUnlocked();
+    }
+
+    m_isLocked = false;
+}
diff --git a/src/protocols/LockNotify.hpp b/src/protocols/LockNotify.hpp
new file mode 100644
index 00000000000..ec71034b016
--- /dev/null
+++ b/src/protocols/LockNotify.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <memory>
+#include <vector>
+#include <unordered_map>
+#include "WaylandProtocol.hpp"
+#include "hyprland-lock-notify-v1.hpp"
+
+class CEventLoopTimer;
+
+class CHyprlandLockNotification {
+  public:
+    CHyprlandLockNotification(SP<CHyprlandLockNotificationV1> resource_);
+    ~CHyprlandLockNotification() = default;
+
+    bool good();
+    void onLocked();
+    void onUnlocked();
+
+  private:
+    SP<CHyprlandLockNotificationV1> m_resource;
+    bool                            m_locked = false;
+};
+
+class CLockNotifyProtocol : public IWaylandProtocol {
+  public:
+    CLockNotifyProtocol(const wl_interface* iface, const int& ver, const std::string& name);
+
+    virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
+
+    void         onLocked();
+    void         onUnlocked();
+
+  private:
+    void onManagerResourceDestroy(wl_resource* res);
+    void destroyNotification(CHyprlandLockNotification* notif);
+    void onGetNotification(CHyprlandLockNotifierV1* pMgr, uint32_t id);
+
+    bool m_isLocked = false;
+
+    //
+    std::vector<UP<CHyprlandLockNotifierV1>>   m_managers;
+    std::vector<SP<CHyprlandLockNotification>> m_notifications;
+
+    friend class CHyprlandLockNotification;
+};
+
+namespace PROTO {
+    inline UP<CLockNotifyProtocol> lockNotify;
+};
diff --git a/src/protocols/SessionLock.cpp b/src/protocols/SessionLock.cpp
index 642a5b89167..ea2384d9196 100644
--- a/src/protocols/SessionLock.cpp
+++ b/src/protocols/SessionLock.cpp
@@ -2,6 +2,7 @@
 #include "../Compositor.hpp"
 #include "../managers/SeatManager.hpp"
 #include "FractionalScale.hpp"
+#include "LockNotify.hpp"
 #include "core/Compositor.hpp"
 #include "core/Output.hpp"
 
@@ -114,6 +115,8 @@ CSessionLock::CSessionLock(SP<CExtSessionLockV1> resource_) : resource(resource_
 
         PROTO::sessionLock->locked = false;
 
+        PROTO::lockNotify->onUnlocked();
+
         events.unlockAndDestroy.emit();
 
         inert = true;
@@ -127,6 +130,7 @@ CSessionLock::~CSessionLock() {
 
 void CSessionLock::sendLocked() {
     resource->sendLocked();
+    PROTO::lockNotify->onLocked();
 }
 
 bool CSessionLock::good() {