Move hyprpaper startup logic into scripts

This commit is contained in:
2026-05-12 23:58:06 -07:00
parent 35bee5750f
commit 5d414403d8
3 changed files with 117 additions and 85 deletions

View File

@@ -0,0 +1,60 @@
#!/usr/bin/env bash
set -u
wallpaper_dir="${WALLPAPER_DIR:-/var/lib/syncthing/sync/Wallpaper/use}"
hyprctl_bin="${HYPRCTL:-hyprctl}"
runtime_dir="${XDG_RUNTIME_DIR:-}"
sig="${HYPRLAND_INSTANCE_SIGNATURE:-}"
sock_path=""
if [ -n "$runtime_dir" ] && [ -n "$sig" ]; then
sock_path="$runtime_dir/hypr/$sig/.hyprpaper.sock"
fi
# Don't fail the service if Hyprland has not exported its instance yet.
if [ -z "$sock_path" ]; then
echo "set-hyprpaper-wallpaper: missing XDG_RUNTIME_DIR/HYPRLAND_INSTANCE_SIGNATURE" >&2
exit 0
fi
for _ in {1..50}; do
if [ -S "$sock_path" ]; then
break
fi
sleep 0.1
done
if [ ! -S "$sock_path" ]; then
echo "set-hyprpaper-wallpaper: hyprpaper socket not ready at $sock_path" >&2
exit 0
fi
if [ ! -d "$wallpaper_dir" ]; then
echo "set-hyprpaper-wallpaper: wallpaper directory missing: $wallpaper_dir" >&2
exit 0
fi
wallpaper="$(find -L "$wallpaper_dir" -type f -regextype posix-extended -iregex '.*\.(png|jpe?g|webp)$' -print0 | shuf -z -n 1 | tr -d '\0')"
if [ -z "$wallpaper" ]; then
echo "set-hyprpaper-wallpaper: no wallpapers found in $wallpaper_dir" >&2
exit 0
fi
mons="$("$hyprctl_bin" -j monitors 2>/dev/null | jq -r '.[].name' 2>/dev/null || true)"
if [ -z "$mons" ]; then
mons="$("$hyprctl_bin" monitors 2>/dev/null | grep -oE '^Monitor [^ ]+' | cut -d' ' -f2 || true)"
fi
for mon in $mons; do
# The socket file can exist before hyprpaper is actually ready to accept IPC.
for _ in {1..50}; do
if "$hyprctl_bin" hyprpaper wallpaper "$mon,$wallpaper" >/dev/null 2>&1; then
break
fi
sleep 0.1
done
done
exit 0

View File

@@ -0,0 +1,40 @@
#!/usr/bin/env bash
set -euo pipefail
config="${HYPRPAPER_CONFIG:-${XDG_CONFIG_HOME:-$HOME/.config}/hypr/hyprpaper.conf}"
for _ in {1..50}; do
if [ -n "${WAYLAND_DISPLAY:-}" ] && [ -n "${XDG_RUNTIME_DIR:-}" ] && [ -S "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" ]; then
break
fi
sleep 0.1
done
if [ -z "${WAYLAND_DISPLAY:-}" ] || [ -z "${XDG_RUNTIME_DIR:-}" ] || [ ! -S "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" ]; then
echo "start-hyprpaper: Wayland socket not ready: WAYLAND_DISPLAY=${WAYLAND_DISPLAY:-} XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-}" >&2
exit 1
fi
nvidia_version="$(sed -n 's/^NVRM version:.* \([0-9][0-9.]*\) .*/\1/p' /proc/driver/nvidia/version 2>/dev/null | head -n 1 || true)"
current_vendor="/run/opengl-driver/share/glvnd/egl_vendor.d/10_nvidia.json"
if [ -n "$nvidia_version" ] && [ -r "$current_vendor" ] && ! grep -q "$nvidia_version" "$current_vendor"; then
matching_vendor=""
for candidate in /nix/store/*-nvidia-x11-"$nvidia_version"*/share/glvnd/egl_vendor.d/10_nvidia.json; do
if [ -r "$candidate" ]; then
matching_vendor="$candidate"
break
fi
done
if [ -n "$matching_vendor" ]; then
echo "start-hyprpaper: using NVIDIA EGL vendor file for loaded driver $nvidia_version: $matching_vendor" >&2
export __EGL_VENDOR_LIBRARY_FILENAMES="$matching_vendor"
else
echo "start-hyprpaper: NVIDIA driver mismatch, but no EGL vendor file for loaded driver $nvidia_version was found" >&2
exit 75
fi
fi
exec hyprpaper -c "$config"

View File

@@ -53,92 +53,18 @@ in {
};
systemd.user.services.hyprpaper = let
wallpaperDir = "/var/lib/syncthing/sync/Wallpaper/use";
waitForWayland = pkgs.writeShellScript "wait-for-wayland" ''
# Hyprpaper needs a Wayland socket. systemd "Condition*" checks are
# brittle here (they skip the unit entirely, with no retry) and also
# don't support expanding $WAYLAND_DISPLAY in paths.
for _ in {1..50}; do
if [ -n "$WAYLAND_DISPLAY" ] && [ -n "$XDG_RUNTIME_DIR" ] && [ -S "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" ]; then
exit 0
fi
${pkgs.coreutils}/bin/sleep 0.1
done
echo "Wayland socket not ready: WAYLAND_DISPLAY=$WAYLAND_DISPLAY XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR" >&2
exit 1
'';
setWallpaper = pkgs.writeShellScript "set-hyprpaper-wallpaper" ''
# hyprpaper 0.8.x doesn't seem to apply `wallpaper = ...` entries from
# its config on startup reliably. Explicitly set the wallpaper via IPC
# once the hyprpaper socket is ready.
#
# NOTE: hyprctl hyprpaper currently ignores `--instance` and uses
# $HYPRLAND_INSTANCE_SIGNATURE to find hyprpaper's socket, so we rely on
# the environment imported by Hyprland on login.
set -u
runtimeDir="''${XDG_RUNTIME_DIR:-}"
sig="''${HYPRLAND_INSTANCE_SIGNATURE:-}"
sockPath=""
if [ -n "$runtimeDir" ] && [ -n "$sig" ]; then
sockPath="$runtimeDir/hypr/$sig/.hyprpaper.sock"
fi
# Don't fail the service if env isn't available yet.
if [ -z "$sockPath" ]; then
echo "set-hyprpaper-wallpaper: missing XDG_RUNTIME_DIR/HYPRLAND_INSTANCE_SIGNATURE" >&2
exit 0
fi
for _ in {1..50}; do
if [ -S "$sockPath" ]; then
break
fi
${pkgs.coreutils}/bin/sleep 0.1
done
if [ ! -S "$sockPath" ]; then
echo "set-hyprpaper-wallpaper: hyprpaper socket not ready at $sockPath" >&2
exit 0
fi
if [ ! -d "${wallpaperDir}" ]; then
echo "set-hyprpaper-wallpaper: wallpaper directory missing: ${wallpaperDir}" >&2
exit 0
fi
wallpaper="$(${pkgs.findutils}/bin/find "${wallpaperDir}" -type f -regextype posix-extended -iregex '.*\.(png|jpe?g|webp)$' -print0 | ${pkgs.coreutils}/bin/shuf -z -n 1 | ${pkgs.coreutils}/bin/tr -d '\0')"
if [ -z "$wallpaper" ]; then
echo "set-hyprpaper-wallpaper: no wallpapers found in ${wallpaperDir}" >&2
exit 0
fi
# Apply to all current monitors.
mons=$(/run/current-system/sw/bin/hyprctl -j monitors 2>/dev/null | ${pkgs.jq}/bin/jq -r '.[].name' 2>/dev/null || true)
if [ -z "$mons" ]; then
# Fallback to parsing non-JSON output.
mons=$(/run/current-system/sw/bin/hyprctl monitors 2>/dev/null | ${pkgs.gnugrep}/bin/grep -oE '^Monitor [^ ]+' | ${pkgs.coreutils}/bin/cut -d' ' -f2 || true)
fi
for mon in $mons; do
# The socket file can exist before hyprpaper is actually ready to
# accept connections, so retry briefly.
for _ in {1..50}; do
if /run/current-system/sw/bin/hyprctl hyprpaper wallpaper "$mon,$wallpaper" >/dev/null 2>&1; then
break
fi
${pkgs.coreutils}/bin/sleep 0.1
done
done
exit 0
'';
hyprpaperConf = pkgs.writeText "hyprpaper.conf" ''
ipc = true
splash = false
'';
hyprpaperPath = pkgs.lib.makeBinPath [
pkgs.coreutils
pkgs.findutils
pkgs.gnugrep
pkgs.gnused
pkgs.hyprpaper
pkgs.jq
];
in {
Unit = {
Description = "Hyprpaper (managed by home-manager)";
@@ -148,10 +74,16 @@ in {
};
Service = {
ExecStartPre = waitForWayland;
ExecStart = "${pkgs.hyprpaper}/bin/hyprpaper -c ${hyprpaperConf}";
ExecStartPost = setWallpaper;
Environment = [
"HYPRPAPER_CONFIG=${hyprpaperConf}"
"HYPRCTL=/run/current-system/sw/bin/hyprctl"
"PATH=${hyprpaperPath}:/run/current-system/sw/bin"
"WALLPAPER_DIR=/var/lib/syncthing/sync/Wallpaper/use"
];
ExecStart = "${pkgs.bash}/bin/bash ${../dotfiles/lib/bin/start-hyprpaper}";
ExecStartPost = "${pkgs.bash}/bin/bash ${../dotfiles/lib/bin/set-hyprpaper-wallpaper}";
Restart = "on-failure";
RestartPreventExitStatus = 75;
RestartSec = 1;
};