2023-02-03 11:58:55 +00:00
# include "SessionLockManager.hpp"
# include "../Compositor.hpp"
2024-03-03 18:39:20 +00:00
# include "../config/ConfigValue.hpp"
2024-04-20 14:14:54 +01:00
# include "../protocols/FractionalScale.hpp"
2024-04-30 16:32:05 +01:00
# include "../protocols/SessionLock.hpp"
2025-01-17 15:21:35 +00:00
# include "../render/Renderer.hpp"
2025-07-14 13:13:54 +02:00
# include "./managers/SeatManager.hpp"
# include "./managers/input/InputManager.hpp"
# include "./managers/eventLoop/EventLoopManager.hpp"
2024-07-13 18:32:08 +08:00
# include <algorithm>
2024-08-29 23:30:12 +02:00
# include <ranges>
2023-02-03 11:58:55 +00:00
2024-04-30 16:32:05 +01:00
SSessionLockSurface : : SSessionLockSurface ( SP < CSessionLockSurface > surface_ ) : surface ( surface_ ) {
2024-05-05 17:16:00 +01:00
pWlrSurface = surface - > surface ( ) ;
2023-02-03 11:58:55 +00:00
2025-07-08 09:56:40 -07:00
listeners . map = surface_ - > m_events . map . listen ( [ this ] {
2024-04-30 16:32:05 +01:00
mapped = true ;
2023-02-03 11:58:55 +00:00
2024-06-25 14:22:38 -07:00
g_pInputManager - > simulateMouseMovement ( ) ;
2023-02-03 11:58:55 +00:00
2024-04-30 16:32:05 +01:00
const auto PMONITOR = g_pCompositor - > getMonitorFromID ( iMonitorID ) ;
2023-02-03 11:58:55 +00:00
2024-04-30 16:32:05 +01:00
if ( PMONITOR )
g_pHyprRenderer - > damageMonitor ( PMONITOR ) ;
} ) ;
2023-02-03 11:58:55 +00:00
2025-07-08 09:56:40 -07:00
listeners . destroy = surface_ - > m_events . destroy . listen ( [ this ] {
2025-04-22 15:23:29 +02:00
if ( pWlrSurface = = g_pCompositor - > m_lastFocus )
g_pCompositor - > m_lastFocus . reset ( ) ;
2023-02-03 11:58:55 +00:00
2024-04-30 16:32:05 +01:00
g_pSessionLockManager - > removeSessionLockSurface ( this ) ;
} ) ;
2023-02-03 11:58:55 +00:00
2025-07-08 09:56:40 -07:00
listeners . commit = surface_ - > m_events . commit . listen ( [ this ] {
2024-04-30 16:32:05 +01:00
const auto PMONITOR = g_pCompositor - > getMonitorFromID ( iMonitorID ) ;
2023-02-03 11:58:55 +00:00
2025-04-22 15:23:29 +02:00
if ( mapped & & ! g_pCompositor - > m_lastFocus )
2024-09-04 15:59:00 +00:00
g_pInputManager - > simulateMouseMovement ( ) ;
2024-04-30 16:32:05 +01:00
if ( PMONITOR )
g_pHyprRenderer - > damageMonitor ( PMONITOR ) ;
} ) ;
2023-02-03 11:58:55 +00:00
}
2024-04-30 16:32:05 +01:00
CSessionLockManager : : CSessionLockManager ( ) {
2025-07-08 09:56:40 -07:00
m_listeners . newLock = PROTO : : sessionLock - > m_events . newLock . listen ( [ this ] ( const auto & lock ) { this - > onNewSessionLock ( lock ) ; } ) ;
2023-02-03 11:58:55 +00:00
}
2024-04-30 16:32:05 +01:00
void CSessionLockManager : : onNewSessionLock ( SP < CSessionLock > pLock ) {
2024-03-03 18:39:20 +00:00
static auto PALLOWRELOCK = CConfigValue < Hyprlang : : INT > ( " misc:allow_session_lock_restore " ) ;
2023-05-13 12:36:36 +01:00
2024-04-30 16:32:05 +01:00
if ( PROTO : : sessionLock - > isLocked ( ) & & ! * PALLOWRELOCK ) {
2025-07-14 13:13:54 +02:00
LOGM ( LOG , " Cannot re-lock, misc:allow_session_lock_restore is disabled " ) ;
2024-05-07 18:42:55 +01:00
pLock - > sendDenied ( ) ;
2023-02-03 11:58:55 +00:00
return ;
}
2025-07-14 13:13:54 +02:00
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 ( ) ) ;
2023-02-03 11:58:55 +00:00
2025-05-02 17:07:20 +02:00
m_sessionLock = makeUnique < SSessionLock > ( ) ;
m_sessionLock - > lock = pLock ;
2025-07-14 13:13:54 +02:00
m_sessionLock - > lockTimer . reset ( ) ;
2023-02-03 11:58:55 +00:00
2025-07-08 09:56:40 -07:00
m_sessionLock - > listeners . newSurface = pLock - > m_events . newLockSurface . listen ( [ this ] ( const SP < CSessionLockSurface > & surface ) {
const auto PMONITOR = surface - > monitor ( ) ;
2023-02-03 11:58:55 +00:00
2025-07-08 09:56:40 -07:00
const auto NEWSURFACE = m_sessionLock - > vSessionLockSurfaces . emplace_back ( makeUnique < SSessionLockSurface > ( surface ) ) . get ( ) ;
2025-04-30 23:45:20 +02:00
NEWSURFACE - > iMonitorID = PMONITOR - > m_id ;
2025-07-08 09:56:40 -07:00
PROTO : : fractional - > sendScale ( surface - > surface ( ) , PMONITOR - > m_scale ) ;
2024-04-30 16:32:05 +01:00
} ) ;
2023-02-03 11:58:55 +00:00
2025-07-08 09:56:40 -07:00
m_sessionLock - > listeners . unlock = pLock - > m_events . unlockAndDestroy . listen ( [ this ] {
2025-05-02 17:07:20 +02:00
m_sessionLock . reset ( ) ;
2024-04-30 16:32:05 +01:00
g_pInputManager - > refocus ( ) ;
2024-03-15 17:17:13 +01:00
2025-04-22 15:23:29 +02:00
for ( auto const & m : g_pCompositor - > m_monitors )
2024-10-19 23:03:29 +01:00
g_pHyprRenderer - > damageMonitor ( m ) ;
2024-04-30 16:32:05 +01:00
} ) ;
2023-02-03 11:58:55 +00:00
2025-07-08 09:56:40 -07:00
m_sessionLock - > listeners . destroy = pLock - > m_events . destroyed . listen ( [ this ] {
2025-05-02 17:07:20 +02:00
m_sessionLock . reset ( ) ;
2024-04-30 16:32:05 +01:00
g_pCompositor - > focusSurface ( nullptr ) ;
2023-02-03 11:58:55 +00:00
2025-04-22 15:23:29 +02:00
for ( auto const & m : g_pCompositor - > m_monitors )
2024-10-19 23:03:29 +01:00
g_pHyprRenderer - > damageMonitor ( m ) ;
2024-04-30 16:32:05 +01:00
} ) ;
2023-02-03 11:58:55 +00:00
2024-06-25 14:22:38 -07:00
g_pCompositor - > focusSurface ( nullptr ) ;
2024-10-17 20:05:55 +00:00
g_pSeatManager - > setGrab ( nullptr ) ;
2025-02-13 11:06:36 +00:00
2025-07-30 20:36:02 +00:00
const bool NOACTIVEMONS = std : : ranges : : all_of ( g_pCompositor - > m_monitors , [ ] ( const auto & m ) { return ! m - > m_enabled | | ! m - > m_dpmsStatus ; } ) ;
if ( NOACTIVEMONS | | g_pCompositor - > m_unsafeState ) {
2025-07-19 16:48:20 +02:00
// Normally the locked event is sent after each output rendered a lock screen frame.
2025-07-30 20:36:02 +00:00
// When there are no active outputs, send it right away.
2025-05-02 17:07:20 +02:00
m_sessionLock - > lock - > sendLocked ( ) ;
m_sessionLock - > hasSentLocked = true ;
2025-07-30 20:36:02 +00:00
return ;
2025-02-13 11:06:36 +00:00
}
2025-07-30 20:36:02 +00:00
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_pSessionLockManager - > m_sessionLock | | ! g_pSessionLockManager - > m_sessionLock - > lock )
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 manage to render to all outputs.
// 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 ;
}
LOGM ( WARN , " Kicking lockscreen client, because it failed to render to all outputs within 5 seconds " ) ;
g_pSessionLockManager - > m_sessionLock - > lock - > sendDenied ( ) ;
g_pSessionLockManager - > m_sessionLock - > hasSentDenied = true ;
} ,
nullptr ) ;
g_pEventLoopManager - > addTimer ( m_sessionLock - > sendDeniedTimer ) ;
2023-02-03 11:58:55 +00:00
}
2025-07-14 13:13:54 +02:00
void CSessionLockManager : : removeSendDeniedTimer ( ) {
if ( ! m_sessionLock | | ! m_sessionLock - > sendDeniedTimer )
return ;
g_pEventLoopManager - > removeTimer ( m_sessionLock - > sendDeniedTimer ) ;
m_sessionLock - > sendDeniedTimer . reset ( ) ;
}
2023-02-03 11:58:55 +00:00
bool CSessionLockManager : : isSessionLocked ( ) {
2024-04-30 16:32:05 +01:00
return PROTO : : sessionLock - > isLocked ( ) ;
2023-02-03 11:58:55 +00:00
}
2025-01-25 12:44:13 -06:00
WP < SSessionLockSurface > CSessionLockManager : : getSessionLockSurfaceForMonitor ( uint64_t id ) {
2025-05-02 17:07:20 +02:00
if ( ! m_sessionLock )
2025-01-25 12:44:13 -06:00
return { } ;
2024-04-30 16:32:05 +01:00
2025-05-02 17:07:20 +02:00
for ( auto const & sls : m_sessionLock - > vSessionLockSurfaces ) {
2023-02-03 11:58:55 +00:00
if ( sls - > iMonitorID = = id ) {
if ( sls - > mapped )
2025-01-25 12:44:13 -06:00
return sls ;
2023-02-03 11:58:55 +00:00
else
2025-01-25 12:44:13 -06:00
return { } ;
2023-02-03 11:58:55 +00:00
}
}
2025-01-25 12:44:13 -06:00
return { } ;
2023-02-03 11:58:55 +00:00
}
2024-07-13 18:32:08 +08:00
void CSessionLockManager : : onLockscreenRenderedOnMonitor ( uint64_t id ) {
2025-07-14 13:13:54 +02:00
if ( ! m_sessionLock | | m_sessionLock - > hasSentLocked | | m_sessionLock - > hasSentDenied )
2024-07-13 18:32:08 +08:00
return ;
2025-07-30 20:36:02 +00:00
2025-05-02 17:07:20 +02:00
m_sessionLock - > lockedMonitors . emplace ( id ) ;
2025-07-30 20:36:02 +00:00
const bool LOCKED =
std : : ranges : : all_of ( g_pCompositor - > m_monitors , [ this ] ( auto m ) { return ! m - > m_enabled | | ! m - > m_dpmsStatus | | m_sessionLock - > lockedMonitors . contains ( m - > m_id ) ; } ) ;
2025-05-02 17:07:20 +02:00
if ( LOCKED & & m_sessionLock - > lock - > good ( ) ) {
2025-07-14 13:13:54 +02:00
removeSendDeniedTimer ( ) ;
2025-05-02 17:07:20 +02:00
m_sessionLock - > lock - > sendLocked ( ) ;
m_sessionLock - > hasSentLocked = true ;
2024-07-13 18:32:08 +08:00
}
}
2024-06-08 10:07:59 +02:00
bool CSessionLockManager : : isSurfaceSessionLock ( SP < CWLSurfaceResource > pSurface ) {
2024-04-30 16:32:05 +01:00
// TODO: this has some edge cases when it's wrong (e.g. destroyed lock but not yet surfaces)
// but can be easily fixed when I rewrite wlr_surface
2025-05-02 17:07:20 +02:00
if ( ! m_sessionLock )
2024-04-30 16:32:05 +01:00
return false ;
2025-05-02 17:07:20 +02:00
for ( auto const & sls : m_sessionLock - > vSessionLockSurfaces ) {
2024-05-05 17:16:00 +01:00
if ( sls - > surface - > surface ( ) = = pSurface )
2023-02-03 11:58:55 +00:00
return true ;
}
return false ;
}
2025-07-14 13:13:54 +02:00
bool CSessionLockManager : : anySessionLockSurfacesPresent ( ) {
return m_sessionLock & & std : : ranges : : any_of ( m_sessionLock - > vSessionLockSurfaces , [ ] ( const auto & surf ) { return surf - > mapped ; } ) ;
}
2023-02-03 11:58:55 +00:00
void CSessionLockManager : : removeSessionLockSurface ( SSessionLockSurface * pSLS ) {
2025-05-02 17:07:20 +02:00
if ( ! m_sessionLock )
2024-04-30 16:32:05 +01:00
return ;
2025-05-02 17:07:20 +02:00
std : : erase_if ( m_sessionLock - > vSessionLockSurfaces , [ & ] ( const auto & other ) { return pSLS = = other . get ( ) ; } ) ;
2024-03-15 19:44:17 +01:00
2025-04-22 15:23:29 +02:00
if ( g_pCompositor - > m_lastFocus )
2024-03-15 19:44:17 +01:00
return ;
2025-05-02 17:07:20 +02:00
for ( auto const & sls : m_sessionLock - > vSessionLockSurfaces ) {
2024-03-15 19:44:17 +01:00
if ( ! sls - > mapped )
continue ;
2024-05-05 17:16:00 +01:00
g_pCompositor - > focusSurface ( sls - > surface - > surface ( ) ) ;
2024-03-15 19:44:17 +01:00
break ;
}
2023-02-03 11:58:55 +00:00
}
2025-07-14 13:13:54 +02:00
bool CSessionLockManager : : clientLocked ( ) {
return m_sessionLock & & m_sessionLock - > hasSentLocked ;
2024-04-30 16:32:05 +01:00
}
2024-08-29 23:30:12 +02:00
2025-07-14 13:13:54 +02:00
bool CSessionLockManager : : clientDenied ( ) {
return m_sessionLock & & m_sessionLock - > hasSentDenied ;
2024-08-29 23:30:12 +02:00
}
2024-11-17 15:58:18 +00:00
bool CSessionLockManager : : shallConsiderLockMissing ( ) {
2025-05-02 17:07:20 +02:00
if ( ! m_sessionLock )
2025-07-14 13:13:54 +02:00
return true ;
2024-11-17 15:58:18 +00:00
static auto LOCKDEAD_SCREEN_DELAY = CConfigValue < Hyprlang : : INT > ( " misc:lockdead_screen_delay " ) ;
2025-07-14 13:13:54 +02:00
return m_sessionLock - > lockTimer . getMillis ( ) > * LOCKDEAD_SCREEN_DELAY ;
2025-01-17 15:21:35 +00:00
}