From 129e99a6f60eac7ece31a1ccd1e781075fd53050 Mon Sep 17 00:00:00 2001 From: vaxerski <43317083+vaxerski@users.noreply.github.com> Date: Fri, 3 Feb 2023 11:58:55 +0000 Subject: [PATCH] Implement ext-session-lock-v1 --- src/Compositor.cpp | 25 ++++- src/Compositor.hpp | 2 + src/events/Events.hpp | 3 + src/events/Misc.cpp | 6 ++ src/includes.hpp | 1 + src/managers/SessionLockManager.cpp | 153 ++++++++++++++++++++++++++++ src/managers/SessionLockManager.hpp | 46 +++++++++ src/managers/input/InputManager.cpp | 12 ++- src/render/Renderer.cpp | 33 +++++- src/render/Renderer.hpp | 2 + 10 files changed, 280 insertions(+), 3 deletions(-) create mode 100644 src/managers/SessionLockManager.cpp create mode 100644 src/managers/SessionLockManager.hpp diff --git a/src/Compositor.cpp b/src/Compositor.cpp index af65d561..616cb829 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -190,6 +190,8 @@ CCompositor::CCompositor() { m_sWLRHeadlessBackend = wlr_headless_backend_create(m_sWLDisplay); + m_sWLRSessionLockMgr = wlr_session_lock_manager_v1_create(m_sWLDisplay); + if (!m_sWLRHeadlessBackend) { Debug::log(CRIT, "Couldn't create the headless backend"); throw std::runtime_error("wlr_headless_backend_create() failed!"); @@ -253,6 +255,7 @@ void CCompositor::initAllSignals() { addWLSignal(&m_sWLRIMEMgr->events.input_method, &Events::listen_newIME, m_sWLRIMEMgr, "IMEMgr"); addWLSignal(&m_sWLRTextInputMgr->events.text_input, &Events::listen_newTextInput, m_sWLRTextInputMgr, "TextInputMgr"); addWLSignal(&m_sWLRActivation->events.request_activate, &Events::listen_activateXDG, m_sWLRActivation, "ActivationV1"); + addWLSignal(&m_sWLRSessionLockMgr->events.new_lock, &Events::listen_newSessionLock, m_sWLRSessionLockMgr, "SessionLockMgr"); if (m_sWRLDRMLeaseMgr) addWLSignal(&m_sWRLDRMLeaseMgr->events.request, &Events::listen_leaseRequest, &m_sWRLDRMLeaseMgr, "DRM"); @@ -340,6 +343,9 @@ void CCompositor::startCompositor() { Debug::log(LOG, "Creating the ProtocolManager!"); g_pProtocolManager = std::make_unique(); + Debug::log(LOG, "Creating the SessionLockManager!"); + g_pSessionLockManager = std::make_unique(); + Debug::log(LOG, "Creating the EventManager!"); g_pEventManager = std::make_unique(); g_pEventManager->startThread(); @@ -1112,7 +1118,24 @@ CWindow* CCompositor::getFirstWindowOnWorkspace(const int& id) { } bool CCompositor::doesSeatAcceptInput(wlr_surface* surface) { - return !m_sSeat.exclusiveClient || (surface && m_sSeat.exclusiveClient == wl_resource_get_client(surface->resource)); + if (g_pSessionLockManager->isSessionLocked()) { + if (g_pSessionLockManager->isSurfaceSessionLock(surface)) + return true; + + if (surface && m_sSeat.exclusiveClient == wl_resource_get_client(surface->resource)) + return true; + + return false; + } + + if (m_sSeat.exclusiveClient) { + if (surface && m_sSeat.exclusiveClient == wl_resource_get_client(surface->resource)) + return true; + + return false; + } + + return true; } bool CCompositor::isWindowActive(CWindow* pWindow) { diff --git a/src/Compositor.hpp b/src/Compositor.hpp index efb8bcc7..24f8dd21 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -16,6 +16,7 @@ #include "managers/AnimationManager.hpp" #include "managers/EventManager.hpp" #include "managers/ProtocolManager.hpp" +#include "managers/SessionLockManager.hpp" #include "debug/HyprDebugOverlay.hpp" #include "helpers/Monitor.hpp" #include "helpers/Workspace.hpp" @@ -73,6 +74,7 @@ class CCompositor { wlr_xdg_activation_v1* m_sWLRActivation; wlr_linux_dmabuf_v1* m_sWLRLinuxDMABuf; wlr_backend* m_sWLRHeadlessBackend; + wlr_session_lock_manager_v1* m_sWLRSessionLockMgr; // ------------------------------------------------- // std::string m_szWLDisplaySocket = ""; diff --git a/src/events/Events.hpp b/src/events/Events.hpp index 5036bd75..69b40d69 100644 --- a/src/events/Events.hpp +++ b/src/events/Events.hpp @@ -157,4 +157,7 @@ namespace Events { LISTENER(holdBegin); LISTENER(holdEnd); + + // Session Lock + LISTENER(newSessionLock); }; diff --git a/src/events/Misc.cpp b/src/events/Misc.cpp index cba0a294..8d1b9c00 100644 --- a/src/events/Misc.cpp +++ b/src/events/Misc.cpp @@ -224,3 +224,9 @@ void Events::listener_newTextInput(wl_listener* listener, void* data) { g_pInputManager->m_sIMERelay.onNewTextInput((wlr_text_input_v3*)data); } + +void Events::listener_newSessionLock(wl_listener* listener, void* data) { + Debug::log(LOG, "New session lock!"); + + g_pSessionLockManager->onNewSessionLock((wlr_session_lock_v1*)data); +} diff --git a/src/includes.hpp b/src/includes.hpp index 8c804ceb..aaac7c09 100644 --- a/src/includes.hpp +++ b/src/includes.hpp @@ -102,6 +102,7 @@ extern "C" { #include #include #include +#include #include diff --git a/src/managers/SessionLockManager.cpp b/src/managers/SessionLockManager.cpp new file mode 100644 index 00000000..64380cc4 --- /dev/null +++ b/src/managers/SessionLockManager.cpp @@ -0,0 +1,153 @@ +#include "SessionLockManager.hpp" +#include "../Compositor.hpp" + +static void handleSurfaceMap(void* owner, void* data) { + const auto PSURFACE = (SSessionLockSurface*)owner; + + Debug::log(LOG, "SessionLockSurface %lx mapped", PSURFACE); + + PSURFACE->mapped = true; + + g_pCompositor->focusSurface(PSURFACE->pWlrLockSurface->surface); + + const auto PMONITOR = g_pCompositor->getMonitorFromID(PSURFACE->iMonitorID); + + if (PMONITOR) + g_pHyprRenderer->damageMonitor(PMONITOR); + + g_pSessionLockManager->activateLock(); // activate lock here to prevent the red screen from flashing before that +} + +static void handleSurfaceCommit(void* owner, void* data) { + const auto PSURFACE = (SSessionLockSurface*)owner; + + const auto PMONITOR = g_pCompositor->getMonitorFromID(PSURFACE->iMonitorID); + + if (PMONITOR) + g_pHyprRenderer->damageMonitor(PMONITOR); +} + +static void handleSurfaceDestroy(void* owner, void* data) { + const auto PSURFACE = (SSessionLockSurface*)owner; + + Debug::log(LOG, "SessionLockSurface %lx destroyed", PSURFACE); + + PSURFACE->hyprListener_commit.removeCallback(); + PSURFACE->hyprListener_destroy.removeCallback(); + PSURFACE->hyprListener_map.removeCallback(); + + g_pSessionLockManager->removeSessionLockSurface(PSURFACE); +} + +void CSessionLockManager::onNewSessionLock(wlr_session_lock_v1* pWlrLock) { + + if (m_sSessionLock.active) { + Debug::log(LOG, "Attempted to lock a locked session!"); + wlr_session_lock_v1_destroy(pWlrLock); + return; + } + + Debug::log(LOG, "Session got locked by %lx", pWlrLock); + + m_sSessionLock.pWlrLock = pWlrLock; + + g_pCompositor->m_sSeat.exclusiveClient = wl_resource_get_client(pWlrLock->resource); + + m_sSessionLock.hyprListener_newSurface.initCallback( + &pWlrLock->events.new_surface, + [&](void* owner, void* data) { + const auto PSURFACE = &*m_sSessionLock.vSessionLockSurfaces.emplace_back(std::make_unique()); + + const auto PWLRSURFACE = (wlr_session_lock_surface_v1*)data; + + const auto PMONITOR = g_pCompositor->getMonitorFromOutput(PWLRSURFACE->output); + + if (!PMONITOR) { + m_sSessionLock.vSessionLockSurfaces.pop_back(); + return; + } + + PSURFACE->pWlrLockSurface = PWLRSURFACE; + PSURFACE->iMonitorID = PMONITOR->ID; + + wlr_session_lock_surface_v1_configure(PWLRSURFACE, PMONITOR->vecSize.x, PMONITOR->vecSize.y); + + PSURFACE->hyprListener_map.initCallback(&PWLRSURFACE->events.map, &handleSurfaceMap, PSURFACE, "SSessionLockSurface"); + PSURFACE->hyprListener_destroy.initCallback(&PWLRSURFACE->events.destroy, &handleSurfaceDestroy, PSURFACE, "SSessionLockSurface"); + PSURFACE->hyprListener_commit.initCallback(&PWLRSURFACE->surface->events.commit, &handleSurfaceCommit, PSURFACE, "SSessionLockSurface"); + }, + pWlrLock, "wlr_session_lock_v1"); + + m_sSessionLock.hyprListener_unlock.initCallback( + &pWlrLock->events.unlock, + [&](void* owner, void* data) { + Debug::log(LOG, "Session Unlocked"); + + m_sSessionLock.hyprListener_destroy.removeCallback(); + m_sSessionLock.hyprListener_newSurface.removeCallback(); + m_sSessionLock.hyprListener_unlock.removeCallback(); + + m_sSessionLock.active = false; + + g_pCompositor->m_sSeat.exclusiveClient = nullptr; + g_pInputManager->refocus(); + + for (auto& m : g_pCompositor->m_vMonitors) + g_pHyprRenderer->damageMonitor(m.get()); + }, + pWlrLock, "wlr_session_lock_v1"); + + m_sSessionLock.hyprListener_destroy.initCallback( + &pWlrLock->events.destroy, + [&](void* owner, void* data) { + Debug::log(LOG, "Session Lock Abandoned"); + + m_sSessionLock.hyprListener_destroy.removeCallback(); + m_sSessionLock.hyprListener_newSurface.removeCallback(); + m_sSessionLock.hyprListener_unlock.removeCallback(); + + g_pCompositor->m_sSeat.exclusiveClient = nullptr; + + g_pInputManager->refocus(); + + for (auto& m : g_pCompositor->m_vMonitors) + g_pHyprRenderer->damageMonitor(m.get()); + }, + pWlrLock, "wlr_session_lock_v1"); + + wlr_session_lock_v1_send_locked(pWlrLock); +} + +bool CSessionLockManager::isSessionLocked() { + return m_sSessionLock.active; +} + +SSessionLockSurface* CSessionLockManager::getSessionLockSurfaceForMonitor(const int& id) { + for (auto& sls : m_sSessionLock.vSessionLockSurfaces) { + if (sls->iMonitorID == id) { + if (sls->mapped) + return sls.get(); + else + return nullptr; + } + } + + return nullptr; +} + +bool CSessionLockManager::isSurfaceSessionLock(wlr_surface* pSurface) { + for (auto& sls : m_sSessionLock.vSessionLockSurfaces) { + if (sls->pWlrLockSurface->surface == pSurface) + return true; + } + + return false; +} + +void CSessionLockManager::removeSessionLockSurface(SSessionLockSurface* pSLS) { + std::erase_if(m_sSessionLock.vSessionLockSurfaces, [&](const auto& other) { return pSLS == other.get(); }); +} + +void CSessionLockManager::activateLock() { + m_sSessionLock.active = true; +} \ No newline at end of file diff --git a/src/managers/SessionLockManager.hpp b/src/managers/SessionLockManager.hpp new file mode 100644 index 00000000..51232867 --- /dev/null +++ b/src/managers/SessionLockManager.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include "../defines.hpp" + +struct SSessionLockSurface { + wlr_session_lock_surface_v1* pWlrLockSurface = nullptr; + int iMonitorID = -1; + + bool mapped = false; + + DYNLISTENER(map); + DYNLISTENER(destroy); + DYNLISTENER(commit); +}; + +struct SSessionLock { + bool active = false; + wlr_session_lock_v1* pWlrLock = nullptr; + + std::vector> vSessionLockSurfaces; + + DYNLISTENER(newSurface); + DYNLISTENER(unlock); + DYNLISTENER(destroy); +}; + +class CSessionLockManager { + public: + CSessionLockManager() = default; + ~CSessionLockManager() = default; + + void onNewSessionLock(wlr_session_lock_v1*); + SSessionLockSurface* getSessionLockSurfaceForMonitor(const int&); + + bool isSessionLocked(); + bool isSurfaceSessionLock(wlr_surface*); + + void removeSessionLockSurface(SSessionLockSurface*); + + void activateLock(); + + private: + SSessionLock m_sSessionLock; +}; + +inline std::unique_ptr g_pSessionLockManager; \ No newline at end of file diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index e3bd9c81..b2e653ae 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -85,7 +85,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { // constraints // All constraints TODO: multiple mice? - if (g_pCompositor->m_sSeat.mouse->currentConstraint && !g_pCompositor->m_sSeat.exclusiveClient) { + if (g_pCompositor->m_sSeat.mouse->currentConstraint && !g_pCompositor->m_sSeat.exclusiveClient && !g_pSessionLockManager->isSessionLocked()) { // XWayland windows sometimes issue constraints weirdly. // TODO: We probably should search their parent. wlr_xwayland_surface->parent const auto CONSTRAINTWINDOW = g_pCompositor->getConstraintWindow(g_pCompositor->m_sSeat.mouse); @@ -149,6 +149,16 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { ACTIVEWORKSPACE->setActive(true); } + if (g_pSessionLockManager->isSessionLocked()) { + const auto PSLS = PMONITOR ? g_pSessionLockManager->getSessionLockSurfaceForMonitor(PMONITOR->ID) : nullptr; + + if (!PSLS) + return; + + foundSurface = PSLS->pWlrLockSurface->surface; + surfacePos = PMONITOR->vecPosition; + } + // overlay is above fullscreen if (!foundSurface) foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &surfaceCoords, &pFoundLayerSurface); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 1c540f7b..6ff71702 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -383,6 +383,18 @@ void CHyprRenderer::renderIMEPopup(SIMEPopup* pPopup, CMonitor* pMonitor, timesp wlr_surface_for_each_surface(pPopup->pSurface->surface, renderSurface, &renderdata); } +void CHyprRenderer::renderSessionLockSurface(SSessionLockSurface* pSurface, CMonitor* pMonitor, timespec* time) { + SRenderData renderdata = {pMonitor, time, 0, 0}; + + renderdata.blur = false; + renderdata.surface = pSurface->pWlrLockSurface->surface; + renderdata.decorate = false; + renderdata.w = pMonitor->vecSize.x; + renderdata.h = pMonitor->vecSize.y; + + wlr_surface_for_each_surface(pSurface->pWlrLockSurface->surface, renderSurface, &renderdata); +} + void CHyprRenderer::renderAllClientsForMonitor(const int& ID, timespec* time) { const auto PMONITOR = g_pCompositor->getMonitorFromID(ID); static auto* const PDIMSPECIAL = &g_pConfigManager->getConfigValuePtr("decoration:dim_special")->floatValue; @@ -390,6 +402,13 @@ void CHyprRenderer::renderAllClientsForMonitor(const int& ID, timespec* time) { if (!PMONITOR) return; + if (!g_pCompositor->m_sSeat.exclusiveClient && g_pSessionLockManager->isSessionLocked()) { + // locked with no exclusive, draw only red + wlr_box boxe = {0, 0, INT16_MAX, INT16_MAX}; + g_pHyprOpenGL->renderRect(&boxe, CColor(1.0, 0.2, 0.2, 1.0)); + return; + } + // Render layer surfaces below windows for monitor for (auto& ls : PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]) { renderLayer(ls.get(), PMONITOR, time); @@ -541,6 +560,18 @@ void CHyprRenderer::renderAllClientsForMonitor(const int& ID, timespec* time) { } renderDragIcon(PMONITOR, time); + + if (g_pSessionLockManager->isSessionLocked()) { + const auto PSLS = g_pSessionLockManager->getSessionLockSurfaceForMonitor(PMONITOR->ID); + + if (!PSLS) { + // locked with no surface, fill with red + wlr_box boxe = {0, 0, INT16_MAX, INT16_MAX}; + g_pHyprOpenGL->renderRect(&boxe, CColor(1.0, 0.2, 0.2, 1.0)); + } else { + renderSessionLockSurface(PSLS, PMONITOR, time); + } + } } void CHyprRenderer::calculateUVForSurface(CWindow* pWindow, wlr_surface* pSurface, bool main) { @@ -702,7 +733,7 @@ bool CHyprRenderer::attemptDirectScanout(CMonitor* pMonitor) { } void CHyprRenderer::setWindowScanoutMode(CWindow* pWindow) { - if (!g_pCompositor->m_sWLRLinuxDMABuf) + if (!g_pCompositor->m_sWLRLinuxDMABuf || g_pSessionLockManager->isSessionLocked()) return; if (!pWindow->m_bIsFullscreen) { diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index c1cef365..9f2456f8 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -27,6 +27,7 @@ enum eRenderPassMode class CToplevelExportProtocolManager; class CInputManager; +struct SSessionLockSurface; class CHyprRenderer { public: @@ -63,6 +64,7 @@ class CHyprRenderer { void renderWorkspaceWithFullscreenWindow(CMonitor*, CWorkspace*, timespec*); void renderWindow(CWindow*, CMonitor*, timespec*, bool, eRenderPassMode, bool ignorePosition = false, bool ignoreAllGeometry = false); void renderLayer(SLayerSurface*, CMonitor*, timespec*); + void renderSessionLockSurface(SSessionLockSurface*, CMonitor*, timespec*); void renderDragIcon(CMonitor*, timespec*); void renderIMEPopup(SIMEPopup*, CMonitor*, timespec*);