From 15ca902b2cb845a8a5378ec022c11a4a77155b83 Mon Sep 17 00:00:00 2001 From: Maximilian Seidler <78690852+PaideiaDilemma@users.noreply.github.com> Date: Mon, 27 Jan 2025 13:24:13 +0000 Subject: [PATCH] core: implement hyprlock-lock-notify-v1 functionality (#122) --------- Co-authored-by: Mihai Fufezan --- .gitignore | 6 +- CMakeLists.txt | 67 ++++----- README.md | 1 + flake.lock | 50 +++++++ flake.nix | 12 ++ nix/default.nix | 10 +- nix/overlays.nix | 2 + src/config/ConfigManager.cpp | 11 +- src/config/ConfigManager.hpp | 7 +- src/core/Hypridle.cpp | 258 ++++++++++++++++++++++++----------- src/core/Hypridle.hpp | 39 ++++-- src/defines.hpp | 8 ++ 12 files changed, 338 insertions(+), 133 deletions(-) create mode 100644 src/defines.hpp diff --git a/.gitignore b/.gitignore index 33419bc..c109696 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ .vscode/ build/ -protocols/ \ No newline at end of file +protocols/ +.clangd/ +.direnv/ +.cache/ +compile_commands.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 4dfb8a9..9746b92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/README.md b/README.md index e6584fc..3d07fd6 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ will make those events ignored. - wayland-protocols - hyprlang >= 0.4.0 - sdbus-c++ + - hyprwayland-scanner ## Building & Installation diff --git a/flake.lock b/flake.lock index f703faa..6471c51 100644 --- a/flake.lock +++ b/flake.lock @@ -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" } diff --git a/flake.nix b/flake.nix index 24364c7..40dbfe5 100644 --- a/flake.nix +++ b/flake.nix @@ -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 = { diff --git a/nix/default.nix b/nix/default.nix index 5238809..9e35005 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -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 diff --git a/nix/overlays.nix b/nix/overlays.nix index c3d5651..4791768 100644 --- a/nix/overlays.nix +++ b/nix/overlays.nix @@ -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 { diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 95d6854..777c60d 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -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::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(); -} diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp index 8557984..6160914 100644 --- a/src/config/ConfigManager.hpp +++ b/src/config/ConfigManager.hpp @@ -18,9 +18,12 @@ class CConfigManager { std::string onResume = ""; }; - std::string getOnTimeoutCommand(); std::vector getRules(); - void* const* getValuePtr(const std::string& name); + + template + Hyprlang::CSimpleConfigValue getValue(const std::string& name) { + return Hyprlang::CSimpleConfigValue(&m_config, name.c_str()); + } private: Hyprlang::CConfig m_config; diff --git a/src/core/Hypridle.cpp b/src/core/Hypridle.cpp index 8c85e9d..2ed1f9b 100644 --- a/src/core/Hypridle.cpp +++ b/src/core/Hypridle.cpp @@ -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((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, ®istryListener, nullptr); + if (IFACE == ext_idle_notifier_v1_interface.name) { + m_sWaylandIdleState.notifier = + makeShared((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((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((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(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(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("general:inhibit_sleep"); + static const auto SLEEPCMD = g_pConfigManager->getValue("general:before_sleep_cmd"); + static const auto LOCKCMD = g_pConfigManager->getValue("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(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("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("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("general:lock_cmd"); + static const auto UNLOCKCMD = g_pConfigManager->getValue("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("general:before_sleep_cmd"); + static const auto AFTERSLEEPCMD = g_pConfigManager->getValue("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("general:ignore_dbus_inhibit"); + static const auto IGNORESYSTEMDINHIBIT = g_pConfigManager->getValue("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()); +} diff --git a/src/core/Hypridle.hpp b/src/core/Hypridle.hpp index e5a131a..d7fc588 100644 --- a/src/core/Hypridle.hpp +++ b/src/core/Hypridle.hpp @@ -2,20 +2,23 @@ #include #include -#include #include #include -#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 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 registry = nullptr; + SP seat = nullptr; + SP lockNotifier = nullptr; + SP lockNotification = nullptr; } m_sWaylandState; struct { - ext_idle_notifier_v1* notifier = nullptr; + SP notifier = nullptr; std::vector listeners; } m_sWaylandIdleState; @@ -61,8 +80,10 @@ class CHypridle { struct { std::unique_ptr connection; std::unique_ptr screenSaverServiceConnection; + std::unique_ptr login; std::vector> screenSaverObjects; std::vector inhibitCookies; + sdbus::UnixFd sleepInhibitFd; } m_sDBUSState; struct { diff --git a/src/defines.hpp b/src/defines.hpp new file mode 100644 index 0000000..9b66def --- /dev/null +++ b/src/defines.hpp @@ -0,0 +1,8 @@ +#include "wayland.hpp" +#include "ext-idle-notify-v1.hpp" +#include "hyprland-lock-notify-v1.hpp" + +#include +using namespace Hyprutils::Memory; +#define SP CSharedPointer +#define WP CWeakPointer