idle
Idle inhibitors, plus threshold and inhibit methods.
mantle.idle reads like the others (:get, :map, :on_change) but has no actions. Its
thresholds take Lua callbacks, which cannot cross to the Supervisor, so it has methods
instead. Any method, :get included, starts it.
local dimmed = state("dimmed", false)
mantle.idle:register_threshold(300, function()
dimmed:set(true)
end, function()
dimmed:set(false)
end)
State
mantle.idle:get() returns IdleState, nil before the first push. A field marked ? may be absent.
mantle.idle’s payload.
| Field | Type | Description |
|---|---|---|
inhibited | boolean | Something holds the session awake: a logind inhibitor (this shell’s included), a ScreenSaver client or a Wayland inhibitor. No threshold fires while true. |
inhibitors | IdleInhibitor[] | Holders other than this shell, ScreenSaver clients included. The compositor’s hold has an empty who; draw why then. |
IdleInhibitor
One holder blocking idle.
| Field | Type | Description |
|---|---|---|
who | string | Free-text holder name, e.g. "mpv"; draw it, never match it. |
why | string | Free-text reason, e.g. "Playing video"; often empty. |
Actions
None: read-only, so any method but get, map and on_change raises.
Methods
| Method | Contract |
|---|---|
:register_threshold(seconds, on_idle, on_resume) | Runs on_idle after seconds (1 to 4294967, whole) without input and on_resume when input returns. Returns an integer handle. If this idle period already passed seconds for another registration, on_idle runs at once |
:cancel_threshold(handle) | Drops one registration. An unknown or cancelled handle is a no-op |
:inhibit(reason) | Takes one hold on a logind idle block inhibitor. Counted: two calls need two releases |
:release_inhibit() | Releases one hold; with none held, a no-op |
| Event | Thresholds | Inhibit holds |
|---|---|---|
| In-place reload | Dropped before the config evaluates again; top-level register_threshold calls re-register. The same seconds keeps its timer and does not re-run on_idle this idle period | Kept |
| Renderer replacement | Dropped | Dropped |
Backend
| Part | Behaviour |
|---|---|
| Thresholds | ext_idle_notifier_v1 on the Supervisor’s own Wayland connection. Missing protocol, or setup over 5 s: thresholds never fire, logged once |
| Inhibit | Every hold, from any generation, shares one logind Inhibit("idle", "block") fd, closed when the last hold goes |
| ScreenSaver | Hosts org.freedesktop.ScreenSaver when the name is free. A browser’s video hold arrives here, directly or through xdg-desktop-portal, and takes the same fd. A client that leaves the bus loses its holds |
| Gate | Mantle, not logind, acts on idle, so it honours inhibitors itself. While logind’s BlockInhibited names idle, idled thresholds get on_resume and none fire; on release, ones still idle get on_idle again |
| Compositor holds | A Wayland idle inhibitor shows when the shortest threshold’s input-only twin fires and the normal notification does not, so it needs a registered threshold and an idle seat. It sets inhibited and adds one holder with an empty who |
How do I…
Keep the screen awake (caffeine)
The hold survives reloads, so named state records it:
local caffeine = state("caffeine", false)
button {
on_click = function()
if caffeine:get() then
mantle.idle:release_inhibit()
else
mantle.idle:inhibit("caffeine")
end
caffeine:set(not caffeine:get())
end,
children = {
text {
content = computed({ caffeine, mantle.idle }, function(held, idle)
if held then
return "awake (held)"
elseif idle and idle.inhibited then
return "awake (another app)"
end
return "idle allowed"
end),
},
},
}
Gotchas
| Trap | Fix |
|---|---|
register_threshold inside on_change, a timer or a click handler | Each call adds a registration that lives until the next reload. Register once at top level, or keep the handle and cancel_threshold it |
An inhibit hold that never ends | Holds survive reloads and are counted. Record the hold in a state and release once per inhibit |
| A threshold never fires while a video plays | Something holds idle off and inhibited is true. Draw inhibitors to show who |
See also: Lock screen recipe, which locks after an idle threshold.