{ config, pkgs, lib, ... }: let cfg = config.dataPro.wallpaper; # custom namespace # Where we keep a runtime-editable config for the user. runtimeConfDir = "${config.xdg.configHome}/wallpaper"; runtimeConfPath = "${runtimeConfDir}/wallpaper.conf"; rotateScript = pkgs.writeShellScript "wallpaper-rotate" '' set -euo pipefail export PATH=${lib.makeBinPath [ pkgs.coreutils pkgs.findutils pkgs.gnugrep pkgs.gawk pkgs.util-linux pkgs.swww ]} # Defaults (can be overridden by wallpaper.conf) SOURCE_DIR="${cfg.sourceDir}" TARGET_DIR="${cfg.targetDir}" ROTATION_DIR="${cfg.rotationDir}" TRANSITION_TYPE="fade" TRANSITION_DURATION="2.5" TRANSITION_FPS="60" PICK_MODE="random" # random | sequential LAST_FILE="${config.xdg.stateHome}/wallpaper/last.txt" # Load user config if present (KEY=VALUE lines; '#' comments allowed) if [ -f "${runtimeConfPath}" ]; then # shellcheck disable=SC1090 . "${runtimeConfPath}" fi mkdir -p "$TARGET_DIR" "${config.xdg.stateHome}/wallpaper" # Ensure swww-daemon is running if ! pgrep -x swww-daemon >/dev/null 2>&1; then swww-daemon >/dev/null 2>&1 & sleep 0.3 fi # Pick image if [ ! -d "$ROTATION_DIR" ]; then echo "Rotation directory does not exist: $ROTATION_DIR" >&2 exit 0 fi # Build list once per run mapfile -d '' -t imgs < <( find "$ROTATION_DIR" -type f \ \( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' -o -iname '*.webp' \) \ -print0 ) if [ "${#imgs[@]}" -eq 0 ]; then echo "No images found in: $ROTATION_DIR" >&2 exit 0 fi img="" if [ "$PICK_MODE" = "sequential" ]; then last="" if [ -f "$LAST_FILE" ]; then last="$(cat "$LAST_FILE" || true)" fi # Sort for stable order IFS=$'\n' sorted=($(printf '%s\n' "${imgs[@]}" | sort)) unset IFS # Find next after last next="" found_last=0 for f in "${sorted[@]}"; do if [ "$found_last" -eq 1 ]; then next="$f" break fi if [ "$f" = "$last" ]; then found_last=1 fi done if [ -z "$next" ]; then next="${sorted[0]}" fi img="$next" printf '%s' "$img" > "$LAST_FILE" else # random img="$(printf '%s\0' "${imgs[@]}" | shuf -z -n 1 | tr -d '\0')" fi # Smooth transition wallpaper set swww img "$img" \ --transition-type "$TRANSITION_TYPE" \ --transition-duration "$TRANSITION_DURATION" \ --transition-fps "$TRANSITION_FPS" ''; in { options.dataPro.wallpaper = { enable = lib.mkEnableOption "Hourly wallpaper rotation with swww (Hyprland/Wayland)"; # You asked to make this easy to change: sourceDir = lib.mkOption { type = lib.types.str; default = "${config.home.homeDirectory}/nixos/files/wallpaper/picture"; description = "Folder containing wallpapers to rotate (source of truth)."; }; targetDir = lib.mkOption { type = lib.types.str; default = "${config.home.homeDirectory}/wallpaper"; description = "Folder in the user's home where wallpapers are copied to."; }; rotationDir = lib.mkOption { type = lib.types.str; default = "${config.home.homeDirectory}/wallpaper/picture"; description = "Folder used by the rotator to pick wallpapers from (usually inside targetDir)."; }; }; config = lib.mkIf cfg.enable { home.packages = [ pkgs.swww pkgs.coreutils pkgs.findutils pkgs.gnugrep pkgs.gawk pkgs.util-linux ]; # Copy wallpapers from sourceDir into rotationDir (real copy) at activation. home.activation.wallpaperCopy = lib.hm.dag.entryAfter [ "writeBoundary" ] '' set -euo pipefail mkdir -p "${cfg.targetDir}" "${cfg.rotationDir}" if [ -d "${cfg.sourceDir}" ]; then # Copy source into rotationDir (contents only). cp -a "${cfg.sourceDir}/." "${cfg.rotationDir}/" fi ''; # Install a runtime-editable config into ~/.config/wallpaper/wallpaper.conf # First-time copy from repo if present; then never overwrite if user edited it. home.activation.wallpaperConf = lib.hm.dag.entryAfter [ "writeBoundary" ] '' set -euo pipefail mkdir -p "${runtimeConfDir}" if [ ! -f "${runtimeConfPath}" ]; then if [ -f "${config.home.homeDirectory}/nixos/files/conf/wallpaper/wallpaper.conf" ]; then cp -a "${config.home.homeDirectory}/nixos/files/conf/wallpaper/wallpaper.conf" "${runtimeConfPath}" else cat > "${runtimeConfPath}" <<'EOF' # wallpaper.conf — runtime settings for the wallpaper rotator (swww) # Lines are KEY=VALUE. '#' starts a comment. # Folder to copy wallpapers from (source of truth) #SOURCE_DIR="$HOME/nixos/files/wallpaper/picture" # Where wallpapers are copied to (a folder in your home) #TARGET_DIR="$HOME/wallpaper" # Folder used for rotation (where images are selected from) #ROTATION_DIR="$HOME/wallpaper/picture" # random | sequential #PICK_MODE="random" # swww transition settings #TRANSITION_TYPE="fade" #TRANSITION_DURATION="2.5" #TRANSITION_FPS="60" EOF fi fi ''; # Start swww daemon on graphical session start systemd.user.services.swww-daemon = { Unit = { Description = "swww wallpaper daemon"; After = [ "graphical-session.target" ]; PartOf = [ "graphical-session.target" ]; }; Service = { ExecStart = "${pkgs.swww}/bin/swww-daemon"; Restart = "on-failure"; }; Install = { WantedBy = [ "graphical-session.target" ]; }; }; # Rotate wallpaper hourly systemd.user.services.wallpaper-rotate = { Unit = { Description = "Rotate wallpaper (hourly)"; After = [ "swww-daemon.service" ]; }; Service = { Type = "oneshot"; ExecStart = "${rotateScript}"; }; }; systemd.user.timers.wallpaper-rotate = { Unit = { Description = "Hourly wallpaper rotation timer"; }; Timer = { OnBootSec = "2m"; OnUnitActiveSec = "1h"; Unit = "wallpaper-rotate.service"; }; Install = { WantedBy = [ "timers.target" ]; }; }; }; }