From 59ff7b2f891d06f4097128faf7027a3863542167 Mon Sep 17 00:00:00 2001 From: MithicSpirit Date: Sun, 19 Oct 2025 07:54:27 -0400 Subject: [PATCH] dispatchers: add forceidle (#11922) The forceidle dispatcher resets all ext-idle-notify timers as if the user had been idle for the specified number of seconds. If a notification has already fired, but would now be set with a nonzero delay, then it is reset. Conversely, if a timer has not yet fired, but would now be set to a nonpositive delay, then it is immediately fired. This process ignores any existing inhibitors, but timers are otherwise reset as normal if any new inhibitors are created or destroyed. --- src/managers/KeybindManager.cpp | 15 +++++++++++ src/managers/KeybindManager.hpp | 1 + src/protocols/IdleNotify.cpp | 45 ++++++++++++++++++++++++--------- src/protocols/IdleNotify.hpp | 8 ++++-- 4 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index c9008c2f..d04d6ba7 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -4,6 +4,7 @@ #include "../protocols/LayerShell.hpp" #include "../protocols/ShortcutsInhibit.hpp" #include "../protocols/GlobalShortcuts.hpp" +#include "../protocols/IdleNotify.hpp" #include "../protocols/core/DataDevice.hpp" #include "../render/decorations/CHyprGroupBarDecoration.hpp" #include "KeybindManager.hpp" @@ -143,6 +144,7 @@ CKeybindManager::CKeybindManager() { m_dispatchers["event"] = event; m_dispatchers["global"] = global; m_dispatchers["setprop"] = setProp; + m_dispatchers["forceidle"] = forceIdle; m_scrollTimer.reset(); @@ -3257,6 +3259,19 @@ SDispatchResult CKeybindManager::setProp(std::string args) { return {}; } +SDispatchResult CKeybindManager::forceIdle(std::string args) { + std::optional duration = getPlusMinusKeywordResult(args, 0); + + if (!duration.has_value()) { + Debug::log(ERR, "Duration invalid in forceIdle!"); + return {.success = false, .error = "Duration invalid in forceIdle!"}; + } + + PROTO::idle->setTimers(duration.value() * 1000.0); + + return {}; +} + SDispatchResult CKeybindManager::sendkeystate(std::string args) { // args=[,WINDOW_RULES] const auto ARGS = CVarList(args, 4); diff --git a/src/managers/KeybindManager.hpp b/src/managers/KeybindManager.hpp index 45742e78..592588b5 100644 --- a/src/managers/KeybindManager.hpp +++ b/src/managers/KeybindManager.hpp @@ -237,6 +237,7 @@ class CKeybindManager { static SDispatchResult global(std::string); static SDispatchResult event(std::string); static SDispatchResult setProp(std::string); + static SDispatchResult forceIdle(std::string); friend class CCompositor; friend class CInputManager; diff --git a/src/protocols/IdleNotify.cpp b/src/protocols/IdleNotify.cpp index 0ebe24e3..4122bf24 100644 --- a/src/protocols/IdleNotify.cpp +++ b/src/protocols/IdleNotify.cpp @@ -21,7 +21,7 @@ CExtIdleNotification::CExtIdleNotification(SP resource_, m_timer = makeShared(std::nullopt, onTimer, this); g_pEventLoopManager->addTimer(m_timer); - updateTimer(); + update(); LOGM(LOG, "Registered idle-notification for {}ms", timeoutMs_); } @@ -35,24 +35,39 @@ bool CExtIdleNotification::good() { return m_resource->resource(); } -void CExtIdleNotification::updateTimer() { - if (PROTO::idle->isInhibited && m_obeyInhibitors) - m_timer->updateTimeout(std::nullopt); - else - m_timer->updateTimeout(std::chrono::milliseconds(m_timeoutMs)); +void CExtIdleNotification::update(uint32_t elapsedMs) { + m_timer->updateTimeout(std::nullopt); + + if (elapsedMs == 0 && PROTO::idle->isInhibited && m_obeyInhibitors) { + reset(); + return; + } + + if (m_timeoutMs > elapsedMs) { + reset(); + m_timer->updateTimeout(std::chrono::milliseconds(m_timeoutMs - elapsedMs)); + } else + onTimerFired(); +} + +void CExtIdleNotification::update() { + update(0); } void CExtIdleNotification::onTimerFired() { + if (m_idled) + return; + m_resource->sendIdled(); m_idled = true; } -void CExtIdleNotification::onActivity() { - if (m_idled) - m_resource->sendResumed(); +void CExtIdleNotification::reset() { + if (!m_idled) + return; + m_resource->sendResumed(); m_idled = false; - updateTimer(); } bool CExtIdleNotification::inhibitorsAreObeyed() const { @@ -96,7 +111,7 @@ void CIdleNotifyProtocol::onGetNotification(CExtIdleNotifierV1* pMgr, uint32_t i void CIdleNotifyProtocol::onActivity() { for (auto const& n : m_notifications) { - n->onActivity(); + n->update(); } } @@ -104,6 +119,12 @@ void CIdleNotifyProtocol::setInhibit(bool inhibited) { isInhibited = inhibited; for (auto const& n : m_notifications) { if (n->inhibitorsAreObeyed()) - n->onActivity(); + n->update(); + } +} + +void CIdleNotifyProtocol::setTimers(uint32_t elapsedMs) { + for (auto const& n : m_notifications) { + n->update(elapsedMs); } } diff --git a/src/protocols/IdleNotify.hpp b/src/protocols/IdleNotify.hpp index 02b59e65..0e0433a4 100644 --- a/src/protocols/IdleNotify.hpp +++ b/src/protocols/IdleNotify.hpp @@ -14,7 +14,6 @@ class CExtIdleNotification { bool good(); void onTimerFired(); - void onActivity(); bool inhibitorsAreObeyed() const; @@ -26,7 +25,11 @@ class CExtIdleNotification { bool m_idled = false; bool m_obeyInhibitors = false; - void updateTimer(); + void reset(); + void update(); + void update(uint32_t elapsedMs); + + friend class CIdleNotifyProtocol; }; class CIdleNotifyProtocol : public IWaylandProtocol { @@ -37,6 +40,7 @@ class CIdleNotifyProtocol : public IWaylandProtocol { void onActivity(); void setInhibit(bool inhibited); + void setTimers(uint32_t elapsedMs); private: void onManagerResourceDestroy(wl_resource* res);