Lock before suspend with sleep inhibitor service

This commit is contained in:
David Heinemeier Hansson
2026-05-21 07:57:08 +02:00
parent 0762b20e3e
commit d1b452cc5f
9 changed files with 91 additions and 55 deletions
+4 -1
View File
@@ -29,9 +29,12 @@ quickshell --no-color log -p "$OMARCHY_PATH/shell" --any-display --tail "$lines"
section "Relevant processes"
ps -eo pid=,args= \
| grep -E 'quickshell -n -p|dbus-monitor.*PrepareForSleep|org\.omarchy\.screensaver|omarchy-screensaver|(^|/| )tte( |$)' \
| grep -E 'quickshell -n -p|omarchy-system-sleep-monitor|systemd-inhibit.*Lock screen before suspend|org\.omarchy\.screensaver|omarchy-screensaver|(^|/| )tte( |$)' \
| grep -v grep || true
section "Sleep lock service"
systemctl --user status omarchy-sleep-lock.service --no-pager 2>/dev/null || true
section "Hyprland screensaver clients"
hyprctl clients -j 2>/dev/null \
| jq -r '.[] | select(.class == "org.omarchy.screensaver" or .initialClass == "org.omarchy.screensaver") | [.pid,.class,.initialClass,.title,.focusHistoryID] | @tsv' || true
+5
View File
@@ -5,6 +5,11 @@
CONFIG_DIR="$OMARCHY_PATH/shell"
if [[ $(omarchy-shell lock isLocked 2>/dev/null || true) == "true" ]]; then
echo "Refusing to restart Omarchy shell while the session is locked." >&2
exit 1
fi
pkill -x quickshell 2>/dev/null || true
sleep 0.2
setsid quickshell -n -p "$CONFIG_DIR" >/dev/null 2>&1 &
+25
View File
@@ -0,0 +1,25 @@
#!/bin/bash
# omarchy:summary=Lock before suspend and wait for the session lock to become secure
# omarchy:group=system
# omarchy:hidden=true
wait_attempts=${1:-100}
if [[ ! $wait_attempts =~ ^[0-9]+$ ]] || (( wait_attempts < 1 )); then
wait_attempts=100
fi
omarchy-shell lock lock >/dev/null 2>&1 || exit 1
for (( attempt = 0; attempt < wait_attempts; attempt++ )); do
status=$(omarchy-shell lock status 2>/dev/null || true)
if jq -e '.secure == true' <<<"$status" >/dev/null 2>&1; then
exit 0
fi
sleep 0.05
done
exit 1
+30
View File
@@ -0,0 +1,30 @@
#!/bin/bash
# omarchy:summary=Monitor sleep preparation and lock before suspend
# omarchy:group=system
# omarchy:hidden=true
monitor_command='exec dbus-monitor --system "type='\''signal'\'',sender='\''org.freedesktop.login1'\'',interface='\''org.freedesktop.login1.Manager'\'',member='\''PrepareForSleep'\''"'
consume_sleep_events() {
local line
while IFS= read -r line; do
if [[ $line == *"boolean true"* ]]; then
"$OMARCHY_PATH/bin/omarchy-system-sleep-lock"
exit 0
fi
done
}
if [[ ${1:-} == "--consume" ]]; then
consume_sleep_events
exit 0
fi
exec systemd-inhibit \
--what=sleep \
--mode=delay \
--who=Omarchy \
--why="Lock screen before suspend" \
bash -lc "$monitor_command | \"$OMARCHY_PATH/bin/omarchy-system-sleep-monitor\" --consume"
@@ -0,0 +1,13 @@
[Unit]
Description=Lock Omarchy before suspend
After=dbus.socket
Requires=dbus.socket
[Service]
Type=simple
ExecStart=%h/.local/share/omarchy/bin/omarchy-system-sleep-monitor
Restart=always
RestartSec=2
[Install]
WantedBy=graphical-session.target
+1
View File
@@ -7,6 +7,7 @@ run_logged $OMARCHY_INSTALL/config/timezones.sh
run_logged $OMARCHY_INSTALL/config/increase-sudo-tries.sh
run_logged $OMARCHY_INSTALL/config/increase-lockout-limit.sh
run_logged $OMARCHY_INSTALL/config/lockscreen-pam.sh
run_logged $OMARCHY_INSTALL/config/sleep-lock.sh
run_logged $OMARCHY_INSTALL/config/ssh-flakiness.sh
run_logged $OMARCHY_INSTALL/config/increase-file-watchers.sh
run_logged $OMARCHY_INSTALL/config/increase-fd-limit.sh
+3
View File
@@ -0,0 +1,3 @@
mkdir -p ~/.config/systemd/user/
cp "$OMARCHY_PATH/config/systemd/user/omarchy-sleep-lock.service" ~/.config/systemd/user/
systemctl --user enable omarchy-sleep-lock.service
+8
View File
@@ -0,0 +1,8 @@
echo "Install sleep lock service"
mkdir -p ~/.config/systemd/user/
cp "$OMARCHY_PATH/config/systemd/user/omarchy-sleep-lock.service" ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user reset-failed omarchy-sleep-lock.service 2>/dev/null || true
systemctl --user enable --now omarchy-sleep-lock.service
+2 -54
View File
@@ -202,7 +202,6 @@ Item {
idle: idleMonitor.isIdle,
inIdleCycle: root.idledThisCycle,
screensaverStarted: root.screensaverStartedThisCycle,
sleepMonitor: sleepMonitorProcess.running,
screensaver: root.screensaverTimeoutSeconds,
lock: root.lockTimeoutSeconds,
screensaverDelay: root.screensaverDelaySeconds,
@@ -211,32 +210,18 @@ Item {
timers: {
screensaver: screensaverTimer.running,
lock: lockTimer.running,
screensaverLaunchGrace: screensaverLaunchGraceTimer.running,
sleepMonitorRestart: sleepMonitorRestartTimer.running
screensaverLaunchGrace: screensaverLaunchGraceTimer.running
},
processes: {
screensaver: screensaverProcess.running,
lock: lockProcess.running,
wake: wakeProcess.running,
sleepLock: sleepLockProcess.running,
sleepWake: sleepWakeProcess.running
wake: wakeProcess.running
},
lastEvent: root.lastEvent,
lastEventAt: root.lastEventAt
})
}
function handleSleepPreparing(preparing) {
if (!root.idleEnabled) return
if (preparing) {
cancelIdleCycle("sleep-preparing")
runProcess(sleepLockProcess, "sleep-lock", "OMARCHY_LOCK_ONLY=true omarchy-system-lock")
} else {
runProcess(sleepWakeProcess, "sleep-wake", "sleep 1 && omarchy-system-wake")
}
}
function setIdleEnabled(value) {
var enabled = !!value
if (persisted.idleEnabled === enabled) return enabled ? "enabled" : "disabled"
@@ -299,43 +284,6 @@ Item {
id: wakeProcess
onExited: function(exitCode, exitStatus) { root.logEvent("process-exit", "wake exitCode=" + exitCode + " status=" + exitStatus) }
}
Process {
id: sleepLockProcess
onExited: function(exitCode, exitStatus) { root.logEvent("process-exit", "sleep-lock exitCode=" + exitCode + " status=" + exitStatus) }
}
Process {
id: sleepWakeProcess
onExited: function(exitCode, exitStatus) { root.logEvent("process-exit", "sleep-wake exitCode=" + exitCode + " status=" + exitStatus) }
}
Process {
id: sleepMonitorProcess
command: ["bash", "-lc", "exec dbus-monitor --system \"type='signal',sender='org.freedesktop.login1',interface='org.freedesktop.login1.Manager',member='PrepareForSleep'\""]
running: true
stdout: SplitParser {
onRead: function(line) {
var text = String(line || "").trim()
if (text === "boolean true") root.handleSleepPreparing(true)
else if (text === "boolean false") root.handleSleepPreparing(false)
}
}
onExited: {
root.logEvent("sleep-monitor-exit", "restarting")
sleepMonitorRestartTimer.restart()
}
}
Timer {
id: sleepMonitorRestartTimer
interval: 5000
repeat: false
onTriggered: {
if (!sleepMonitorProcess.running) {
root.logEvent("sleep-monitor-restart")
sleepMonitorProcess.running = true
}
}
}
Component.onCompleted: {
logEvent("service-ready")