hyprland: run screensaver as layer overlay
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
general {
|
general {
|
||||||
lock_cmd = pidof hyprlock || hyprlock
|
lock_cmd = pidof hyprlock || hyprlock
|
||||||
before_sleep_cmd = loginctl lock-session
|
before_sleep_cmd = loginctl lock-session
|
||||||
after_sleep_cmd = hyprctl dispatch dpms on
|
|
||||||
}
|
}
|
||||||
|
|
||||||
listener {
|
listener {
|
||||||
@@ -9,9 +8,3 @@ listener {
|
|||||||
on-timeout = /home/imalison/dotfiles/dotfiles/lib/bin/hypr-screensaver start
|
on-timeout = /home/imalison/dotfiles/dotfiles/lib/bin/hypr-screensaver start
|
||||||
on-resume = /home/imalison/dotfiles/dotfiles/lib/bin/hypr-screensaver stop
|
on-resume = /home/imalison/dotfiles/dotfiles/lib/bin/hypr-screensaver stop
|
||||||
}
|
}
|
||||||
|
|
||||||
listener {
|
|
||||||
timeout = 900
|
|
||||||
on-timeout = /home/imalison/dotfiles/dotfiles/lib/bin/hypr-screensaver stop && hyprctl dispatch dpms off
|
|
||||||
on-resume = hyprctl dispatch dpms on && /home/imalison/dotfiles/dotfiles/lib/bin/hypr-screensaver stop
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -518,10 +518,10 @@ simplifiedScreensaverWidget =
|
|||||||
then return False
|
then return False
|
||||||
else case button of
|
else case button of
|
||||||
1 -> do
|
1 -> do
|
||||||
void $ spawnCommand "hypr-screensaver toggle >/dev/null 2>&1"
|
void $ spawnCommand "/home/imalison/dotfiles/dotfiles/lib/bin/hypr-screensaver toggle >/dev/null 2>&1"
|
||||||
return True
|
return True
|
||||||
3 -> do
|
3 -> do
|
||||||
void $ spawnCommand "hypr-screensaver stop >/dev/null 2>&1"
|
void $ spawnCommand "/home/imalison/dotfiles/dotfiles/lib/bin/hypr-screensaver stop >/dev/null 2>&1"
|
||||||
return True
|
return True
|
||||||
_ -> return False
|
_ -> return False
|
||||||
Gtk.widgetShowAll ebox
|
Gtk.widgetShowAll ebox
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
script_path="$(readlink -f "${BASH_SOURCE[0]}")"
|
|
||||||
state_dir="${XDG_RUNTIME_DIR:-/tmp}/hypr-screensaver"
|
state_dir="${XDG_RUNTIME_DIR:-/tmp}/hypr-screensaver"
|
||||||
|
pid_file="$state_dir/mpvpaper.pid"
|
||||||
|
event_log="$state_dir/events.log"
|
||||||
mkdir -p "$state_dir"
|
mkdir -p "$state_dir"
|
||||||
|
|
||||||
title_prefix="hypr-screensaver:"
|
title_prefix="hypr-screensaver:"
|
||||||
@@ -15,11 +16,11 @@ usage() {
|
|||||||
Usage: hypr-screensaver <start|stop|toggle|status|session>
|
Usage: hypr-screensaver <start|stop|toggle|status|session>
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
start Launch the screensaver on every Hyprland monitor.
|
start Launch the screensaver as a Wayland layer-shell overlay.
|
||||||
stop Stop any running screensaver windows.
|
stop Stop any running screensaver overlay.
|
||||||
toggle Start if stopped, otherwise stop.
|
toggle Start if stopped, otherwise stop.
|
||||||
status Exit 0 if any screensaver window is running, otherwise exit 1.
|
status Exit 0 if the screensaver overlay is running, otherwise exit 1.
|
||||||
session Run the configured screensaver payload for one monitor.
|
session Compatibility alias for start.
|
||||||
|
|
||||||
By default, start chooses a random media file from:
|
By default, start chooses a random media file from:
|
||||||
/var/lib/syncthing/sync/Screensaver/use
|
/var/lib/syncthing/sync/Screensaver/use
|
||||||
@@ -33,6 +34,10 @@ example:
|
|||||||
You can also override the rotation directory:
|
You can also override the rotation directory:
|
||||||
HYPR_SCREENSAVER_USE_DIR='/path/to/use'
|
HYPR_SCREENSAVER_USE_DIR='/path/to/use'
|
||||||
|
|
||||||
|
Layer-shell/output overrides:
|
||||||
|
HYPR_SCREENSAVER_OUTPUT='ALL'
|
||||||
|
HYPR_SCREENSAVER_LAYER='overlay'
|
||||||
|
|
||||||
HDR handling defaults to matching Hyprland's monitor color-management preset.
|
HDR handling defaults to matching Hyprland's monitor color-management preset.
|
||||||
Only monitors with preset "hdr" or "hdredid" get HDR colorspace hints. Override
|
Only monitors with preset "hdr" or "hdredid" get HDR colorspace hints. Override
|
||||||
with:
|
with:
|
||||||
@@ -46,28 +51,11 @@ monitors_json() {
|
|||||||
hyprctl -j monitors
|
hyprctl -j monitors
|
||||||
}
|
}
|
||||||
|
|
||||||
monitor_names() {
|
log_event() {
|
||||||
monitors_json | jq -r '.[].name'
|
printf '%s %s\n' "$(date --iso-8601=seconds)" "$*" >>"$event_log"
|
||||||
}
|
}
|
||||||
|
|
||||||
monitor_specs() {
|
legacy_screensaver_window_pids() {
|
||||||
monitors_json | jq -c '.[] | { name, width, height }'
|
|
||||||
}
|
|
||||||
|
|
||||||
focused_monitor() {
|
|
||||||
monitors_json | jq -r '.[] | select(.focused) | .name'
|
|
||||||
}
|
|
||||||
|
|
||||||
monitor_color_management_preset() {
|
|
||||||
local monitor="$1"
|
|
||||||
monitors_json | jq -r --arg monitor "$monitor" '
|
|
||||||
.[]
|
|
||||||
| select(.name == $monitor)
|
|
||||||
| .colorManagementPreset // "srgb"
|
|
||||||
'
|
|
||||||
}
|
|
||||||
|
|
||||||
screensaver_window_pids() {
|
|
||||||
hyprctl -j clients 2>/dev/null | jq -r --arg prefix "$title_prefix" '
|
hyprctl -j clients 2>/dev/null | jq -r --arg prefix "$title_prefix" '
|
||||||
.[]
|
.[]
|
||||||
| select((.title // "") | startswith($prefix))
|
| select((.title // "") | startswith($prefix))
|
||||||
@@ -77,27 +65,28 @@ screensaver_window_pids() {
|
|||||||
|
|
||||||
is_running() {
|
is_running() {
|
||||||
local pid
|
local pid
|
||||||
for pid in $(screensaver_window_pids); do
|
if [ -f "$pid_file" ]; then
|
||||||
if kill -0 "$pid" 2>/dev/null; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
shopt -s nullglob
|
|
||||||
local pid_file
|
|
||||||
for pid_file in "$state_dir"/*.pid; do
|
|
||||||
pid="$(<"$pid_file")"
|
pid="$(<"$pid_file")"
|
||||||
if kill -0 "$pid" 2>/dev/null; then
|
if kill -0 "$pid" 2>/dev/null; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
done
|
rm -f "$pid_file"
|
||||||
|
fi
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
default_source() {
|
default_source() {
|
||||||
local width="$1"
|
local size width height
|
||||||
local height="$2"
|
size="$(
|
||||||
|
monitors_json 2>/dev/null \
|
||||||
|
| jq -r 'max_by((.width // 0) * (.height // 0)) | "\(.width // 1920)x\(.height // 1080)"' 2>/dev/null \
|
||||||
|
|| true
|
||||||
|
)"
|
||||||
|
size="${size:-1920x1080}"
|
||||||
|
width="${size%x*}"
|
||||||
|
height="${size#*x}"
|
||||||
|
|
||||||
printf 'av://lavfi:life=s=%sx%s:r=60:mold=10:ratio=0.065:death_color=#101414:life_color=#7dd3fc:mold_color=#1e3a5f,format=yuv420p' \
|
printf 'av://lavfi:life=s=%sx%s:r=60:mold=10:ratio=0.065:death_color=#101414:life_color=#7dd3fc:mold_color=#1e3a5f,format=yuv420p' \
|
||||||
"$width" "$height"
|
"$width" "$height"
|
||||||
}
|
}
|
||||||
@@ -128,10 +117,8 @@ random_source() {
|
|||||||
printf '%s\n' "${candidates[$((RANDOM % ${#candidates[@]}))]}"
|
printf '%s\n' "${candidates[$((RANDOM % ${#candidates[@]}))]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
monitor_uses_hdr() {
|
screensaver_uses_hdr() {
|
||||||
local monitor="$1"
|
|
||||||
local mode="${HYPR_SCREENSAVER_HDR_MODE:-auto}"
|
local mode="${HYPR_SCREENSAVER_HDR_MODE:-auto}"
|
||||||
local preset
|
|
||||||
|
|
||||||
case "$mode" in
|
case "$mode" in
|
||||||
hdr)
|
hdr)
|
||||||
@@ -141,15 +128,8 @@ monitor_uses_hdr() {
|
|||||||
return 1
|
return 1
|
||||||
;;
|
;;
|
||||||
auto)
|
auto)
|
||||||
preset="$(monitor_color_management_preset "$monitor" 2>/dev/null || true)"
|
monitors_json 2>/dev/null \
|
||||||
case "$preset" in
|
| jq -e 'any(.[]; (.colorManagementPreset // "srgb") == "hdr" or (.colorManagementPreset // "srgb") == "hdredid")' >/dev/null 2>&1
|
||||||
hdr|hdredid)
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
printf 'Invalid HYPR_SCREENSAVER_HDR_MODE=%s; expected auto, sdr, or hdr\n' "$mode" >&2
|
printf 'Invalid HYPR_SCREENSAVER_HDR_MODE=%s; expected auto, sdr, or hdr\n' "$mode" >&2
|
||||||
@@ -158,119 +138,99 @@ monitor_uses_hdr() {
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
mpv_color_args() {
|
mpv_color_options() {
|
||||||
local monitor="$1"
|
if screensaver_uses_hdr; then
|
||||||
|
printf '%s ' \
|
||||||
if monitor_uses_hdr "$monitor"; then
|
target-colorspace-hint=yes \
|
||||||
printf '%s\0' \
|
target-colorspace-hint-mode=source
|
||||||
--target-colorspace-hint=yes \
|
|
||||||
--target-colorspace-hint-mode=source
|
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf '%s\0' \
|
printf '%s ' \
|
||||||
--target-colorspace-hint=no \
|
target-colorspace-hint=no \
|
||||||
--target-prim=bt.709 \
|
target-prim=bt.709 \
|
||||||
--target-trc=srgb \
|
target-trc=srgb \
|
||||||
--target-gamut=bt.709 \
|
target-gamut=bt.709 \
|
||||||
--target-peak=80 \
|
target-peak=80 \
|
||||||
--inverse-tone-mapping=no
|
inverse-tone-mapping=no
|
||||||
|
}
|
||||||
|
|
||||||
|
mpv_options() {
|
||||||
|
if [ -n "${HYPR_SCREENSAVER_MPV_OPTIONS:-}" ]; then
|
||||||
|
printf '%s\n' "$HYPR_SCREENSAVER_MPV_OPTIONS"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s %s\n' \
|
||||||
|
"no-audio loop-file=inf osc=no osd-level=0 input-default-bindings=no terminal=no image-display-duration=inf keep-open=yes" \
|
||||||
|
"$(mpv_color_options)"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_mpvpaper() {
|
||||||
|
if command -v mpvpaper >/dev/null 2>&1; then
|
||||||
|
exec mpvpaper "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec nix shell nixpkgs#mpvpaper --command mpvpaper "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
local current_monitor spec monitor width height pid source
|
local source output layer options pid
|
||||||
|
|
||||||
if is_running; then
|
if is_running; then
|
||||||
|
log_event "start ignored: already running pid=$(<"$pid_file")"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
current_monitor="$(focused_monitor || true)"
|
stop
|
||||||
|
|
||||||
source="${HYPR_SCREENSAVER_SOURCE:-}"
|
source="${HYPR_SCREENSAVER_SOURCE:-}"
|
||||||
if [ -z "$source" ]; then
|
if [ -z "$source" ]; then
|
||||||
source="$(random_source || true)"
|
source="$(random_source || true)"
|
||||||
fi
|
fi
|
||||||
|
if [ -z "$source" ]; then
|
||||||
while IFS= read -r spec; do
|
source="$(default_source)"
|
||||||
monitor="$(jq -r '.name' <<<"$spec")"
|
|
||||||
width="$(jq -r '.width' <<<"$spec")"
|
|
||||||
height="$(jq -r '.height' <<<"$spec")"
|
|
||||||
[ -n "$monitor" ] || continue
|
|
||||||
HYPR_SCREENSAVER_MONITOR="$monitor" \
|
|
||||||
HYPR_SCREENSAVER_WIDTH="$width" \
|
|
||||||
HYPR_SCREENSAVER_HEIGHT="$height" \
|
|
||||||
HYPR_SCREENSAVER_SOURCE="$source" \
|
|
||||||
"$script_path" session >/dev/null 2>&1 &
|
|
||||||
pid=$!
|
|
||||||
printf '%s\n' "$pid" > "$state_dir/${monitor}.pid"
|
|
||||||
sleep 0.15
|
|
||||||
done < <(monitor_specs)
|
|
||||||
|
|
||||||
if [ -n "$current_monitor" ]; then
|
|
||||||
hyprctl dispatch focusmonitor "$current_monitor" >/dev/null 2>&1 || true
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
output="${HYPR_SCREENSAVER_OUTPUT:-ALL}"
|
||||||
|
layer="${HYPR_SCREENSAVER_LAYER:-overlay}"
|
||||||
|
options="$(mpv_options)"
|
||||||
|
log_event "start output=$output layer=$layer source=$source"
|
||||||
|
|
||||||
|
(
|
||||||
|
exec </dev/null
|
||||||
|
run_mpvpaper --layer "$layer" --mpv-options "$options" "$output" "$source"
|
||||||
|
) >>"$state_dir/mpvpaper.log" 2>&1 &
|
||||||
|
pid=$!
|
||||||
|
printf '%s\n' "$pid" > "$pid_file"
|
||||||
|
sleep 0.2
|
||||||
|
if ! kill -0 "$pid" 2>/dev/null; then
|
||||||
|
rm -f "$pid_file"
|
||||||
|
log_event "start failed: process exited early pid=$pid"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
log_event "start ok pid=$pid"
|
||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
local pid pid_file
|
local pid legacy_pid
|
||||||
|
|
||||||
for pid in $(screensaver_window_pids); do
|
if [ -f "$pid_file" ]; then
|
||||||
kill "$pid" >/dev/null 2>&1 || true
|
|
||||||
done
|
|
||||||
|
|
||||||
shopt -s nullglob
|
|
||||||
for pid_file in "$state_dir"/*.pid; do
|
|
||||||
pid="$(<"$pid_file")"
|
pid="$(<"$pid_file")"
|
||||||
|
log_event "stop pid=$pid"
|
||||||
kill "$pid" >/dev/null 2>&1 || true
|
kill "$pid" >/dev/null 2>&1 || true
|
||||||
|
pkill -TERM -P "$pid" >/dev/null 2>&1 || true
|
||||||
rm -f "$pid_file"
|
rm -f "$pid_file"
|
||||||
|
else
|
||||||
|
log_event "stop with no pid file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for legacy_pid in $(legacy_screensaver_window_pids); do
|
||||||
|
log_event "stop legacy pid=$legacy_pid"
|
||||||
|
kill "$legacy_pid" >/dev/null 2>&1 || true
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
session() {
|
|
||||||
local monitor="${HYPR_SCREENSAVER_MONITOR:?missing HYPR_SCREENSAVER_MONITOR}"
|
|
||||||
local width="${HYPR_SCREENSAVER_WIDTH:-1920}"
|
|
||||||
local height="${HYPR_SCREENSAVER_HEIGHT:-1080}"
|
|
||||||
local source="${HYPR_SCREENSAVER_SOURCE:-}"
|
|
||||||
local -a color_args=()
|
|
||||||
if [ -z "$source" ]; then
|
|
||||||
source="$(random_source || true)"
|
|
||||||
fi
|
|
||||||
if [ -z "$source" ]; then
|
|
||||||
source="$(default_source "$width" "$height")"
|
|
||||||
fi
|
|
||||||
while IFS= read -r -d '' arg; do
|
|
||||||
color_args+=("$arg")
|
|
||||||
done < <(mpv_color_args "$monitor")
|
|
||||||
|
|
||||||
local -a mpv_args=(
|
|
||||||
--no-config
|
|
||||||
--really-quiet
|
|
||||||
--fullscreen
|
|
||||||
--fs-screen-name="$monitor"
|
|
||||||
--screen-name="$monitor"
|
|
||||||
--force-window=immediate
|
|
||||||
--border=no
|
|
||||||
--title-bar=no
|
|
||||||
--ontop
|
|
||||||
--keep-open=yes
|
|
||||||
--loop-file=inf
|
|
||||||
--audio=no
|
|
||||||
--osc=no
|
|
||||||
--osd-level=0
|
|
||||||
--input-default-bindings=no
|
|
||||||
--wayland-app-id=hypr-screensaver
|
|
||||||
--title="${title_prefix}${monitor}"
|
|
||||||
--image-display-duration=inf
|
|
||||||
"${color_args[@]}"
|
|
||||||
"$source"
|
|
||||||
)
|
|
||||||
|
|
||||||
if command -v mpv >/dev/null 2>&1; then
|
|
||||||
exec mpv "${mpv_args[@]}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec nix shell nixpkgs#mpv --command mpv "${mpv_args[@]}"
|
|
||||||
}
|
|
||||||
|
|
||||||
status() {
|
status() {
|
||||||
is_running
|
is_running
|
||||||
}
|
}
|
||||||
@@ -293,7 +253,7 @@ case "${1:-}" in
|
|||||||
status
|
status
|
||||||
;;
|
;;
|
||||||
session)
|
session)
|
||||||
session
|
start
|
||||||
;;
|
;;
|
||||||
""|-h|--help|help)
|
""|-h|--help|help)
|
||||||
usage
|
usage
|
||||||
|
|||||||
Reference in New Issue
Block a user