manage monitors with monique; reduce hypr-lid to lock/suspend timers
Add the monique GUI/daemon for monitor arrangement, hotplug profiles and clamshell handling (writes monitors.lua, keybind SUPER+SHIFT+M, stowed settings). hypr-lid is trimmed to hypr-lid-power: display management moved to moniqued, it now only runs the delayed lock/suspend timers when the lid is closed undocked. Update logind drop-in comments accordingly.
This commit is contained in:
Executable
+91
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env bash
|
||||
# hypr-lid-power: delayed lock/suspend for the laptop lid.
|
||||
#
|
||||
# Display/clamshell/hotplug handling is owned by moniqued (see
|
||||
# ~/.config/hypr/monitors.lua and moniqued's clamshell mode). This service only
|
||||
# runs the power timers while the lid is closed with no external display
|
||||
# attached:
|
||||
# - lock after LOCK_SECONDS
|
||||
# - suspend after SUSPEND_SECONDS
|
||||
#
|
||||
# Runs as a systemd user service (hypr-lid-power.service) and polls the lid +
|
||||
# monitor state, so connecting/disconnecting a dock while the lid is closed is
|
||||
# handled too. Lock/suspend use transient systemd timers, which pause during
|
||||
# suspend and are recreated afterwards, so if the machine wakes while the lid
|
||||
# stays closed it will sleep again after the full timeout.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
RUNTIME="/run/user/$(id -u)"
|
||||
LOCK_SECONDS=60
|
||||
SUSPEND_SECONDS=600
|
||||
POLL_SECONDS=2
|
||||
|
||||
log() { printf '[hypr-lid-power] %s %s\n' "$(date '+%H:%M:%S')" "$*"; }
|
||||
|
||||
# Run hyprctl against the active Hyprland instance, if any.
|
||||
hypr() {
|
||||
local sig=""
|
||||
for d in "$RUNTIME"/hypr/*/; do
|
||||
if [ -S "$d/.socket.sock" ]; then
|
||||
sig="$(basename "$d")"
|
||||
break
|
||||
fi
|
||||
done
|
||||
[ -n "$sig" ] || return 1
|
||||
HYPRLAND_INSTANCE_SIGNATURE="$sig" command hyprctl "$@"
|
||||
}
|
||||
|
||||
lid_closed() {
|
||||
local state
|
||||
state="$(cat /proc/acpi/button/lid/*/state 2>/dev/null)" || return 1
|
||||
[[ "$state" == *closed* ]]
|
||||
}
|
||||
|
||||
# True when Hyprland has any output up that is not the built-in panel/headless.
|
||||
has_external() {
|
||||
hypr monitors -j 2>/dev/null | grep '"name": "' | grep -qvE '"(eDP|HEADLESS)-'
|
||||
}
|
||||
|
||||
stop_timers() {
|
||||
systemctl --user stop hypr-lid-lock.timer hypr-lid-suspend.timer 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Re-create the named timer unless it is already waiting. Transient timers are
|
||||
# collected after they fire, so this also restarts them after a suspend.
|
||||
ensure_timer() {
|
||||
local name="$1" seconds="$2"
|
||||
shift 2
|
||||
if ! systemctl --user --quiet is-active "hypr-lid-$name.timer"; then
|
||||
systemd-run --user --quiet --unit="hypr-lid-$name" --on-active="${seconds}s" --collect "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
powering=no
|
||||
|
||||
while true; do
|
||||
if ! hypr monitors >/dev/null 2>&1; then
|
||||
# No Hyprland running (e.g. another compositor, or before first launch).
|
||||
powering=no
|
||||
sleep 3
|
||||
continue
|
||||
fi
|
||||
|
||||
want_power="no"
|
||||
if lid_closed && ! has_external; then
|
||||
want_power="yes"
|
||||
fi
|
||||
|
||||
if [ "$want_power" != "$powering" ]; then
|
||||
log "power mode: $powering -> $want_power"
|
||||
if [ "$want_power" = "yes" ]; then
|
||||
ensure_timer lock "$LOCK_SECONDS" loginctl lock-sessions
|
||||
ensure_timer suspend "$SUSPEND_SECONDS" systemctl suspend
|
||||
else
|
||||
stop_timers
|
||||
fi
|
||||
powering="$want_power"
|
||||
fi
|
||||
|
||||
sleep "$POLL_SECONDS"
|
||||
done
|
||||
@@ -1,109 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# hypr-lid: clamshell + lid handling for Hyprland.
|
||||
#
|
||||
# - Lid closed + external display connected -> disable the built-in panel
|
||||
# (clamshell mode); external displays keep working.
|
||||
# - Lid closed + no external display -> turn built-in off, lock after
|
||||
# LOCK_SECONDS, suspend after SUSPEND_SECONDS.
|
||||
# - Lid open -> enable built-in, turn display on.
|
||||
#
|
||||
# Runs as a systemd user service (hypr-lid.service) and polls the lid + monitor
|
||||
# state, so connecting/disconnecting a dock while the lid is closed is handled
|
||||
# too. Lock/suspend use transient systemd timers, which pause during suspend and
|
||||
# are recreated afterwards, so if the machine wakes while the lid stays closed
|
||||
# it will sleep again after the full timeout.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
INTERNAL="desc:BOE 0x0BC9"
|
||||
RUNTIME="/run/user/$(id -u)"
|
||||
LOCK_SECONDS=60
|
||||
SUSPEND_SECONDS=600
|
||||
POLL_SECONDS=2
|
||||
|
||||
log() { printf '[hypr-lid] %s %s\n' "$(date '+%H:%M:%S')" "$*"; }
|
||||
|
||||
# Run hyprctl against the active Hyprland instance, if any.
|
||||
hypr() {
|
||||
local sig=""
|
||||
for d in "$RUNTIME"/hypr/*/; do
|
||||
if [ -S "$d/.socket.sock" ]; then
|
||||
sig="$(basename "$d")"
|
||||
break
|
||||
fi
|
||||
done
|
||||
[ -n "$sig" ] || return 1
|
||||
HYPRLAND_INSTANCE_SIGNATURE="$sig" command hyprctl "$@"
|
||||
}
|
||||
|
||||
lid_closed() {
|
||||
local state
|
||||
state="$(cat /proc/acpi/button/lid/*/state 2>/dev/null)" || return 1
|
||||
[[ "$state" == *closed* ]]
|
||||
}
|
||||
|
||||
# True when Hyprland has any output up that is not the built-in panel/headless.
|
||||
has_external() {
|
||||
hypr monitors -j 2>/dev/null | grep '"name": "' | grep -qvE '"(eDP|HEADLESS)-'
|
||||
}
|
||||
|
||||
stop_timers() {
|
||||
systemctl --user stop hypr-lid-lock.timer hypr-lid-suspend.timer 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Re-create the named timer unless it is already waiting. Transient timers are
|
||||
# collected after they fire, so this also restarts them after a suspend.
|
||||
ensure_timer() {
|
||||
local name="$1" seconds="$2"
|
||||
shift 2
|
||||
if ! systemctl --user --quiet is-active "hypr-lid-$name.timer"; then
|
||||
systemd-run --user --quiet --unit="hypr-lid-$name" --on-active="${seconds}s" --collect "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
mode=unknown
|
||||
|
||||
while true; do
|
||||
if ! hypr monitors >/dev/null 2>&1; then
|
||||
# No Hyprland running (e.g. another compositor, or before first launch).
|
||||
mode=unknown
|
||||
sleep 3
|
||||
continue
|
||||
fi
|
||||
|
||||
new_mode="open"
|
||||
if lid_closed; then
|
||||
if has_external; then
|
||||
new_mode="clamshell"
|
||||
else
|
||||
new_mode="regular"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$new_mode" != "$mode" ]; then
|
||||
log "transition: $mode -> $new_mode"
|
||||
case "$new_mode" in
|
||||
open)
|
||||
hypr keyword monitor "$INTERNAL, preferred, auto, 1"
|
||||
hypr dispatch dpms on
|
||||
stop_timers
|
||||
;;
|
||||
clamshell)
|
||||
hypr keyword monitor "$INTERNAL, disabled"
|
||||
stop_timers
|
||||
;;
|
||||
regular)
|
||||
hypr keyword monitor "$INTERNAL, preferred, auto, 1"
|
||||
hypr dispatch dpms off
|
||||
;;
|
||||
esac
|
||||
mode="$new_mode"
|
||||
fi
|
||||
|
||||
if [ "$mode" = "regular" ]; then
|
||||
ensure_timer lock "$LOCK_SECONDS" loginctl lock-sessions
|
||||
ensure_timer suspend "$SUSPEND_SECONDS" systemctl suspend
|
||||
fi
|
||||
|
||||
sleep "$POLL_SECONDS"
|
||||
done
|
||||
Reference in New Issue
Block a user