mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-07-25 17:21:54 -07:00
session-lock: don't render workspaces when locked (#10865)
Avoid rendering the workspace behind if we are locked
This commit is contained in:
committed by
GitHub
parent
d0f58baf29
commit
01971cb6c7
@@ -1218,6 +1218,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.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)",
|
||||
|
@@ -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});
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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::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};
|
||||
|
||||
m_renderPass.add(makeShared<CRectPassElement>(data));
|
||||
}
|
||||
|
||||
void CHyprRenderer::renderSessionLockMissing(PHLMONITOR pMonitor) {
|
||||
const auto ALPHA = g_pSessionLockManager->getRedScreenAlphaForMonitor(pMonitor->m_id);
|
||||
|
||||
CBox monbox = {{}, pMonitor->m_pixelSize};
|
||||
|
||||
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,
|
||||
|
@@ -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);
|
||||
|
Reference in New Issue
Block a user