Linux: Idle sensing feature similar to HID idleness in macOS#56
Draft
joerybka wants to merge 9 commits into
Draft
Linux: Idle sensing feature similar to HID idleness in macOS#56joerybka wants to merge 9 commits into
joerybka wants to merge 9 commits into
Conversation
macOS uses ioreg's HIDIdleTime and pmset; Linux had no equivalent, so doctor's "(n) HID idle source" check and the daemon's sleep-eligibility gate always fell back to heartbeat-idle only. logind tracks IdleHint/IdleSinceHint from seat input activity independent of the display server, so it works identically under X11, Wayland, or a bare console with no guessing required. Query it via busctl (already a systemd dependency of this project) using Manager.GetSessionByPID, which is robust across systemd versions unlike the newer "/session/self" object-path shortcut. Added IdleDetector.os_idle_time_sec() as a platform dispatcher so callers (sleep_eligible(), status()) no longer need their own OS checks -- it senses platform.system() and delegates to the right backend. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
GetSessionByPID(getpid()) resolves the *calling process's* own session -- but the daemon runs as a systemd user service, not attached to any interactive session's cgroup, so this lookup always fails in the one context that actually matters (verified live: "PID N does not belong to any known session"). Enumerate ListSessions instead and pick the caller's own uid's seat-attached (non-headless) session. Also switch to `busctl --json=short` throughout instead of regex-scraping the human-readable table output -- more robust, and avoids ambiguity in how busctl formats multi-entry arrays. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
idle_str and logind_str differed only in their None fallback text
("unavailable" vs "not idle") but were computed unconditionally at the
top regardless of which branch actually used them. Move each into the
branch where its fallback text applies.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Extract idle_str() as a small helper -- the only thing that actually
varies between the logind and HIDIdleTime paths is the None fallback
label ("not idle" vs "unavailable"). detail/status are now computed
per-branch but the CheckResult construction itself happens once.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
detail and result_status are fully overwritten in every branch, so the nested else added indentation without sharing anything. signals_str and pmset_str are hoisted once; idle_str's fallback label is the only thing that actually varies per branch. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cosmetic reorder -- the two are mutually exclusive per-platform signals, so this doesn't change behavior. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ribe() check_n_hid_idle_source() branched on "HIDIdleTime"/"logind" string membership to pick its wording -- exactly the platform check os_idle_time_sec()'s docstring claimed callers wouldn't need. Moved that formatting into a new IdleDetector.describe() -> (detail, status) method, next to the platform dispatch that already knows about these backends. The doctor check is now a pure pass-through with zero backend-name knowledge of its own. Also tightened os_idle_time_sec()'s docstring: the "no platform checks" guarantee applies to consumers that only use idle_seconds (sleep_eligible); source_name is a diagnostic label for reporting consumers, and source-name-specific formatting belongs in this module, not a caller. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Previously picked the first uid+seat match from ListSessions and reported only that session's idle state. On real multi-seat hardware this risks reading the wrong seat's IdleHint while the user is actively working on a different seat. Now every seat-attached session for the uid is checked: overall idle requires all of them idle (any active seat means not idle), and the reported duration is the minimum across idle seats -- the most conservative estimate, since one seat may have gone idle much more recently than another. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ectly logind_idle_time_sec() was only ever called by tests -- os_idle_time_sec() (the actual dispatcher) inlines the equivalent two calls itself to avoid enumerating sessions twice. Unlike hid_idle_time_sec(), which the Darwin branch does call directly, there was no real parity to preserve by keeping a Linux method the production path never used. Retargeted its tests to _logind_aggregate_idle(), which is what os_idle_time_sec() actually calls, and dropped the redundant "session not found" case already covered by _logind_session_paths()'s own empty-list tests. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Contributor
Author
|
@CodeAbra I have left this in draft, since I am looking for feedback on whether to simplify the idle check for the more common single-console case that this would likely be deployed with. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Updates the idle sensor to read from the logind service on a Linux machine. This aims to be agnostic between X11 and Wayland.
Type of change
Affected areas
Testing
pytestpasses locallyruff check src/ tests/cleanBenchmarks
This does not touch the active path of any benchmark-eligible change.
Notes for reviewers
This change updates the idle sensor for Linux platforms to use logind, mimicking how the enriched check on macOS checks for HID idleness.
logind Differences
The underlying theory of the way that logind works on Linux and how IOHIDSystem work on macOS is different. When querying IOReg on macOS, there is no need to scan for a session that is using a console, since there is only one console to worry about. logind is predicated on multi-session, so the code must scan for all sessions using a console (seatN) and then look for the lowest idle time to return for actual comparison. In truthfulness, this is me being extremely pedantic for completeness, the likelihood of this actually happening is very low.
The current implementation could be pulled back and simplified to look for the first seat found (usually seat0), and then just use the IdleHint from that console. Given the code already has to fall through the MCP heartbeat check to start polling for idleness, that would optimise it slightly, shaving about ~300 nanoseconds from the idle check. I already have it written the way it is though, so not sure if you want me to pull that back or not.
I looked into unifying the idle approach, since they are both Unix-based, and that is actually a worse result with the same split complexity, because looking up the active terminals is different across platforms.