Files
community-plugins/lid-guard/service.luau
T
pedroandGitHub 98e7cc1ea1 feat(lid-guard): add lid-close suspension inhibitor (#30)
* feat(lid-guard): add lid-close suspension inhibitor

* docs(lid-guard): replace plugin thumbnail
2026-07-23 23:46:04 -04:00

128 lines
3.4 KiB
Luau

--!nonstrict
-- Owns the systemd inhibitor. Widgets and shortcuts communicate with this
-- singleton through noctalia.state.
local UNIT = "noctalia-lid-guard.service"
local CHECK_COMMAND = `systemctl --user is-active --quiet {UNIT}`
local START_COMMAND = `systemctl --user reset-failed {UNIT} >/dev/null 2>&1 || true; ` .. table.concat({
`systemd-run --user --quiet --collect --unit={UNIT}`,
`--description='Noctalia Lid Guard'`,
`systemd-inhibit --what=handle-lid-switch`,
`--who='Noctalia Lid Guard'`,
`--why='Keep background jobs running while the laptop lid is closed'`,
`--mode=block sleep infinity`,
}, " ")
local STOP_COMMAND = `systemctl --user stop {UNIT}`
local active = false
local busy = false
local available = noctalia.commandExists("systemctl")
and noctalia.commandExists("systemd-run")
and noctalia.commandExists("systemd-inhibit")
and noctalia.commandExists("sleep")
local revision = 0
local function publish(errorMessage)
revision += 1
noctalia.state.set("lid_guard_status", {
active = active,
busy = busy,
available = available,
error = errorMessage or "",
revision = revision,
})
end
local function refresh(callback)
if not available then
active = false
publish(noctalia.tr("error.missing_dependencies"))
if callback then callback(false) end
return
end
noctalia.runAsync(CHECK_COMMAND, function(result)
active = result.exitCode == 0
publish()
if callback then callback(active) end
end)
end
local function finishToggle(expectedActive, result)
if result.exitCode ~= 0 then
busy = false
local detail = noctalia.string.trim(result.stderr or "")
if detail == "" then detail = noctalia.tr("error.command_failed") end
publish(detail)
noctalia.notifyError(noctalia.tr("title"), detail)
return
end
refresh(function(currentActive)
busy = false
active = currentActive
publish()
if currentActive ~= expectedActive then
noctalia.notifyError(noctalia.tr("title"), noctalia.tr("error.state_mismatch"))
elseif currentActive then
noctalia.notify(noctalia.tr("notification.enabled.title"), noctalia.tr("notification.enabled.body"))
else
noctalia.notify(noctalia.tr("notification.disabled.title"), noctalia.tr("notification.disabled.body"))
end
end)
end
local function setActive(nextActive)
if busy or not available then return end
busy = true
publish()
if nextActive then
noctalia.runAsync(START_COMMAND, function(result)
finishToggle(true, result)
end)
else
noctalia.runAsync(STOP_COMMAND, function(result)
finishToggle(false, result)
end)
end
end
noctalia.state.watch("lid_guard_command", function(command)
if type(command) ~= "table" then return end
if command.action == "toggle" then
refresh(function(currentActive)
setActive(not currentActive)
end)
elseif command.action == "enable" then
setActive(true)
elseif command.action == "disable" then
setActive(false)
elseif command.action == "refresh" then
refresh()
end
end)
noctalia.setUpdateInterval(5000)
refresh()
function update()
if not busy then refresh() end
end
function onIpc(event, _payload)
if event == "toggle" then
refresh(function(currentActive)
setActive(not currentActive)
end)
elseif event == "enable" then
setActive(true)
elseif event == "disable" then
setActive(false)
elseif event == "refresh" or event == "status" then
refresh()
end
end