diff --git a/nixos/imalison.nix b/nixos/imalison.nix index d7f37641..00d74e29 100644 --- a/nixos/imalison.nix +++ b/nixos/imalison.nix @@ -4,6 +4,120 @@ ./emacs.nix ]; + # Hyprland config starts this target on login (see `dotfiles/config/hypr/hyprland.conf`). + systemd.user.targets.hyprland-session = { + Unit = { + Description = "Hyprland session (custom)"; + }; + }; + + systemd.user.services.hyprpaper = let + wallpaperDir = "/var/lib/syncthing/sync/Wallpaper"; + 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 + ''; + in { + Unit = { + Description = "Hyprpaper (managed by home-manager)"; + PartOf = [ "hyprland-session.target" ]; + After = [ "hyprland-session.target" ]; + }; + + Service = { + ExecStartPre = waitForWayland; + ExecStart = "${pkgs.hyprpaper}/bin/hyprpaper -c ${hyprpaperConf}"; + ExecStartPost = setWallpaper; + Restart = "on-failure"; + RestartSec = 1; + }; + + Install = { + WantedBy = [ "hyprland-session.target" ]; + }; + }; + xdg.desktopEntries.google-chrome-devtools = { name = "Google Chrome (DevTools)"; genericName = "Web Browser"; diff --git a/nixos/xmonad.nix b/nixos/xmonad.nix index 5030251e..61f41f69 100644 --- a/nixos/xmonad.nix +++ b/nixos/xmonad.nix @@ -42,8 +42,10 @@ makeEnable config "myModules.xmonad" true { enable = true; display = "fill"; interval = "1h"; - imageDirectory = "/var/lib/syncthing/sync/Wallpaper/"; + imageDirectory = "/var/lib/syncthing/sync/Wallpaper"; }; + # This service uses feh (X11), so don't run it in Wayland sessions. + systemd.user.services.random-background.Unit.ConditionEnvironment = "XDG_SESSION_TYPE=x11"; services.xsettingsd.enable = true;