2024-02-17 19:30:11 +00:00
# include "Hypridle.hpp"
# include "../helpers/Log.hpp"
# include "../config/ConfigManager.hpp"
2025-02-13 21:55:17 +05:00
# include "csignal"
2024-02-17 19:30:11 +00:00
# include <sys/wait.h>
2024-02-17 22:13:06 +00:00
# include <sys/poll.h>
# include <sys/mman.h>
# include <fcntl.h>
# include <unistd.h>
2025-02-13 21:55:17 +05:00
# include <algorithm>
2024-02-22 11:33:19 +01:00
# include <thread>
# include <mutex>
2025-02-27 23:16:45 +00:00
# include <hyprutils/os/Process.hpp>
2024-02-17 19:30:11 +00:00
CHypridle : : CHypridle ( ) {
m_sWaylandState . display = wl_display_connect ( nullptr ) ;
if ( ! m_sWaylandState . display ) {
Debug : : log ( CRIT , " Couldn't connect to a wayland compositor " ) ;
2024-02-17 22:13:06 +00:00
exit ( 1 ) ;
2024-02-17 19:30:11 +00:00
}
}
void CHypridle : : run ( ) {
2025-01-27 13:24:13 +00:00
m_sWaylandState . registry = makeShared < CCWlRegistry > ( ( wl_proxy * ) wl_display_get_registry ( m_sWaylandState . display ) ) ;
m_sWaylandState . registry - > setGlobal ( [ this ] ( CCWlRegistry * r , uint32_t name , const char * interface , uint32_t version ) {
const std : : string IFACE = interface ;
Debug : : log ( LOG , " | got iface: {} v{} " , IFACE , version ) ;
if ( IFACE = = ext_idle_notifier_v1_interface . name ) {
m_sWaylandIdleState . notifier =
makeShared < CCExtIdleNotifierV1 > ( ( wl_proxy * ) wl_registry_bind ( ( wl_registry * ) r - > resource ( ) , name , & ext_idle_notifier_v1_interface , version ) ) ;
Debug : : log ( LOG , " > Bound to {} v{} " , IFACE , version ) ;
} else if ( IFACE = = hyprland_lock_notifier_v1_interface . name ) {
m_sWaylandState . lockNotifier =
makeShared < CCHyprlandLockNotifierV1 > ( ( wl_proxy * ) wl_registry_bind ( ( wl_registry * ) r - > resource ( ) , name , & hyprland_lock_notifier_v1_interface , version ) ) ;
Debug : : log ( LOG , " > Bound to {} v{} " , IFACE , version ) ;
} else if ( IFACE = = wl_seat_interface . name ) {
if ( m_sWaylandState . seat ) {
Debug : : log ( WARN , " Hypridle does not support multi-seat configurations. Only binding to the first seat. " ) ;
return ;
}
m_sWaylandState . seat = makeShared < CCWlSeat > ( ( wl_proxy * ) wl_registry_bind ( ( wl_registry * ) r - > resource ( ) , name , & wl_seat_interface , version ) ) ;
Debug : : log ( LOG , " > Bound to {} v{} " , IFACE , version ) ;
}
} ) ;
2024-02-17 19:30:11 +00:00
2025-01-27 13:24:13 +00:00
m_sWaylandState . registry - > setGlobalRemove ( [ ] ( CCWlRegistry * r , uint32_t name ) { Debug : : log ( LOG , " | removed iface {} " , name ) ; } ) ;
2024-02-17 19:30:11 +00:00
wl_display_roundtrip ( m_sWaylandState . display ) ;
if ( ! m_sWaylandIdleState . notifier ) {
Debug : : log ( CRIT , " Couldn't bind to ext-idle-notifier-v1, does your compositor support it? " ) ;
2024-02-17 22:13:06 +00:00
exit ( 1 ) ;
2024-02-17 19:30:11 +00:00
}
const auto RULES = g_pConfigManager - > getRules ( ) ;
m_sWaylandIdleState . listeners . resize ( RULES . size ( ) ) ;
2024-02-17 22:13:06 +00:00
Debug : : log ( LOG , " found {} rules " , RULES . size ( ) ) ;
2024-02-17 19:30:11 +00:00
for ( size_t i = 0 ; i < RULES . size ( ) ; + + i ) {
2025-01-27 13:24:13 +00:00
auto & l = m_sWaylandIdleState . listeners [ i ] ;
const auto & r = RULES [ i ] ;
l . onRestore = r . onResume ;
l . onTimeout = r . onTimeout ;
l . notification = makeShared < CCExtIdleNotificationV1 > ( m_sWaylandIdleState . notifier - > sendGetIdleNotification ( r . timeout * 1000 /* ms */ , m_sWaylandState . seat - > resource ( ) ) ) ;
l . notification - > setData ( & m_sWaylandIdleState . listeners [ i ] ) ;
2024-02-17 19:30:11 +00:00
2025-01-27 13:24:13 +00:00
l . notification - > setIdled ( [ this ] ( CCExtIdleNotificationV1 * n ) { onIdled ( ( CHypridle : : SIdleListener * ) n - > data ( ) ) ; } ) ;
l . notification - > setResumed ( [ this ] ( CCExtIdleNotificationV1 * n ) { onResumed ( ( CHypridle : : SIdleListener * ) n - > data ( ) ) ; } ) ;
2024-02-17 19:30:11 +00:00
}
2024-02-18 23:45:00 +00:00
wl_display_roundtrip ( m_sWaylandState . display ) ;
2025-01-27 13:24:13 +00:00
if ( m_sWaylandState . lockNotifier ) {
m_sWaylandState . lockNotification = makeShared < CCHyprlandLockNotificationV1 > ( m_sWaylandState . lockNotifier - > sendGetLockNotification ( ) ) ;
m_sWaylandState . lockNotification - > setLocked ( [ this ] ( CCHyprlandLockNotificationV1 * n ) { onLocked ( ) ; } ) ;
m_sWaylandState . lockNotification - > setUnlocked ( [ this ] ( CCHyprlandLockNotificationV1 * n ) { onUnlocked ( ) ; } ) ;
}
2024-02-17 22:13:06 +00:00
Debug : : log ( LOG , " wayland done, registering dbus " ) ;
try {
m_sDBUSState . connection = sdbus : : createSystemBusConnection ( ) ;
} catch ( std : : exception & e ) {
Debug : : log ( CRIT , " Couldn't create the dbus connection ({}) " , e . what ( ) ) ;
exit ( 1 ) ;
}
2025-01-27 13:24:13 +00:00
if ( ! m_sWaylandState . lockNotifier )
Debug : : log ( WARN ,
" Compositor is missing hyprland-lock-notify-v1! \n "
" general:inhibit_sleep=3, general:on_lock_cmd and general:on_unlock_cmd will not work. " ) ;
static const auto INHIBIT = g_pConfigManager - > getValue < Hyprlang : : INT > ( " general:inhibit_sleep " ) ;
static const auto SLEEPCMD = g_pConfigManager - > getValue < Hyprlang : : STRING > ( " general:before_sleep_cmd " ) ;
static const auto LOCKCMD = g_pConfigManager - > getValue < Hyprlang : : STRING > ( " general:lock_cmd " ) ;
switch ( * INHIBIT ) {
case 0 : // disabled
m_inhibitSleepBehavior = SLEEP_INHIBIT_NONE ;
break ;
case 1 : // enabled
m_inhibitSleepBehavior = SLEEP_INHIBIT_NORMAL ;
break ;
case 2 : { // auto (enable, but wait until locked if before_sleep_cmd contains hyprlock, or loginctl lock-session and lock_cmd contains hyprlock.)
if ( m_sWaylandState . lockNotifier & & std : : string { * SLEEPCMD } . contains ( " hyprlock " ) )
m_inhibitSleepBehavior = SLEEP_INHIBIT_LOCK_NOTIFY ;
else if ( m_sWaylandState . lockNotifier & & std : : string { * LOCKCMD } . contains ( " hyprlock " ) & & std : : string { * SLEEPCMD } . contains ( " lock-session " ) )
m_inhibitSleepBehavior = SLEEP_INHIBIT_LOCK_NOTIFY ;
else
m_inhibitSleepBehavior = SLEEP_INHIBIT_NORMAL ;
} break ;
case 3 : // wait until locked
if ( m_sWaylandState . lockNotifier )
m_inhibitSleepBehavior = SLEEP_INHIBIT_LOCK_NOTIFY ;
break ;
default : Debug : : log ( ERR , " Invalid inhibit_sleep value: {} " , * INHIBIT ) ; break ;
}
switch ( m_inhibitSleepBehavior ) {
case SLEEP_INHIBIT_NONE : Debug : : log ( LOG , " Sleep inhibition disabled " ) ; break ;
case SLEEP_INHIBIT_NORMAL : Debug : : log ( LOG , " Sleep inhibition enabled " ) ; break ;
case SLEEP_INHIBIT_LOCK_NOTIFY : Debug : : log ( LOG , " Sleep inhibition enabled - inhibiting until the wayland session gets locked " ) ; break ;
}
2024-02-17 22:13:06 +00:00
setupDBUS ( ) ;
2025-01-27 13:24:13 +00:00
if ( m_inhibitSleepBehavior ! = SLEEP_INHIBIT_NONE )
inhibitSleep ( ) ;
2024-02-17 22:13:06 +00:00
enterEventLoop ( ) ;
}
void CHypridle : : enterEventLoop ( ) {
2024-05-14 17:10:16 +02:00
nfds_t pollfdsCount = m_sDBUSState . screenSaverServiceConnection ? 3 : 2 ;
2024-02-17 22:13:06 +00:00
pollfd pollfds [ ] = {
{
. fd = m_sDBUSState . connection - > getEventLoopPollData ( ) . fd ,
. events = POLLIN ,
} ,
{
. fd = wl_display_get_fd ( m_sWaylandState . display ) ,
. events = POLLIN ,
} ,
2024-02-17 23:24:01 +00:00
{
. fd = m_sDBUSState . screenSaverServiceConnection ? m_sDBUSState . screenSaverServiceConnection - > getEventLoopPollData ( ) . fd : 0 ,
. events = POLLIN ,
} ,
2024-02-17 22:13:06 +00:00
} ;
2024-05-14 17:10:16 +02:00
std : : thread pollThr ( [ this , & pollfds , & pollfdsCount ] ( ) {
2024-02-17 22:13:06 +00:00
while ( 1 ) {
2024-05-14 17:10:16 +02:00
int ret = poll ( pollfds , pollfdsCount , 5000 /* 5 seconds, reasonable. It's because we might need to terminate */ ) ;
2024-02-17 22:13:06 +00:00
if ( ret < 0 ) {
Debug : : log ( CRIT , " [core] Polling fds failed with {} " , errno ) ;
m_bTerminate = true ;
exit ( 1 ) ;
}
2024-05-14 17:10:16 +02:00
for ( size_t i = 0 ; i < pollfdsCount ; + + i ) {
2024-02-17 22:13:06 +00:00
if ( pollfds [ i ] . revents & POLLHUP ) {
Debug : : log ( CRIT , " [core] Disconnected from pollfd id {} " , i ) ;
m_bTerminate = true ;
exit ( 1 ) ;
}
}
if ( m_bTerminate )
break ;
if ( ret ! = 0 ) {
Debug : : log ( TRACE , " [core] got poll event " ) ;
std : : lock_guard < std : : mutex > lg ( m_sEventLoopInternals . loopRequestMutex ) ;
m_sEventLoopInternals . shouldProcess = true ;
m_sEventLoopInternals . loopSignal . notify_all ( ) ;
}
}
} ) ;
while ( 1 ) { // dbus events
// wait for being awakened
m_sEventLoopInternals . loopRequestMutex . unlock ( ) ; // unlock, we are ready to take events
std : : unique_lock lk ( m_sEventLoopInternals . loopMutex ) ;
2025-02-13 21:55:17 +05:00
if ( ! m_sEventLoopInternals . shouldProcess ) // avoid a lock if a thread managed to request something already since we .unlock()ed
2024-02-17 22:13:06 +00:00
m_sEventLoopInternals . loopSignal . wait ( lk , [ this ] { return m_sEventLoopInternals . shouldProcess = = true ; } ) ; // wait for events
m_sEventLoopInternals . loopRequestMutex . lock ( ) ; // lock incoming events
if ( m_bTerminate )
break ;
m_sEventLoopInternals . shouldProcess = false ;
std : : lock_guard < std : : mutex > lg ( m_sEventLoopInternals . eventLock ) ;
if ( pollfds [ 0 ] . revents & POLLIN /* dbus */ ) {
Debug : : log ( TRACE , " got dbus event " ) ;
2024-10-23 00:45:24 +01:00
while ( m_sDBUSState . connection - > processPendingEvent ( ) ) {
2024-02-17 22:13:06 +00:00
;
}
}
if ( pollfds [ 1 ] . revents & POLLIN /* wl */ ) {
Debug : : log ( TRACE , " got wl event " ) ;
wl_display_flush ( m_sWaylandState . display ) ;
if ( wl_display_prepare_read ( m_sWaylandState . display ) = = 0 ) {
wl_display_read_events ( m_sWaylandState . display ) ;
wl_display_dispatch_pending ( m_sWaylandState . display ) ;
} else {
wl_display_dispatch ( m_sWaylandState . display ) ;
}
}
2024-05-14 17:10:16 +02:00
if ( pollfdsCount > 2 & & pollfds [ 2 ] . revents & POLLIN /* dbus2 */ ) {
2024-02-17 23:24:01 +00:00
Debug : : log ( TRACE , " got dbus event " ) ;
2024-10-23 00:45:24 +01:00
while ( m_sDBUSState . screenSaverServiceConnection - > processPendingEvent ( ) ) {
2024-02-17 23:24:01 +00:00
;
}
}
2024-02-17 22:13:06 +00:00
// finalize wayland dispatching. Dispatch pending on the queue
int ret = 0 ;
do {
ret = wl_display_dispatch_pending ( m_sWaylandState . display ) ;
wl_display_flush ( m_sWaylandState . display ) ;
} while ( ret > 0 ) ;
2024-02-17 19:30:11 +00:00
}
2024-02-17 22:13:06 +00:00
Debug : : log ( ERR , " [core] Terminated " ) ;
2024-02-17 19:30:11 +00:00
}
static void spawn ( const std : : string & args ) {
Debug : : log ( LOG , " Executing {} " , args ) ;
2025-02-27 23:16:45 +00:00
Hyprutils : : OS : : CProcess proc ( " /bin/sh " , { " -c " , args } ) ;
if ( ! proc . runAsync ( ) ) {
Debug : : log ( ERR , " Failed run \" {} \" " , args ) ;
2024-02-17 19:30:11 +00:00
return ;
}
2025-02-27 23:16:45 +00:00
Debug : : log ( LOG , " Process Created with pid {} " , proc . pid ( ) ) ;
2024-02-17 19:30:11 +00:00
}
void CHypridle : : onIdled ( SIdleListener * pListener ) {
Debug : : log ( LOG , " Idled: rule {:x} " , ( uintptr_t ) pListener ) ;
2024-02-27 22:22:15 +01:00
isIdled = true ;
2024-02-18 14:58:39 +00:00
if ( g_pHypridle - > m_iInhibitLocks > 0 ) {
2024-02-19 22:41:52 +01:00
Debug : : log ( LOG , " Ignoring from onIdled(), inhibit locks: {} " , g_pHypridle - > m_iInhibitLocks ) ;
2024-02-18 14:58:39 +00:00
return ;
}
2024-02-17 19:30:11 +00:00
if ( pListener - > onTimeout . empty ( ) ) {
Debug : : log ( LOG , " Ignoring, onTimeout is empty. " ) ;
return ;
}
Debug : : log ( LOG , " Running {} " , pListener - > onTimeout ) ;
spawn ( pListener - > onTimeout ) ;
}
void CHypridle : : onResumed ( SIdleListener * pListener ) {
Debug : : log ( LOG , " Resumed: rule {:x} " , ( uintptr_t ) pListener ) ;
2024-02-27 22:22:15 +01:00
isIdled = false ;
2024-02-18 14:58:39 +00:00
if ( g_pHypridle - > m_iInhibitLocks > 0 ) {
2024-02-19 22:41:52 +01:00
Debug : : log ( LOG , " Ignoring from onResumed(), inhibit locks: {} " , g_pHypridle - > m_iInhibitLocks ) ;
2024-02-18 14:58:39 +00:00
return ;
}
2024-02-17 19:30:11 +00:00
if ( pListener - > onRestore . empty ( ) ) {
Debug : : log ( LOG , " Ignoring, onRestore is empty. " ) ;
return ;
}
Debug : : log ( LOG , " Running {} " , pListener - > onRestore ) ;
spawn ( pListener - > onRestore ) ;
}
2024-02-17 22:13:06 +00:00
2024-02-17 23:24:01 +00:00
void CHypridle : : onInhibit ( bool lock ) {
m_iInhibitLocks + = lock ? 1 : - 1 ;
2024-06-22 18:00:57 +03:00
2024-02-19 22:41:52 +01:00
if ( m_iInhibitLocks < 0 ) {
2024-06-22 18:00:57 +03:00
Debug : : log ( WARN , " BUG THIS: inhibit locks < 0: {} " , m_iInhibitLocks ) ;
2024-02-19 22:41:52 +01:00
m_iInhibitLocks = 0 ;
2024-06-22 18:00:57 +03:00
}
if ( m_iInhibitLocks = = 0 & & isIdled ) {
const auto RULES = g_pConfigManager - > getRules ( ) ;
for ( size_t i = 0 ; i < RULES . size ( ) ; + + i ) {
2024-10-20 23:04:37 +01:00
auto & l = m_sWaylandIdleState . listeners [ i ] ;
const auto & r = RULES [ i ] ;
2024-06-22 18:00:57 +03:00
2025-01-27 13:24:13 +00:00
l . notification - > sendDestroy ( ) ;
2024-06-22 18:00:57 +03:00
2025-01-27 13:24:13 +00:00
l . notification =
makeShared < CCExtIdleNotificationV1 > ( m_sWaylandIdleState . notifier - > sendGetIdleNotification ( r . timeout * 1000 /* ms */ , m_sWaylandState . seat - > resource ( ) ) ) ;
l . notification - > setData ( & m_sWaylandIdleState . listeners [ i ] ) ;
2024-06-22 18:00:57 +03:00
2025-01-27 13:24:13 +00:00
l . notification - > setIdled ( [ this ] ( CCExtIdleNotificationV1 * n ) { onIdled ( ( CHypridle : : SIdleListener * ) n - > data ( ) ) ; } ) ;
l . notification - > setResumed ( [ this ] ( CCExtIdleNotificationV1 * n ) { onResumed ( ( CHypridle : : SIdleListener * ) n - > data ( ) ) ; } ) ;
2024-02-27 22:22:15 +01:00
}
}
2024-06-22 18:00:57 +03:00
Debug : : log ( LOG , " Inhibit locks: {} " , m_iInhibitLocks ) ;
2024-02-17 23:24:01 +00:00
}
2025-01-27 13:24:13 +00:00
void CHypridle : : onLocked ( ) {
Debug : : log ( LOG , " Wayland session got locked " ) ;
m_isLocked = true ;
static const auto LOCKCMD = g_pConfigManager - > getValue < Hyprlang : : STRING > ( " general:on_lock_cmd " ) ;
if ( * LOCKCMD )
spawn ( * LOCKCMD ) ;
if ( m_inhibitSleepBehavior = = SLEEP_INHIBIT_LOCK_NOTIFY )
uninhibitSleep ( ) ;
}
void CHypridle : : onUnlocked ( ) {
Debug : : log ( LOG , " Wayland session got unlocked " ) ;
m_isLocked = false ;
if ( m_inhibitSleepBehavior = = SLEEP_INHIBIT_LOCK_NOTIFY )
inhibitSleep ( ) ;
static const auto UNLOCKCMD = g_pConfigManager - > getValue < Hyprlang : : STRING > ( " general:on_unlock_cmd " ) ;
if ( * UNLOCKCMD )
spawn ( * UNLOCKCMD ) ;
}
2024-02-17 23:24:01 +00:00
CHypridle : : SDbusInhibitCookie CHypridle : : getDbusInhibitCookie ( uint32_t cookie ) {
for ( auto & c : m_sDBUSState . inhibitCookies ) {
if ( c . cookie = = cookie )
return c ;
}
return { } ;
}
void CHypridle : : registerDbusInhibitCookie ( CHypridle : : SDbusInhibitCookie & cookie ) {
m_sDBUSState . inhibitCookies . push_back ( cookie ) ;
}
2024-10-20 23:04:37 +01:00
bool CHypridle : : unregisterDbusInhibitCookie ( const CHypridle : : SDbusInhibitCookie & cookie ) {
2025-02-13 21:55:17 +05:00
const auto IT = std : : ranges : : find_if ( m_sDBUSState . inhibitCookies , [ & cookie ] ( const CHypridle : : SDbusInhibitCookie & item ) { return item . cookie = = cookie . cookie ; } ) ;
2024-02-20 19:25:50 +01:00
if ( IT = = m_sDBUSState . inhibitCookies . end ( ) )
2024-10-20 23:04:37 +01:00
return false ;
m_sDBUSState . inhibitCookies . erase ( IT ) ;
return true ;
}
bool CHypridle : : unregisterDbusInhibitCookies ( const std : : string & ownerID ) {
const auto IT = std : : remove_if ( m_sDBUSState . inhibitCookies . begin ( ) , m_sDBUSState . inhibitCookies . end ( ) ,
[ & ownerID ] ( const CHypridle : : SDbusInhibitCookie & item ) { return item . ownerID = = ownerID ; } ) ;
if ( IT = = m_sDBUSState . inhibitCookies . end ( ) )
return false ;
m_sDBUSState . inhibitCookies . erase ( IT , m_sDBUSState . inhibitCookies . end ( ) ) ;
return true ;
2024-02-20 19:25:50 +01:00
}
2024-10-23 00:45:24 +01:00
static void handleDbusLogin ( sdbus : : Message msg ) {
2024-02-17 22:13:06 +00:00
// lock & unlock
2025-01-27 13:24:13 +00:00
static const auto LOCKCMD = g_pConfigManager - > getValue < Hyprlang : : STRING > ( " general:lock_cmd " ) ;
static const auto UNLOCKCMD = g_pConfigManager - > getValue < Hyprlang : : STRING > ( " general:unlock_cmd " ) ;
2024-02-17 22:13:06 +00:00
Debug : : log ( LOG , " Got dbus .Session " ) ;
2024-10-23 00:45:24 +01:00
const std : : string MEMBER = msg . getMemberName ( ) ;
2024-02-17 22:13:06 +00:00
if ( MEMBER = = " Lock " ) {
Debug : : log ( LOG , " Got Lock from dbus " ) ;
2025-01-27 13:24:13 +00:00
if ( ! std : : string { * LOCKCMD } . empty ( ) ) {
Debug : : log ( LOG , " Locking with {} " , * LOCKCMD ) ;
spawn ( * LOCKCMD ) ;
2024-02-17 22:13:06 +00:00
}
} else if ( MEMBER = = " Unlock " ) {
Debug : : log ( LOG , " Got Unlock from dbus " ) ;
2025-01-27 13:24:13 +00:00
if ( ! std : : string { * UNLOCKCMD } . empty ( ) ) {
2025-02-27 23:16:45 +00:00
Debug : : log ( LOG , " Unlocking with {} " , * UNLOCKCMD ) ;
2025-01-27 13:24:13 +00:00
spawn ( * UNLOCKCMD ) ;
2024-02-17 22:13:06 +00:00
}
}
}
2024-10-23 00:45:24 +01:00
static void handleDbusSleep ( sdbus : : Message msg ) {
const std : : string MEMBER = msg . getMemberName ( ) ;
2024-02-17 22:13:06 +00:00
if ( MEMBER ! = " PrepareForSleep " )
return ;
2024-02-17 23:48:54 +00:00
bool toSleep = true ;
msg > > toSleep ;
2024-02-17 22:13:06 +00:00
2025-01-27 13:24:13 +00:00
static const auto SLEEPCMD = g_pConfigManager - > getValue < Hyprlang : : STRING > ( " general:before_sleep_cmd " ) ;
static const auto AFTERSLEEPCMD = g_pConfigManager - > getValue < Hyprlang : : STRING > ( " general:after_sleep_cmd " ) ;
2024-02-17 22:13:06 +00:00
2024-02-17 23:48:54 +00:00
Debug : : log ( LOG , " Got PrepareForSleep from dbus with sleep {} " , toSleep ) ;
2025-01-27 13:24:13 +00:00
std : : string cmd = toSleep ? * SLEEPCMD : * AFTERSLEEPCMD ;
2024-02-17 23:48:54 +00:00
2025-01-27 13:24:13 +00:00
if ( ! toSleep )
g_pHypridle - > handleInhibitOnDbusSleep ( toSleep ) ;
if ( ! cmd . empty ( ) )
spawn ( cmd ) ;
2024-02-17 22:13:06 +00:00
2025-01-27 13:24:13 +00:00
if ( toSleep )
g_pHypridle - > handleInhibitOnDbusSleep ( toSleep ) ;
2024-02-17 22:13:06 +00:00
}
2025-02-27 23:16:45 +00:00
static void handleDbusBlockInhibits ( const std : : string & inhibits ) {
2024-05-13 23:22:06 +02:00
static auto inhibited = false ;
// BlockInhibited is a colon separated list of inhibit types. Wrapping in additional colons allows for easier checking if there are active inhibits we are interested in
auto inhibits_ = " : " + inhibits + " : " ;
if ( inhibits_ . contains ( " :idle: " ) ) {
if ( ! inhibited ) {
inhibited = true ;
Debug : : log ( LOG , " systemd idle inhibit active " ) ;
g_pHypridle - > onInhibit ( true ) ;
}
} else if ( inhibited ) {
inhibited = false ;
Debug : : log ( LOG , " systemd idle inhibit inactive " ) ;
g_pHypridle - > onInhibit ( false ) ;
}
}
2024-10-23 00:45:24 +01:00
static void handleDbusBlockInhibitsPropertyChanged ( sdbus : : Message msg ) {
2024-10-20 23:04:37 +01:00
std : : string interface ;
2024-05-13 23:22:06 +02:00
std : : map < std : : string , sdbus : : Variant > changedProperties ;
msg > > interface > > changedProperties ;
if ( changedProperties . contains ( " BlockInhibited " ) ) {
handleDbusBlockInhibits ( changedProperties [ " BlockInhibited " ] . get < std : : string > ( ) ) ;
}
}
2024-10-23 11:39:20 +01:00
static uint32_t handleDbusScreensaver ( std : : string app , std : : string reason , uint32_t cookie , bool inhibit , const char * sender ) {
2024-10-23 00:45:24 +01:00
std : : string ownerID = sender ;
2024-02-17 23:24:01 +00:00
2024-10-23 00:45:24 +01:00
if ( ! inhibit ) {
2024-02-20 19:25:50 +01:00
Debug : : log ( TRACE , " Read uninhibit cookie: {} " , cookie ) ;
2024-02-17 23:24:01 +00:00
const auto COOKIE = g_pHypridle - > getDbusInhibitCookie ( cookie ) ;
if ( COOKIE . cookie = = 0 ) {
Debug : : log ( WARN , " No cookie in uninhibit " ) ;
} else {
2024-10-20 23:04:37 +01:00
app = COOKIE . app ;
reason = COOKIE . reason ;
ownerID = COOKIE . ownerID ;
2024-10-23 00:45:24 +01:00
if ( ! g_pHypridle - > unregisterDbusInhibitCookie ( COOKIE ) )
2024-10-20 23:04:37 +01:00
Debug : : log ( WARN , " BUG THIS: attempted to unregister unknown cookie " ) ;
2024-02-17 23:24:01 +00:00
}
}
2024-10-20 23:04:37 +01:00
Debug : : log ( LOG , " ScreenSaver inhibit: {} dbus message from {} (owner: {}) with content {} " , inhibit , app , ownerID , reason ) ;
2024-02-17 23:24:01 +00:00
2024-05-14 17:10:16 +02:00
if ( inhibit )
g_pHypridle - > onInhibit ( true ) ;
else
g_pHypridle - > onInhibit ( false ) ;
2024-02-17 23:24:01 +00:00
2025-02-13 21:55:17 +05:00
static uint32_t cookieID = 1337 ;
2024-02-17 23:24:01 +00:00
if ( inhibit ) {
2025-02-13 21:55:17 +05:00
auto cookie = CHypridle : : SDbusInhibitCookie { . cookie = cookieID , . app = app , . reason = reason , . ownerID = ownerID } ;
2024-02-20 19:25:50 +01:00
2024-10-23 00:45:24 +01:00
Debug : : log ( LOG , " Cookie {} sent " , cookieID ) ;
2024-02-20 19:25:50 +01:00
g_pHypridle - > registerDbusInhibitCookie ( cookie ) ;
2024-10-23 00:45:24 +01:00
return cookieID + + ;
2024-02-17 23:24:01 +00:00
}
2024-10-23 00:45:24 +01:00
return 0 ;
2024-02-17 23:24:01 +00:00
}
2024-10-23 00:45:24 +01:00
static void handleDbusNameOwnerChanged ( sdbus : : Message msg ) {
2024-10-20 23:04:37 +01:00
std : : string name , oldOwner , newOwner ;
msg > > name > > oldOwner > > newOwner ;
if ( ! newOwner . empty ( ) )
return ;
if ( g_pHypridle - > unregisterDbusInhibitCookies ( oldOwner ) ) {
Debug : : log ( LOG , " App with owner {} disconnected " , oldOwner ) ;
g_pHypridle - > onInhibit ( false ) ;
}
}
2024-02-17 22:13:06 +00:00
void CHypridle : : setupDBUS ( ) {
2025-01-27 13:24:13 +00:00
static const auto IGNOREDBUSINHIBIT = g_pConfigManager - > getValue < Hyprlang : : INT > ( " general:ignore_dbus_inhibit " ) ;
static const auto IGNORESYSTEMDINHIBIT = g_pConfigManager - > getValue < Hyprlang : : INT > ( " general:ignore_systemd_inhibit " ) ;
2024-05-14 17:10:16 +02:00
2024-10-23 00:45:24 +01:00
auto systemConnection = sdbus : : createSystemBusConnection ( ) ;
auto proxy = sdbus : : createProxy ( * systemConnection , sdbus : : ServiceName { " org.freedesktop.login1 " } , sdbus : : ObjectPath { " /org/freedesktop/login1 " } ) ;
2024-02-17 22:13:06 +00:00
sdbus : : ObjectPath path ;
2024-02-27 13:07:22 +00:00
try {
2024-10-23 00:45:24 +01:00
proxy - > callMethod ( " GetSession " ) . onInterface ( " org.freedesktop.login1.Manager " ) . withArguments ( std : : string { " auto " } ) . storeResultsTo ( path ) ;
2024-07-18 20:03:02 +00:00
2024-10-23 00:45:24 +01:00
m_sDBUSState . connection - > addMatch ( " type='signal',path=' " + path + " ',interface='org.freedesktop.login1.Session' " , : : handleDbusLogin ) ;
m_sDBUSState . connection - > addMatch ( " type='signal',path='/org/freedesktop/login1',interface='org.freedesktop.login1.Manager' " , : : handleDbusSleep ) ;
2025-01-27 13:24:13 +00:00
m_sDBUSState . login = sdbus : : createProxy ( * m_sDBUSState . connection , sdbus : : ServiceName { " org.freedesktop.login1 " } , sdbus : : ObjectPath { " /org/freedesktop/login1 " } ) ;
2024-10-20 23:04:37 +01:00
} catch ( std : : exception & e ) { Debug : : log ( WARN , " Couldn't connect to logind service ({}) " , e . what ( ) ) ; }
2024-02-17 22:13:06 +00:00
Debug : : log ( LOG , " Using dbus path {} " , path . c_str ( ) ) ;
2025-01-27 13:24:13 +00:00
if ( ! * IGNORESYSTEMDINHIBIT ) {
2024-10-23 00:45:24 +01:00
m_sDBUSState . connection - > addMatch ( " type='signal',path='/org/freedesktop/login1',interface='org.freedesktop.DBus.Properties' " , : : handleDbusBlockInhibitsPropertyChanged ) ;
2024-02-17 23:24:01 +00:00
2024-05-14 17:10:16 +02:00
try {
2024-10-23 00:45:24 +01:00
std : : string value = ( proxy - > getProperty ( " BlockInhibited " ) . onInterface ( " org.freedesktop.login1.Manager " ) ) . get < std : : string > ( ) ;
2024-05-14 17:10:16 +02:00
handleDbusBlockInhibits ( value ) ;
} catch ( std : : exception & e ) { Debug : : log ( WARN , " Couldn't retrieve current systemd inhibits ({}) " , e . what ( ) ) ; }
}
2024-02-17 23:24:01 +00:00
2025-01-27 13:24:13 +00:00
if ( ! * IGNOREDBUSINHIBIT ) {
2024-05-14 17:10:16 +02:00
// attempt to register as ScreenSaver
std : : string paths [ ] = {
" /org/freedesktop/ScreenSaver " ,
" /ScreenSaver " ,
} ;
try {
2024-10-23 00:45:24 +01:00
m_sDBUSState . screenSaverServiceConnection = sdbus : : createSessionBusConnection ( sdbus : : ServiceName { " org.freedesktop.ScreenSaver " } ) ;
2024-05-14 17:10:16 +02:00
2024-10-20 23:04:37 +01:00
for ( const std : : string & path : paths ) {
2024-05-14 17:10:16 +02:00
try {
2024-10-23 00:45:24 +01:00
auto obj = sdbus : : createObject ( * m_sDBUSState . screenSaverServiceConnection , sdbus : : ObjectPath { path } ) ;
obj - > addVTable ( sdbus : : registerMethod ( " Inhibit " ) . implementedAs ( [ object = obj . get ( ) ] ( std : : string s1 , std : : string s2 ) {
return handleDbusScreensaver ( s1 , s2 , 0 , true , object - > getCurrentlyProcessedMessage ( ) . getSender ( ) ) ;
} ) ,
sdbus : : registerMethod ( " UnInhibit " ) . implementedAs ( [ object = obj . get ( ) ] ( uint32_t c ) {
handleDbusScreensaver ( " " , " " , c , false , object - > getCurrentlyProcessedMessage ( ) . getSender ( ) ) ;
} ) )
. forInterface ( sdbus : : InterfaceName { " org.freedesktop.ScreenSaver " } ) ;
2024-05-14 17:10:16 +02:00
m_sDBUSState . screenSaverObjects . push_back ( std : : move ( obj ) ) ;
} catch ( std : : exception & e ) { Debug : : log ( ERR , " Failed registering for {}, perhaps taken? \n err: {} " , path , e . what ( ) ) ; }
}
2024-10-20 23:04:37 +01:00
m_sDBUSState . screenSaverServiceConnection - > addMatch ( " type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged' " ,
2024-10-23 00:45:24 +01:00
: : handleDbusNameOwnerChanged ) ;
2025-03-30 01:29:55 +01:00
} catch ( sdbus : : Error & e ) {
if ( e . getName ( ) = = sdbus : : Error : : Name { " org.freedesktop.DBus.Error.FileExists " } ) {
Debug : : log ( ERR , " Another service is already providing the org.freedesktop.ScreenSaver interface " ) ;
Debug : : log ( ERR , " Is hypridle already running? " ) ;
} else
Debug : : log ( ERR , " Failed to connect to ScreenSaver service \n err: {} " , e . what ( ) ) ;
}
2024-05-14 17:10:16 +02:00
}
2024-10-23 00:45:24 +01:00
systemConnection . reset ( ) ;
2024-02-17 22:13:06 +00:00
}
2025-01-27 13:24:13 +00:00
void CHypridle : : handleInhibitOnDbusSleep ( bool toSleep ) {
if ( m_inhibitSleepBehavior = = SLEEP_INHIBIT_NONE | | //
m_inhibitSleepBehavior = = SLEEP_INHIBIT_LOCK_NOTIFY // Sleep inhibition handled via onLocked/onUnlocked
)
return ;
if ( ! toSleep )
inhibitSleep ( ) ;
else
uninhibitSleep ( ) ;
}
void CHypridle : : inhibitSleep ( ) {
2025-02-27 23:16:45 +00:00
if ( m_sDBUSState . sleepInhibitFd . isValid ( ) ) {
Debug : : log ( WARN , " Called inhibitSleep, but previous sleep inhibitor is still active! " ) ;
m_sDBUSState . sleepInhibitFd . reset ( ) ;
}
2025-01-27 13:24:13 +00:00
auto method = m_sDBUSState . login - > createMethodCall ( sdbus : : InterfaceName { " org.freedesktop.login1.Manager " } , sdbus : : MethodName { " Inhibit " } ) ;
method < < " sleep " ;
method < < " hypridle " ;
method < < " Hypridle wants to delay sleep until it's before_sleep handling is done. " ;
method < < " delay " ;
try {
auto reply = m_sDBUSState . login - > callMethod ( method ) ;
if ( ! reply | | ! reply . isValid ( ) ) {
Debug : : log ( ERR , " Failed to inhibit sleep " ) ;
return ;
}
if ( reply . isEmpty ( ) ) {
Debug : : log ( ERR , " Failed to inhibit sleep, empty reply " ) ;
return ;
}
2025-02-27 23:16:45 +00:00
sdbus : : UnixFd fd ;
// This calls dup on the fd, no F_DUPFD_CLOEXEC :(
// There seems to be no way to get the file descriptor out of the reply other than that.
reply > > fd ;
2025-01-27 13:24:13 +00:00
2025-02-27 23:16:45 +00:00
// Setting the O_CLOEXEC flag does not work for some reason. Instead we make our own dupe and close the one from UnixFd.
auto immidiateFD = Hyprutils : : OS : : CFileDescriptor ( fd . release ( ) ) ;
m_sDBUSState . sleepInhibitFd = immidiateFD . duplicate ( F_DUPFD_CLOEXEC ) ;
immidiateFD . reset ( ) ; // close the fd that was opened with dup
Debug : : log ( LOG , " Inhibited sleep with fd {} " , m_sDBUSState . sleepInhibitFd . get ( ) ) ;
} catch ( const std : : exception & e ) { Debug : : log ( ERR , " Failed to inhibit sleep ({}) " , e . what ( ) ) ; }
2025-01-27 13:24:13 +00:00
}
void CHypridle : : uninhibitSleep ( ) {
if ( ! m_sDBUSState . sleepInhibitFd . isValid ( ) ) {
Debug : : log ( ERR , " No sleep inhibitor fd to release " ) ;
return ;
}
Debug : : log ( LOG , " Releasing the sleep inhibitor! " ) ;
2025-02-27 23:16:45 +00:00
m_sDBUSState . sleepInhibitFd . reset ( ) ;
2025-01-27 13:24:13 +00:00
}