core: implement hyprlock-lock-notify-v1 functionality (#122)

---------

Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
This commit is contained in:
Maximilian Seidler 2025-01-27 13:24:13 +00:00 committed by GitHub
parent 33ac8cae64
commit 15ca902b2c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 338 additions and 133 deletions

6
.gitignore vendored
View file

@ -1,3 +1,7 @@
.vscode/
build/
protocols/
protocols/
.clangd/
.direnv/
.cache/
compile_commands.json

View file

@ -35,13 +35,14 @@ message(STATUS "Checking deps...")
find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED)
find_package(hyprwayland-scanner 0.4.4 REQUIRED)
pkg_check_modules(
deps
REQUIRED
IMPORTED_TARGET
wayland-client
wayland-protocols
hyprlang>=0.4.0
hyprlang>=0.6.0
hyprutils>=0.2.0
sdbus-c++>=0.2.0)
@ -50,45 +51,47 @@ add_executable(hypridle ${SRCFILES})
target_link_libraries(hypridle PRIVATE rt Threads::Threads PkgConfig::deps)
# protocols
find_program(WaylandScanner NAMES wayland-scanner)
message(STATUS "Found WaylandScanner at ${WaylandScanner}")
execute_process(
COMMAND pkg-config --variable=pkgdatadir wayland-protocols
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE WAYLAND_PROTOCOLS_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}")
pkg_get_variable(WAYLAND_SCANNER_PKGDATA_DIR wayland-scanner pkgdatadir)
message(STATUS "Found wayland-scanner pkgdatadir at ${WAYLAND_SCANNER_PKGDATA_DIR}")
function(protocol protoPath protoName external)
pkg_check_modules(hyprland_protocols_dep REQUIRED IMPORTED_TARGET hyprland-protocols>=0.6.0)
pkg_get_variable(HYPRLAND_PROTOCOLS hyprland-protocols pkgdatadir)
message(STATUS "Found hyprland-protocols at ${HYPRLAND_PROTOCOLS}")
function(protocolnew protoPath protoName external)
if(external)
execute_process(
COMMAND ${WaylandScanner} client-header ${protoPath}
protocols/${protoName}-protocol.h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
execute_process(
COMMAND ${WaylandScanner} private-code ${protoPath}
protocols/${protoName}-protocol.c
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
target_sources(hypridle PRIVATE protocols/${protoName}-protocol.c)
set(path ${protoPath})
else()
execute_process(
COMMAND
${WaylandScanner} client-header ${WAYLAND_PROTOCOLS_DIR}/${protoPath}
protocols/${protoName}-protocol.h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
execute_process(
COMMAND
${WaylandScanner} private-code ${WAYLAND_PROTOCOLS_DIR}/${protoPath}
protocols/${protoName}-protocol.c
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
target_sources(hypridle PRIVATE protocols/${protoName}-protocol.c)
set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath})
endif()
message(STATUS "Full proto path: ${path}")
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}.cpp
${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp
COMMAND hyprwayland-scanner --client ${path}/${protoName}.xml
${CMAKE_SOURCE_DIR}/protocols/
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
target_sources(hypridle PRIVATE protocols/${protoName}.cpp
protocols/${protoName}.hpp)
endfunction()
function(protocolWayland)
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp
${CMAKE_SOURCE_DIR}/protocols/wayland.hpp
COMMAND hyprwayland-scanner --wayland-enums --client
${WAYLAND_SCANNER_PKGDATA_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
target_sources(hypridle PRIVATE protocols/wayland.cpp protocols/wayland.hpp)
endfunction()
make_directory(${CMAKE_SOURCE_DIR}/protocols) # we don't ship any custom ones so
# the dir won't be there
protocol("staging/ext-idle-notify/ext-idle-notify-v1.xml" "ext-idle-notify-v1"
false)
protocolwayland()
protocolnew("staging/ext-idle-notify" "ext-idle-notify-v1" false)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-lock-notify-v1" true)
# Installation
install(TARGETS hypridle)

View file

@ -36,6 +36,7 @@ will make those events ignored.
- wayland-protocols
- hyprlang >= 0.4.0
- sdbus-c++
- hyprwayland-scanner
## Building & Installation

View file

@ -1,5 +1,28 @@
{
"nodes": {
"hyprland-protocols": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"systems": [
"systems"
]
},
"locked": {
"lastModified": 1737556638,
"narHash": "sha256-laKgI3mr2qz6tas/q3tuGPxMdsGhBi/w+HO+hO2f1AY=",
"owner": "hyprwm",
"repo": "hyprland-protocols",
"rev": "4c75dd5c015c8a0e5a34c6d02a018a650f57feb5",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprland-protocols",
"type": "github"
}
},
"hyprlang": {
"inputs": {
"hyprutils": [
@ -13,6 +36,8 @@
]
},
"locked": {
"lastModified": 1734364628,
"narHash": "sha256-ii8fzJfI953n/EmIxVvq64ZAwhvwuuPHWfGd61/mJG8=",
"lastModified": 1737634606,
"narHash": "sha256-W7W87Cv6wqZ9PHegI6rH1+ve3zJPiyevMFf0/HwdbCQ=",
"owner": "hyprwm",
@ -49,6 +74,29 @@
"type": "github"
}
},
"hyprwayland-scanner": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"systems": [
"systems"
]
},
"locked": {
"lastModified": 1735493474,
"narHash": "sha256-fktzv4NaqKm94VAkAoVqO/nqQlw+X0/tJJNAeCSfzK4=",
"owner": "hyprwm",
"repo": "hyprwayland-scanner",
"rev": "de913476b59ee88685fdc018e77b8f6637a2ae0b",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprwayland-scanner",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1737469691,
@ -67,8 +115,10 @@
},
"root": {
"inputs": {
"hyprland-protocols": "hyprland-protocols",
"hyprlang": "hyprlang",
"hyprutils": "hyprutils",
"hyprwayland-scanner": "hyprwayland-scanner",
"nixpkgs": "nixpkgs",
"systems": "systems"
}

View file

@ -17,6 +17,18 @@
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
};
hyprland-protocols = {
url = "github:hyprwm/hyprland-protocols";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
};
hyprwayland-scanner = {
url = "github:hyprwm/hyprwayland-scanner";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
};
};
outputs = {

View file

@ -1,11 +1,13 @@
{
lib,
stdenv,
cmake,
pkg-config,
hyprland-protocols,
hyprlang,
hyprutils,
hyprwayland-scanner,
lib,
pkg-config,
sdbus-cpp,
stdenv,
systemd,
wayland,
wayland-protocols,
@ -19,11 +21,13 @@ stdenv.mkDerivation {
nativeBuildInputs = [
cmake
hyprwayland-scanner
pkg-config
wayland-scanner
];
buildInputs = [
hyprland-protocols
hyprlang
hyprutils
sdbus-cpp

View file

@ -13,8 +13,10 @@ in {
default = inputs.self.overlays.hypridle;
hypridle = lib.composeManyExtensions [
inputs.hyprland-protocols.overlays.default
inputs.hyprlang.overlays.default
inputs.hyprutils.overlays.default
inputs.hyprwayland-scanner.overlays.default
inputs.self.overlays.sdbuscpp
(final: prev: {
hypridle = prev.callPackage ./default.nix {

View file

@ -23,10 +23,13 @@ void CConfigManager::init() {
m_config.addConfigValue("general:lock_cmd", Hyprlang::STRING{""});
m_config.addConfigValue("general:unlock_cmd", Hyprlang::STRING{""});
m_config.addConfigValue("general:on_lock_cmd", Hyprlang::STRING{""});
m_config.addConfigValue("general:on_unlock_cmd", Hyprlang::STRING{""});
m_config.addConfigValue("general:before_sleep_cmd", Hyprlang::STRING{""});
m_config.addConfigValue("general:after_sleep_cmd", Hyprlang::STRING{""});
m_config.addConfigValue("general:ignore_dbus_inhibit", Hyprlang::INT{0});
m_config.addConfigValue("general:ignore_systemd_inhibit", Hyprlang::INT{0});
m_config.addConfigValue("general:inhibit_sleep", Hyprlang::INT{2});
m_config.commence();
@ -77,11 +80,3 @@ Hyprlang::CParseResult CConfigManager::postParse() {
std::vector<CConfigManager::STimeoutRule> CConfigManager::getRules() {
return m_vRules;
}
std::string CConfigManager::getOnTimeoutCommand() {
return m_vRules.front().onTimeout;
}
void* const* CConfigManager::getValuePtr(const std::string& name) {
return m_config.getConfigValuePtr(name.c_str())->getDataStaticPtr();
}

View file

@ -18,9 +18,12 @@ class CConfigManager {
std::string onResume = "";
};
std::string getOnTimeoutCommand();
std::vector<STimeoutRule> getRules();
void* const* getValuePtr(const std::string& name);
template <typename T>
Hyprlang::CSimpleConfigValue<T> getValue(const std::string& name) {
return Hyprlang::CSimpleConfigValue<T>(&m_config, name.c_str());
}
private:
Hyprlang::CConfig m_config;

View file

@ -18,36 +18,32 @@ CHypridle::CHypridle() {
}
}
void handleGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) {
g_pHypridle->onGlobal(data, registry, name, interface, version);
}
void handleGlobalRemove(void* data, struct wl_registry* registry, uint32_t name) {
g_pHypridle->onGlobalRemoved(data, registry, name);
}
inline const wl_registry_listener registryListener = {
.global = handleGlobal,
.global_remove = handleGlobalRemove,
};
void handleIdled(void* data, ext_idle_notification_v1* ext_idle_notification_v1) {
g_pHypridle->onIdled((CHypridle::SIdleListener*)data);
}
void handleResumed(void* data, ext_idle_notification_v1* ext_idle_notification_v1) {
g_pHypridle->onResumed((CHypridle::SIdleListener*)data);
}
inline const ext_idle_notification_v1_listener idleListener = {
.idled = handleIdled,
.resumed = handleResumed,
};
void CHypridle::run() {
m_sWaylandState.registry = wl_display_get_registry(m_sWaylandState.display);
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);
wl_registry_add_listener(m_sWaylandState.registry, &registryListener, nullptr);
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);
}
});
m_sWaylandState.registry->setGlobalRemove([](CCWlRegistry* r, uint32_t name) { Debug::log(LOG, " | removed iface {}", name); });
wl_display_roundtrip(m_sWaylandState.display);
@ -62,17 +58,26 @@ void CHypridle::run() {
Debug::log(LOG, "found {} rules", RULES.size());
for (size_t i = 0; i < RULES.size(); ++i) {
auto& l = m_sWaylandIdleState.listeners[i];
const auto& r = RULES[i];
l.notification = ext_idle_notifier_v1_get_idle_notification(m_sWaylandIdleState.notifier, r.timeout * 1000 /* ms */, m_sWaylandState.seat);
l.onRestore = r.onResume;
l.onTimeout = r.onTimeout;
auto& l = m_sWaylandIdleState.listeners[i];
const auto& r = RULES[i];
l.onRestore = r.onResume;
l.onTimeout = r.onTimeout;
ext_idle_notification_v1_add_listener(l.notification, &idleListener, &l);
l.notification = makeShared<CCExtIdleNotificationV1>(m_sWaylandIdleState.notifier->sendGetIdleNotification(r.timeout * 1000 /* ms */, m_sWaylandState.seat->resource()));
l.notification->setData(&m_sWaylandIdleState.listeners[i]);
l.notification->setIdled([this](CCExtIdleNotificationV1* n) { onIdled((CHypridle::SIdleListener*)n->data()); });
l.notification->setResumed([this](CCExtIdleNotificationV1* n) { onResumed((CHypridle::SIdleListener*)n->data()); });
}
wl_display_roundtrip(m_sWaylandState.display);
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(); });
}
Debug::log(LOG, "wayland done, registering dbus");
try {
@ -82,7 +87,46 @@ void CHypridle::run() {
exit(1);
}
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;
}
setupDBUS();
if (m_inhibitSleepBehavior != SLEEP_INHIBIT_NONE)
inhibitSleep();
enterEventLoop();
}
@ -187,28 +231,6 @@ void CHypridle::enterEventLoop() {
Debug::log(ERR, "[core] Terminated");
}
void CHypridle::onGlobal(void* data, struct wl_registry* registry, 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 = (ext_idle_notifier_v1*)wl_registry_bind(registry, name, &ext_idle_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 = (wl_seat*)wl_registry_bind(registry, name, &wl_seat_interface, version);
Debug::log(LOG, " > Bound to {} v{}", IFACE, version);
}
}
void CHypridle::onGlobalRemoved(void* data, struct wl_registry* registry, uint32_t name) {
;
}
static void spawn(const std::string& args) {
Debug::log(LOG, "Executing {}", args);
@ -310,17 +332,44 @@ void CHypridle::onInhibit(bool lock) {
auto& l = m_sWaylandIdleState.listeners[i];
const auto& r = RULES[i];
ext_idle_notification_v1_destroy(l.notification);
l.notification->sendDestroy();
l.notification = ext_idle_notifier_v1_get_idle_notification(m_sWaylandIdleState.notifier, r.timeout * 1000 /* ms */, m_sWaylandState.seat);
l.notification =
makeShared<CCExtIdleNotificationV1>(m_sWaylandIdleState.notifier->sendGetIdleNotification(r.timeout * 1000 /* ms */, m_sWaylandState.seat->resource()));
l.notification->setData(&m_sWaylandIdleState.listeners[i]);
ext_idle_notification_v1_add_listener(l.notification, &idleListener, &l);
l.notification->setIdled([this](CCExtIdleNotificationV1* n) { onIdled((CHypridle::SIdleListener*)n->data()); });
l.notification->setResumed([this](CCExtIdleNotificationV1* n) { onResumed((CHypridle::SIdleListener*)n->data()); });
}
}
Debug::log(LOG, "Inhibit locks: {}", m_iInhibitLocks);
}
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);
}
CHypridle::SDbusInhibitCookie CHypridle::getDbusInhibitCookie(uint32_t cookie) {
for (auto& c : m_sDBUSState.inhibitCookies) {
if (c.cookie == cookie)
@ -358,8 +407,8 @@ bool CHypridle::unregisterDbusInhibitCookies(const std::string& ownerID) {
static void handleDbusLogin(sdbus::Message msg) {
// lock & unlock
static auto* const PLOCKCMD = (Hyprlang::STRING const*)g_pConfigManager->getValuePtr("general:lock_cmd");
static auto* const PUNLOCKCMD = (Hyprlang::STRING const*)g_pConfigManager->getValuePtr("general:unlock_cmd");
static const auto LOCKCMD = g_pConfigManager->getValue<Hyprlang::STRING>("general:lock_cmd");
static const auto UNLOCKCMD = g_pConfigManager->getValue<Hyprlang::STRING>("general:unlock_cmd");
Debug::log(LOG, "Got dbus .Session");
@ -367,16 +416,16 @@ static void handleDbusLogin(sdbus::Message msg) {
if (MEMBER == "Lock") {
Debug::log(LOG, "Got Lock from dbus");
if (!std::string{*PLOCKCMD}.empty()) {
Debug::log(LOG, "Locking with {}", *PLOCKCMD);
spawn(*PLOCKCMD);
if (!std::string{*LOCKCMD}.empty()) {
Debug::log(LOG, "Locking with {}", *LOCKCMD);
spawn(*LOCKCMD);
}
} else if (MEMBER == "Unlock") {
Debug::log(LOG, "Got Unlock from dbus");
if (!std::string{*PUNLOCKCMD}.empty()) {
Debug::log(LOG, "Locking with {}", *PUNLOCKCMD);
spawn(*PUNLOCKCMD);
if (!std::string{*UNLOCKCMD}.empty()) {
Debug::log(LOG, "Locking with {}", *UNLOCKCMD);
spawn(*UNLOCKCMD);
}
}
}
@ -390,18 +439,21 @@ static void handleDbusSleep(sdbus::Message msg) {
bool toSleep = true;
msg >> toSleep;
static auto* const PSLEEPCMD = (Hyprlang::STRING const*)g_pConfigManager->getValuePtr("general:before_sleep_cmd");
static auto* const PAFTERSLEEPCMD = (Hyprlang::STRING const*)g_pConfigManager->getValuePtr("general:after_sleep_cmd");
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");
Debug::log(LOG, "Got PrepareForSleep from dbus with sleep {}", toSleep);
std::string cmd = toSleep ? *PSLEEPCMD : *PAFTERSLEEPCMD;
std::string cmd = toSleep ? *SLEEPCMD : *AFTERSLEEPCMD;
if (cmd.empty())
return;
if (!toSleep)
g_pHypridle->handleInhibitOnDbusSleep(toSleep);
Debug::log(LOG, "Running: {}", cmd);
spawn(cmd);
if (!cmd.empty())
spawn(cmd);
if (toSleep)
g_pHypridle->handleInhibitOnDbusSleep(toSleep);
}
void handleDbusBlockInhibits(const std::string& inhibits) {
@ -484,8 +536,8 @@ static void handleDbusNameOwnerChanged(sdbus::Message msg) {
}
void CHypridle::setupDBUS() {
static auto const IGNORE_DBUS_INHIBIT = **(Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:ignore_dbus_inhibit");
static auto const IGNORE_SYSTEMD_INHIBIT = **(Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:ignore_systemd_inhibit");
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");
auto systemConnection = sdbus::createSystemBusConnection();
auto proxy = sdbus::createProxy(*systemConnection, sdbus::ServiceName{"org.freedesktop.login1"}, sdbus::ObjectPath{"/org/freedesktop/login1"});
@ -496,11 +548,12 @@ void CHypridle::setupDBUS() {
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);
m_sDBUSState.login = sdbus::createProxy(*m_sDBUSState.connection, sdbus::ServiceName{"org.freedesktop.login1"}, sdbus::ObjectPath{"/org/freedesktop/login1"});
} catch (std::exception& e) { Debug::log(WARN, "Couldn't connect to logind service ({})", e.what()); }
Debug::log(LOG, "Using dbus path {}", path.c_str());
if (!IGNORE_SYSTEMD_INHIBIT) {
if (!*IGNORESYSTEMDINHIBIT) {
m_sDBUSState.connection->addMatch("type='signal',path='/org/freedesktop/login1',interface='org.freedesktop.DBus.Properties'", ::handleDbusBlockInhibitsPropertyChanged);
try {
@ -509,7 +562,7 @@ void CHypridle::setupDBUS() {
} catch (std::exception& e) { Debug::log(WARN, "Couldn't retrieve current systemd inhibits ({})", e.what()); }
}
if (!IGNORE_DBUS_INHIBIT) {
if (!*IGNOREDBUSINHIBIT) {
// attempt to register as ScreenSaver
std::string paths[] = {
"/org/freedesktop/ScreenSaver",
@ -542,3 +595,52 @@ void CHypridle::setupDBUS() {
systemConnection.reset();
}
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() {
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;
}
reply >> m_sDBUSState.sleepInhibitFd;
Debug::log(TRACE, "Inhibited sleep with fd {}", m_sDBUSState.sleepInhibitFd.get());
} catch (const std::exception& e) { Debug::log(ERR, "Failed to inhibit sleep ({})", e.what()); }
Debug::log(LOG, "Inhibited sleep!");
}
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!");
close(m_sDBUSState.sleepInhibitFd.release());
}

View file

@ -2,20 +2,23 @@
#include <memory>
#include <vector>
#include <wayland-client.h>
#include <sdbus-c++/sdbus-c++.h>
#include <condition_variable>
#include "ext-idle-notify-v1-protocol.h"
#include "wayland.hpp"
#include "ext-idle-notify-v1.hpp"
#include "hyprland-lock-notify-v1.hpp"
#include "../defines.hpp"
class CHypridle {
public:
CHypridle();
struct SIdleListener {
ext_idle_notification_v1* notification = nullptr;
std::string onTimeout = "";
std::string onRestore = "";
SP<CCExtIdleNotificationV1> notification = nullptr;
std::string onTimeout = "";
std::string onRestore = "";
};
struct SDbusInhibitCookie {
@ -33,27 +36,43 @@ class CHypridle {
void onInhibit(bool lock);
void onLocked();
void onUnlocked();
SDbusInhibitCookie getDbusInhibitCookie(uint32_t cookie);
void registerDbusInhibitCookie(SDbusInhibitCookie& cookie);
bool unregisterDbusInhibitCookie(const SDbusInhibitCookie& cookie);
bool unregisterDbusInhibitCookies(const std::string& ownerID);
void handleInhibitOnDbusSleep(bool toSleep);
void inhibitSleep();
void uninhibitSleep();
private:
void setupDBUS();
void enterEventLoop();
bool m_bTerminate = false;
bool isIdled = false;
bool m_isLocked = false;
int64_t m_iInhibitLocks = 0;
enum {
SLEEP_INHIBIT_NONE,
SLEEP_INHIBIT_NORMAL,
SLEEP_INHIBIT_LOCK_NOTIFY,
} m_inhibitSleepBehavior;
struct {
wl_display* display = nullptr;
wl_registry* registry = nullptr;
wl_seat* seat = nullptr;
wl_display* display = nullptr;
SP<CCWlRegistry> registry = nullptr;
SP<CCWlSeat> seat = nullptr;
SP<CCHyprlandLockNotifierV1> lockNotifier = nullptr;
SP<CCHyprlandLockNotificationV1> lockNotification = nullptr;
} m_sWaylandState;
struct {
ext_idle_notifier_v1* notifier = nullptr;
SP<CCExtIdleNotifierV1> notifier = nullptr;
std::vector<SIdleListener> listeners;
} m_sWaylandIdleState;
@ -61,8 +80,10 @@ class CHypridle {
struct {
std::unique_ptr<sdbus::IConnection> connection;
std::unique_ptr<sdbus::IConnection> screenSaverServiceConnection;
std::unique_ptr<sdbus::IProxy> login;
std::vector<std::unique_ptr<sdbus::IObject>> screenSaverObjects;
std::vector<SDbusInhibitCookie> inhibitCookies;
sdbus::UnixFd sleepInhibitFd;
} m_sDBUSState;
struct {

8
src/defines.hpp Normal file
View file

@ -0,0 +1,8 @@
#include "wayland.hpp"
#include "ext-idle-notify-v1.hpp"
#include "hyprland-lock-notify-v1.hpp"
#include <hyprutils/memory/WeakPtr.hpp>
using namespace Hyprutils::Memory;
#define SP CSharedPointer
#define WP CWeakPointer