summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMaximilian Seidler <78690852+PaideiaDilemma@users.noreply.github.com>2025-07-14 13:13:54 +0200
committerGitHub <noreply@github.com>2025-07-14 13:13:54 +0200
commit01971cb6c7b7607329314645bf46e83d985eafeb (patch)
tree7c634faa6bfe228fc300845abb67765ed3372223 /src
parentd0f58baf296a2cdd5df0f82212fe17dfbef8438e (diff)
session-lock: don't render workspaces when locked (#10865)
Avoid rendering the workspace behind if we are locked
Diffstat (limited to 'src')
-rw-r--r--src/config/ConfigDescriptions.hpp6
-rw-r--r--src/config/ConfigManager.cpp1
-rw-r--r--src/managers/AnimationManager.cpp2
-rw-r--r--src/managers/SessionLockManager.cpp83
-rw-r--r--src/managers/SessionLockManager.hpp12
-rw-r--r--src/render/Renderer.cpp103
-rw-r--r--src/render/Renderer.hpp1
7 files changed, 128 insertions, 80 deletions
diff --git a/src/config/ConfigDescriptions.hpp b/src/config/ConfigDescriptions.hpp
index 0cf5d87b..927d67de 100644
--- a/src/config/ConfigDescriptions.hpp
+++ b/src/config/ConfigDescriptions.hpp
@@ -1219,6 +1219,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
+ .value = "misc:session_lock_xray",
+ .description = "keep rendering workspaces below your lockscreen",
+ .type = CONFIG_OPTION_BOOL,
+ .data = SConfigOptionDescription::SBoolData{false},
+ },
+ SConfigOptionDescription{
.value = "misc:background_color",
.description = "change the background color. (requires enabled disable_hyprland_logo)",
.type = CONFIG_OPTION_COLOR,
diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp
index 6154fcff..bc198fac 100644
--- a/src/config/ConfigManager.cpp
+++ b/src/config/ConfigManager.cpp
@@ -484,6 +484,7 @@ CConfigManager::CConfigManager() {
registerConfigVar("misc:focus_on_activate", Hyprlang::INT{0});
registerConfigVar("misc:mouse_move_focuses_monitor", Hyprlang::INT{1});
registerConfigVar("misc:allow_session_lock_restore", Hyprlang::INT{0});
+ registerConfigVar("misc:session_lock_xray", Hyprlang::INT{0});
registerConfigVar("misc:close_special_on_empty", Hyprlang::INT{1});
registerConfigVar("misc:background_color", Hyprlang::INT{0xff111111});
registerConfigVar("misc:new_window_takes_over_fullscreen", Hyprlang::INT{0});
diff --git a/src/managers/AnimationManager.cpp b/src/managers/AnimationManager.cpp
index d1da1a36..333111e9 100644
--- a/src/managers/AnimationManager.cpp
+++ b/src/managers/AnimationManager.cpp
@@ -33,7 +33,7 @@ static int wlTick(SP<CEventLoopTimer> self, void* data) {
}
CHyprAnimationManager::CHyprAnimationManager() {
- m_animationTimer = SP<CEventLoopTimer>(new CEventLoopTimer(std::chrono::microseconds(500), wlTick, nullptr));
+ m_animationTimer = makeShared<CEventLoopTimer>(std::chrono::microseconds(500), wlTick, nullptr);
if (g_pEventLoopManager) // null in --verify-config mode
g_pEventLoopManager->addTimer(m_animationTimer);
diff --git a/src/managers/SessionLockManager.cpp b/src/managers/SessionLockManager.cpp
index bfd819cf..9ef37806 100644
--- a/src/managers/SessionLockManager.cpp
+++ b/src/managers/SessionLockManager.cpp
@@ -3,9 +3,10 @@
#include "../config/ConfigValue.hpp"
#include "../protocols/FractionalScale.hpp"
#include "../protocols/SessionLock.hpp"
-#include "../managers/SeatManager.hpp"
#include "../render/Renderer.hpp"
-#include "../managers/input/InputManager.hpp"
+#include "./managers/SeatManager.hpp"
+#include "./managers/input/InputManager.hpp"
+#include "./managers/eventLoop/EventLoopManager.hpp"
#include <algorithm>
#include <ranges>
@@ -49,16 +50,19 @@ void CSessionLockManager::onNewSessionLock(SP<CSessionLock> pLock) {
static auto PALLOWRELOCK = CConfigValue<Hyprlang::INT>("misc:allow_session_lock_restore");
if (PROTO::sessionLock->isLocked() && !*PALLOWRELOCK) {
- Debug::log(LOG, "Cannot re-lock, misc:allow_session_lock_restore is disabled");
+ LOGM(LOG, "Cannot re-lock, misc:allow_session_lock_restore is disabled");
pLock->sendDenied();
return;
}
- Debug::log(LOG, "Session got locked by {:x}", (uintptr_t)pLock.get());
+ if (m_sessionLock && !clientDenied() && !clientLocked())
+ return; // Not allowing to relock in case the old lock is still in a limbo
+
+ LOGM(LOG, "Session got locked by {:x}", (uintptr_t)pLock.get());
m_sessionLock = makeUnique<SSessionLock>();
m_sessionLock->lock = pLock;
- m_sessionLock->mLockTimer.reset();
+ m_sessionLock->lockTimer.reset();
m_sessionLock->listeners.newSurface = pLock->m_events.newLockSurface.listen([this](const SP<CSessionLockSurface>& surface) {
const auto PMONITOR = surface->monitor();
@@ -87,14 +91,48 @@ void CSessionLockManager::onNewSessionLock(SP<CSessionLock> pLock) {
g_pCompositor->focusSurface(nullptr);
g_pSeatManager->setGrab(nullptr);
+ m_sessionLock->sendDeniedTimer = makeShared<CEventLoopTimer>(
+ // Within this arbitrary amount of time, a session-lock client is expected to create and commit a lock surface for each output. If the client fails to do that, it will be denied.
+ std::chrono::seconds(5),
+ [](auto, auto) {
+ if (!g_pSessionLockManager || g_pSessionLockManager->clientLocked() || g_pSessionLockManager->clientDenied())
+ return;
+
+ if (g_pCompositor->m_unsafeState || !g_pCompositor->m_aqBackend->hasSession() || !g_pCompositor->m_aqBackend->session->active) {
+ // Because the session is inactive, there is a good reason for why the client did't recieve locked or denied.
+ // We send locked, although this could lead to imperfect frames when we start to render again.
+ g_pSessionLockManager->m_sessionLock->lock->sendLocked();
+ g_pSessionLockManager->m_sessionLock->hasSentLocked = true;
+ return;
+ }
+
+ if (g_pSessionLockManager->m_sessionLock && g_pSessionLockManager->m_sessionLock->lock) {
+ g_pSessionLockManager->m_sessionLock->lock->sendDenied();
+ g_pSessionLockManager->m_sessionLock->hasSentDenied = true;
+ }
+ },
+ nullptr);
+
+ if (m_sessionLock->sendDeniedTimer)
+ g_pEventLoopManager->addTimer(m_sessionLock->sendDeniedTimer);
+
// Normally the locked event is sent after each output rendered a lock screen frame.
// When there are no outputs, send it right away.
if (g_pCompositor->m_unsafeState) {
+ removeSendDeniedTimer();
m_sessionLock->lock->sendLocked();
m_sessionLock->hasSentLocked = true;
}
}
+void CSessionLockManager::removeSendDeniedTimer() {
+ if (!m_sessionLock || !m_sessionLock->sendDeniedTimer)
+ return;
+
+ g_pEventLoopManager->removeTimer(m_sessionLock->sendDeniedTimer);
+ m_sessionLock->sendDeniedTimer.reset();
+}
+
bool CSessionLockManager::isSessionLocked() {
return PROTO::sessionLock->isLocked();
}
@@ -115,28 +153,13 @@ WP<SSessionLockSurface> CSessionLockManager::getSessionLockSurfaceForMonitor(uin
return {};
}
-// We don't want the red screen to flash.
-float CSessionLockManager::getRedScreenAlphaForMonitor(uint64_t id) {
- if (!m_sessionLock)
- return 1.F;
-
- const auto& NOMAPPEDSURFACETIMER = m_sessionLock->mMonitorsWithoutMappedSurfaceTimers.find(id);
-
- if (NOMAPPEDSURFACETIMER == m_sessionLock->mMonitorsWithoutMappedSurfaceTimers.end()) {
- m_sessionLock->mMonitorsWithoutMappedSurfaceTimers.emplace(id, CTimer());
- m_sessionLock->mMonitorsWithoutMappedSurfaceTimers[id].reset();
- return 0.f;
- }
-
- return std::clamp(NOMAPPEDSURFACETIMER->second.getSeconds() - /* delay for screencopy */ 0.5f, 0.f, 1.f);
-}
-
void CSessionLockManager::onLockscreenRenderedOnMonitor(uint64_t id) {
- if (!m_sessionLock || m_sessionLock->hasSentLocked)
+ if (!m_sessionLock || m_sessionLock->hasSentLocked || m_sessionLock->hasSentDenied)
return;
m_sessionLock->lockedMonitors.emplace(id);
const bool LOCKED = std::ranges::all_of(g_pCompositor->m_monitors, [this](auto m) { return m_sessionLock->lockedMonitors.contains(m->m_id); });
if (LOCKED && m_sessionLock->lock->good()) {
+ removeSendDeniedTimer();
m_sessionLock->lock->sendLocked();
m_sessionLock->hasSentLocked = true;
}
@@ -157,6 +180,10 @@ bool CSessionLockManager::isSurfaceSessionLock(SP<CWLSurfaceResource> pSurface)
return false;
}
+bool CSessionLockManager::anySessionLockSurfacesPresent() {
+ return m_sessionLock && std::ranges::any_of(m_sessionLock->vSessionLockSurfaces, [](const auto& surf) { return surf->mapped; });
+}
+
void CSessionLockManager::removeSessionLockSurface(SSessionLockSurface* pSLS) {
if (!m_sessionLock)
return;
@@ -175,19 +202,19 @@ void CSessionLockManager::removeSessionLockSurface(SSessionLockSurface* pSLS) {
}
}
-bool CSessionLockManager::isSessionLockPresent() {
- return m_sessionLock && !m_sessionLock->vSessionLockSurfaces.empty();
+bool CSessionLockManager::clientLocked() {
+ return m_sessionLock && m_sessionLock->hasSentLocked;
}
-bool CSessionLockManager::anySessionLockSurfacesPresent() {
- return m_sessionLock && std::ranges::any_of(m_sessionLock->vSessionLockSurfaces, [](const auto& surf) { return surf->mapped; });
+bool CSessionLockManager::clientDenied() {
+ return m_sessionLock && m_sessionLock->hasSentDenied;
}
bool CSessionLockManager::shallConsiderLockMissing() {
if (!m_sessionLock)
- return false;
+ return true;
static auto LOCKDEAD_SCREEN_DELAY = CConfigValue<Hyprlang::INT>("misc:lockdead_screen_delay");
- return m_sessionLock->mLockTimer.getMillis() > *LOCKDEAD_SCREEN_DELAY;
+ return m_sessionLock->lockTimer.getMillis() > *LOCKDEAD_SCREEN_DELAY;
}
diff --git a/src/managers/SessionLockManager.hpp b/src/managers/SessionLockManager.hpp
index da15496e..2938a851 100644
--- a/src/managers/SessionLockManager.hpp
+++ b/src/managers/SessionLockManager.hpp
@@ -3,6 +3,7 @@
#include "../defines.hpp"
#include "../helpers/time/Timer.hpp"
#include "../helpers/signal/Signal.hpp"
+#include "./eventLoop/EventLoopTimer.hpp"
#include <cstdint>
#include <unordered_map>
#include <unordered_set>
@@ -29,10 +30,10 @@ struct SSessionLockSurface {
struct SSessionLock {
WP<CSessionLock> lock;
- CTimer mLockTimer;
+ CTimer lockTimer;
+ SP<CEventLoopTimer> sendDeniedTimer;
std::vector<UP<SSessionLockSurface>> vSessionLockSurfaces;
- std::unordered_map<uint64_t, CTimer> mMonitorsWithoutMappedSurfaceTimers;
struct {
CHyprSignalListener newSurface;
@@ -41,6 +42,7 @@ struct SSessionLock {
} listeners;
bool hasSentLocked = false;
+ bool hasSentDenied = false;
std::unordered_set<uint64_t> lockedMonitors;
};
@@ -51,10 +53,9 @@ class CSessionLockManager {
WP<SSessionLockSurface> getSessionLockSurfaceForMonitor(uint64_t);
- float getRedScreenAlphaForMonitor(uint64_t);
-
bool isSessionLocked();
- bool isSessionLockPresent();
+ bool clientLocked();
+ bool clientDenied();
bool isSurfaceSessionLock(SP<CWLSurfaceResource>);
bool anySessionLockSurfacesPresent();
@@ -72,6 +73,7 @@ class CSessionLockManager {
} m_listeners;
void onNewSessionLock(SP<CSessionLock> pWlrLock);
+ void removeSendDeniedTimer();
};
inline UP<CSessionLockManager> g_pSessionLockManager;
diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp
index 015fc24b..b2dd5ffc 100644
--- a/src/render/Renderer.cpp
+++ b/src/render/Renderer.cpp
@@ -36,6 +36,7 @@
#include "../protocols/ColorManagement.hpp"
#include "../protocols/types/ContentType.hpp"
#include "../helpers/MiscFunctions.hpp"
+#include "render/OpenGL.hpp"
#include <hyprutils/utils/ScopeGuard.hpp>
using namespace Hyprutils::Utils;
@@ -831,13 +832,16 @@ void CHyprRenderer::renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPA
static auto PRENDERTEX = CConfigValue<Hyprlang::INT>("misc:disable_hyprland_logo");
static auto PBACKGROUNDCOLOR = CConfigValue<Hyprlang::INT>("misc:background_color");
static auto PXPMODE = CConfigValue<Hyprlang::INT>("render:xp_mode");
+ static auto PSESSIONLOCKXRAY = CConfigValue<Hyprlang::INT>("misc:session_lock_xray");
if (!pMonitor)
return;
- if (g_pSessionLockManager->isSessionLocked() && !g_pSessionLockManager->isSessionLockPresent()) {
- // do not render anything. We will render a lockscreen anyways later.
- return;
+ if (g_pSessionLockManager->isSessionLocked() && !*PSESSIONLOCKXRAY) {
+ // We stop to render workspaces as soon as the lockscreen was sent the "locked" or "finished" (aka denied) event.
+ // In addition we make sure to stop rendering workspaces after misc:lockdead_screen_delay has passed.
+ if (g_pSessionLockManager->shallConsiderLockMissing() || g_pSessionLockManager->clientLocked() || g_pSessionLockManager->clientDenied())
+ return;
}
// todo: matrices are buggy atm for some reason, but probably would be preferable in the long run
@@ -990,66 +994,73 @@ void CHyprRenderer::renderLockscreen(PHLMONITOR pMonitor, const Time::steady_tp&
TRACY_GPU_ZONE("RenderLockscreen");
const bool LOCKED = g_pSessionLockManager->isSessionLocked();
+ if (!LOCKED) {
+ g_pHyprOpenGL->ensureLockTexturesRendered(false);
+ return;
+ }
- g_pHyprOpenGL->ensureLockTexturesRendered( //
- LOCKED && // session is locked AND
- !g_pSessionLockManager->getSessionLockSurfaceForMonitor(pMonitor->m_id) && // no session lock surface AND
- (g_pSessionLockManager->shallConsiderLockMissing() ||
- !g_pSessionLockManager->isSessionLockPresent()) // we can consider rendering the lockMissing texture OR there is no client altogether
- );
+ const bool RENDERPRIMER = g_pSessionLockManager->shallConsiderLockMissing() || g_pSessionLockManager->clientLocked() || g_pSessionLockManager->clientDenied();
+ if (RENDERPRIMER)
+ renderSessionLockPrimer(pMonitor);
- if (LOCKED) {
- Vector2D translate = {geometry.x, geometry.y};
+ const auto PSLS = g_pSessionLockManager->getSessionLockSurfaceForMonitor(pMonitor->m_id);
+ const bool RENDERLOCKMISSING = (PSLS.expired() || g_pSessionLockManager->clientDenied()) && g_pSessionLockManager->shallConsiderLockMissing();
- const auto PSLS = g_pSessionLockManager->getSessionLockSurfaceForMonitor(pMonitor->m_id);
- if (!PSLS) {
- if (g_pSessionLockManager->shallConsiderLockMissing() || !g_pSessionLockManager->isSessionLockPresent())
- renderSessionLockMissing(pMonitor);
- } else {
- renderSessionLockSurface(PSLS, pMonitor, now);
+ g_pHyprOpenGL->ensureLockTexturesRendered(RENDERLOCKMISSING);
- // render layers and then their popups for abovelock rule
- for (auto const& lsl : pMonitor->m_layerSurfaceLayers) {
- for (auto const& ls : lsl) {
- renderLayer(ls.lock(), pMonitor, now, false, true);
- }
+ if (RENDERLOCKMISSING)
+ renderSessionLockMissing(pMonitor);
+ else if (PSLS) {
+ renderSessionLockSurface(PSLS, pMonitor, now);
+ g_pSessionLockManager->onLockscreenRenderedOnMonitor(pMonitor->m_id);
+
+ // render layers and then their popups for abovelock rule
+ for (auto const& lsl : pMonitor->m_layerSurfaceLayers) {
+ for (auto const& ls : lsl) {
+ renderLayer(ls.lock(), pMonitor, now, false, true);
}
- for (auto const& lsl : pMonitor->m_layerSurfaceLayers) {
- for (auto const& ls : lsl) {
- renderLayer(ls.lock(), pMonitor, now, true, true);
- }
+ }
+ for (auto const& lsl : pMonitor->m_layerSurfaceLayers) {
+ for (auto const& ls : lsl) {
+ renderLayer(ls.lock(), pMonitor, now, true, true);
}
-
- g_pSessionLockManager->onLockscreenRenderedOnMonitor(pMonitor->m_id);
}
}
}
-void CHyprRenderer::renderSessionLockMissing(PHLMONITOR pMonitor) {
- const auto ALPHA = g_pSessionLockManager->getRedScreenAlphaForMonitor(pMonitor->m_id);
+void CHyprRenderer::renderSessionLockPrimer(PHLMONITOR pMonitor) {
+ static auto PSESSIONLOCKXRAY = CConfigValue<Hyprlang::INT>("misc:session_lock_xray");
+ if (*PSESSIONLOCKXRAY)
+ return;
+
+ CRectPassElement::SRectData data;
+ data.color = CHyprColor(0, 0, 0, 1.f);
+ data.box = CBox{{}, pMonitor->m_pixelSize};
- CBox monbox = {{}, pMonitor->m_pixelSize};
+ m_renderPass.add(makeShared<CRectPassElement>(data));
+}
+void CHyprRenderer::renderSessionLockMissing(PHLMONITOR pMonitor) {
const bool ANY_PRESENT = g_pSessionLockManager->anySessionLockSurfacesPresent();
- if (ANY_PRESENT) {
- // render image2, without instructions. Lock still "alive", unless texture dead
- g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_lockDead2Texture, monbox, ALPHA);
- } else {
- // render image, with instructions. Lock is gone.
- g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_lockDeadTexture, monbox, ALPHA);
+ // ANY_PRESENT: render image2, without instructions. Lock still "alive", unless texture dead
+ // else: render image, with instructions. Lock is gone.
+ CBox monbox = {{}, pMonitor->m_pixelSize};
+ CTexPassElement::SRenderData data;
+ data.tex = (ANY_PRESENT) ? g_pHyprOpenGL->m_lockDead2Texture : g_pHyprOpenGL->m_lockDeadTexture;
+ data.box = monbox;
+ data.a = 1;
+ m_renderPass.add(makeShared<CTexPassElement>(data));
+
+ if (!ANY_PRESENT && g_pHyprOpenGL->m_lockTtyTextTexture) {
// also render text for the tty number
- if (g_pHyprOpenGL->m_lockTtyTextTexture) {
- CBox texbox = {{}, g_pHyprOpenGL->m_lockTtyTextTexture->m_size};
- g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_lockTtyTextTexture, texbox, ALPHA);
- }
- }
+ CBox texbox = {{}, g_pHyprOpenGL->m_lockTtyTextTexture->m_size};
+ data.tex = g_pHyprOpenGL->m_lockTtyTextTexture;
+ data.box = texbox;
- if (ALPHA < 1.f) /* animate */
- damageMonitor(pMonitor);
- else
- g_pSessionLockManager->onLockscreenRenderedOnMonitor(pMonitor->m_id);
+ m_renderPass.add(makeShared<CTexPassElement>(data));
+ }
}
void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface, PHLMONITOR pMonitor, bool main, const Vector2D& projSize,
diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp
index ad4819eb..9f733a57 100644
--- a/src/render/Renderer.hpp
+++ b/src/render/Renderer.hpp
@@ -126,6 +126,7 @@ class CHyprRenderer {
void renderDragIcon(PHLMONITOR, const Time::steady_tp&);
void renderIMEPopup(CInputPopup*, PHLMONITOR, const Time::steady_tp&);
void sendFrameEventsToWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now); // sends frame displayed events but doesn't actually render anything
+ void renderSessionLockPrimer(PHLMONITOR pMonitor);
void renderSessionLockMissing(PHLMONITOR pMonitor);
bool commitPendingAndDoExplicitSync(PHLMONITOR pMonitor);