mirror of
https://github.com/hyprwm/hypridle.git
synced 2026-05-07 13:48:00 +02:00
Merge 5a93bb3089 into ff41dd6d94
This commit is contained in:
commit
5f63202191
5 changed files with 101 additions and 15 deletions
|
|
@ -25,6 +25,8 @@ listener {
|
|||
timeout = 500 # in seconds
|
||||
on-timeout = notify-send "You are idle!" # command to run when timeout has passed
|
||||
on-resume = notify-send "Welcome back!" # command to run when activity is detected after timeout has fired.
|
||||
condition_cmd = # if set, run this command before on-timeout. Exit 0 = proceed, non-zero = defer
|
||||
condition_retry = 0 # retry interval in seconds when condition_cmd defers (default: 0, no retry)
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@ void CConfigManager::init() {
|
|||
m_config.addSpecialConfigValue("listener", "on-timeout", Hyprlang::STRING{""});
|
||||
m_config.addSpecialConfigValue("listener", "on-resume", Hyprlang::STRING{""});
|
||||
m_config.addSpecialConfigValue("listener", "ignore_inhibit", Hyprlang::INT{0});
|
||||
m_config.addSpecialConfigValue("listener", "condition_cmd", Hyprlang::STRING{""});
|
||||
m_config.addSpecialConfigValue("listener", "condition_retry", Hyprlang::INT{0});
|
||||
|
||||
m_config.addConfigValue("general:lock_cmd", Hyprlang::STRING{""});
|
||||
m_config.addConfigValue("general:unlock_cmd", Hyprlang::STRING{""});
|
||||
|
|
@ -109,6 +111,8 @@ Hyprlang::CParseResult CConfigManager::postParse() {
|
|||
rule.onResume = std::any_cast<Hyprlang::STRING>(m_config.getSpecialConfigValue("listener", "on-resume", k.c_str()));
|
||||
|
||||
rule.ignoreInhibit = std::any_cast<Hyprlang::INT>(m_config.getSpecialConfigValue("listener", "ignore_inhibit", k.c_str()));
|
||||
rule.conditionCmd = std::any_cast<Hyprlang::STRING>(m_config.getSpecialConfigValue("listener", "condition_cmd", k.c_str()));
|
||||
rule.conditionRetry = std::any_cast<Hyprlang::INT>(m_config.getSpecialConfigValue("listener", "condition_retry", k.c_str()));
|
||||
|
||||
if (timeout == -1) {
|
||||
result.setError("Category has a missing timeout setting");
|
||||
|
|
@ -119,8 +123,8 @@ Hyprlang::CParseResult CConfigManager::postParse() {
|
|||
}
|
||||
|
||||
for (auto& r : m_vRules) {
|
||||
Debug::log(LOG, "Registered timeout rule for {}s:\n on-timeout: {}\n on-resume: {}\n ignore_inhibit: {}", r.timeout, r.onTimeout, r.onResume,
|
||||
r.ignoreInhibit);
|
||||
Debug::log(LOG, "Registered timeout rule for {}s:\n on-timeout: {}\n on-resume: {}\n ignore_inhibit: {}\n condition_cmd: {}\n condition_retry: {}",
|
||||
r.timeout, r.onTimeout, r.onResume, r.ignoreInhibit, r.conditionCmd, r.conditionRetry);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -14,10 +14,12 @@ class CConfigManager {
|
|||
void init();
|
||||
|
||||
struct STimeoutRule {
|
||||
uint64_t timeout = 0;
|
||||
std::string onTimeout = "";
|
||||
std::string onResume = "";
|
||||
bool ignoreInhibit = false;
|
||||
uint64_t timeout = 0;
|
||||
std::string onTimeout = "";
|
||||
std::string onResume = "";
|
||||
bool ignoreInhibit = false;
|
||||
std::string conditionCmd = "";
|
||||
int64_t conditionRetry = 0;
|
||||
};
|
||||
|
||||
std::vector<STimeoutRule> getRules();
|
||||
|
|
|
|||
|
|
@ -65,9 +65,11 @@ void CHypridle::run() {
|
|||
for (size_t i = 0; i < RULES.size(); ++i) {
|
||||
auto& l = m_sWaylandIdleState.listeners[i];
|
||||
const auto& r = RULES[i];
|
||||
l.onRestore = r.onResume;
|
||||
l.onTimeout = r.onTimeout;
|
||||
l.ignoreInhibit = r.ignoreInhibit;
|
||||
l.onRestore = r.onResume;
|
||||
l.onTimeout = r.onTimeout;
|
||||
l.ignoreInhibit = r.ignoreInhibit;
|
||||
l.conditionCmd = r.conditionCmd;
|
||||
l.conditionRetry = r.conditionRetry;
|
||||
|
||||
if (*IGNOREWAYLANDINHIBIT || r.ignoreInhibit)
|
||||
l.notification =
|
||||
|
|
@ -142,6 +144,45 @@ void CHypridle::run() {
|
|||
enterEventLoop();
|
||||
}
|
||||
|
||||
static int64_t nowMonotonic() {
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
static bool runConditionCmd(const std::string& cmd) {
|
||||
Debug::log(LOG, "Running condition_cmd: {}", cmd);
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
Debug::log(ERR, "Failed to fork for condition_cmd");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
execl("/bin/sh", "/bin/sh", "-c", cmd.c_str(), nullptr);
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
// Wait with 5s timeout
|
||||
for (int i = 0; i < 50; i++) {
|
||||
int status = 0;
|
||||
pid_t ret = waitpid(pid, &status, WNOHANG);
|
||||
if (ret > 0) {
|
||||
int exitCode = WIFEXITED(status) ? WEXITSTATUS(status) : 1;
|
||||
Debug::log(LOG, "condition_cmd exited with {}", exitCode);
|
||||
return exitCode == 0;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
// Timeout — kill and return false
|
||||
Debug::log(WARN, "condition_cmd timed out after 5s, killing");
|
||||
kill(pid, SIGKILL);
|
||||
waitpid(pid, nullptr, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void spawn(const std::string& args);
|
||||
|
||||
void CHypridle::enterEventLoop() {
|
||||
|
||||
nfds_t pollfdsCount = m_sDBUSState.screenSaverServiceConnection ? 3 : 2;
|
||||
|
|
@ -196,7 +237,7 @@ void CHypridle::enterEventLoop() {
|
|||
|
||||
std::unique_lock lk(m_sEventLoopInternals.loopMutex);
|
||||
if (!m_sEventLoopInternals.shouldProcess) // avoid a lock if a thread managed to request something already since we .unlock()ed
|
||||
m_sEventLoopInternals.loopSignal.wait(lk, [this] { return m_sEventLoopInternals.shouldProcess == true; }); // wait for events
|
||||
m_sEventLoopInternals.loopSignal.wait_for(lk, std::chrono::seconds(5), [this] { return m_sEventLoopInternals.shouldProcess == true; }); // wait for events or timeout (for condition_cmd retries)
|
||||
|
||||
m_sEventLoopInternals.loopRequestMutex.lock(); // lock incoming events
|
||||
|
||||
|
|
@ -238,6 +279,24 @@ void CHypridle::enterEventLoop() {
|
|||
ret = wl_display_dispatch_pending(m_sWaylandState.display);
|
||||
wl_display_flush(m_sWaylandState.display);
|
||||
} while (ret > 0);
|
||||
|
||||
// Check condition_cmd retries for pending listeners
|
||||
const auto now = nowMonotonic();
|
||||
for (auto& l : m_sWaylandIdleState.listeners) {
|
||||
if (!l.conditionPending || now < l.conditionRetryAt)
|
||||
continue;
|
||||
|
||||
Debug::log(LOG, "Retrying condition_cmd for rule {:x}", (uintptr_t)&l);
|
||||
if (runConditionCmd(l.conditionCmd)) {
|
||||
l.conditionPending = false;
|
||||
l.onTimeoutFired = true;
|
||||
Debug::log(LOG, "Condition met, running {}", l.onTimeout);
|
||||
spawn(l.onTimeout);
|
||||
} else {
|
||||
l.conditionRetryAt = now + l.conditionRetry;
|
||||
Debug::log(LOG, "Condition still not met, retrying in {}s", l.conditionRetry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug::log(ERR, "[core] Terminated");
|
||||
|
|
@ -268,6 +327,20 @@ void CHypridle::onIdled(SIdleListener* pListener) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Check condition_cmd before firing on-timeout
|
||||
if (!pListener->conditionCmd.empty()) {
|
||||
if (!runConditionCmd(pListener->conditionCmd)) {
|
||||
if (pListener->conditionRetry > 0) {
|
||||
Debug::log(LOG, "condition_cmd blocked on-timeout, retrying in {}s", pListener->conditionRetry);
|
||||
pListener->conditionPending = true;
|
||||
pListener->conditionRetryAt = nowMonotonic() + pListener->conditionRetry;
|
||||
} else {
|
||||
Debug::log(LOG, "condition_cmd blocked on-timeout, no retry configured");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Debug::log(LOG, "Running {}", pListener->onTimeout);
|
||||
pListener->onTimeoutFired = true;
|
||||
spawn(pListener->onTimeout);
|
||||
|
|
@ -276,6 +349,7 @@ void CHypridle::onIdled(SIdleListener* pListener) {
|
|||
void CHypridle::onResumed(SIdleListener* pListener) {
|
||||
Debug::log(LOG, "Resumed: rule {:x}", (uintptr_t)pListener);
|
||||
isIdled = false;
|
||||
pListener->conditionPending = false;
|
||||
|
||||
// If on-timeout never actually executed (was inhibited), skip on-resume too
|
||||
if (!pListener->onTimeoutFired) {
|
||||
|
|
|
|||
|
|
@ -17,11 +17,15 @@ class CHypridle {
|
|||
CHypridle();
|
||||
|
||||
struct SIdleListener {
|
||||
SP<CCExtIdleNotificationV1> notification = nullptr;
|
||||
std::string onTimeout = "";
|
||||
std::string onRestore = "";
|
||||
bool ignoreInhibit = false;
|
||||
bool onTimeoutFired = false;
|
||||
SP<CCExtIdleNotificationV1> notification = nullptr;
|
||||
std::string onTimeout = "";
|
||||
std::string onRestore = "";
|
||||
bool ignoreInhibit = false;
|
||||
bool onTimeoutFired = false;
|
||||
std::string conditionCmd = "";
|
||||
int64_t conditionRetry = 0;
|
||||
bool conditionPending = false;
|
||||
int64_t conditionRetryAt = 0;
|
||||
};
|
||||
|
||||
struct SDbusInhibitCookie {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue