{ config, pkgs, lib, flakeRoot, ... }: let repoWallpaperDir = flakeRoot + "/assets/conf/desktop/wallpaper"; userRelRoot = "nixos_conf/wallpaperstuff"; userAbsRoot = "${config.home.homeDirectory}/${userRelRoot}"; picturesDir = "${userAbsRoot}/pictures"; currentDir = "${userAbsRoot}/current"; scriptRel = "hypr/scripts/wpaperd-workspace-1to9.sh"; wpaperdConfRel = "wpaperd/config.toml"; wpaperdConfAbs = "${config.xdg.configHome}/${wpaperdConfRel}"; in { home.packages = [ pkgs.wpaperd pkgs.socat pkgs.jq pkgs.coreutils ]; # Copy wallpapers from repo → ~/nixos_conf/wallpaperstuff home.file."${userRelRoot}" = { source = repoWallpaperDir; recursive = true; }; # Create a correct wpaperd config. # - [default] sets stretch globally # - [any] acts as fallback for outputs not explicitly listed (wpaperd-output(5)) # - Per-output sections point at CURRENT/.jpg (symlinks the script updates) # # NOTE: This is safe even if you add/remove monitors: [any] keeps things working. xdg.configFile."${wpaperdConfRel}".text = '' [default] mode = "stretch" [any] path = "${picturesDir}/wallpaper1.jpg" [eDP-1] path = "${currentDir}/eDP-1.jpg" [DP-1] path = "${currentDir}/DP-1.jpg" ''; # Listener: on focused monitor change, update CURRENT/.jpg -> pictures/wallpaperN.jpg xdg.configFile."${scriptRel}" = { executable = true; text = '' #!/usr/bin/env bash set -euo pipefail log() { echo "[wpaperd-ws] $*" >&2; } PICTURES="${picturesDir}" CURRENT="${currentDir}" CONF="${wpaperdConfAbs}" : "''${XDG_RUNTIME_DIR:?XDG_RUNTIME_DIR not set}" : "''${HYPRLAND_INSTANCE_SIGNATURE:?HYPRLAND_INSTANCE_SIGNATURE not set}" SOCK="''${XDG_RUNTIME_DIR}/hypr/''${HYPRLAND_INSTANCE_SIGNATURE}/.socket2.sock" mkdir -p "$CURRENT" "$(dirname "$CONF")" # Wait for Hyprland socket so the service doesn't exit too early for i in $(seq 1 120); do [[ -S "$SOCK" ]] && break sleep 0.1 done [[ -S "$SOCK" ]] || { log "Hyprland socket not found: $SOCK"; exit 1; } ws_num() { local w="''${1%%:*}" [[ "$w" =~ ^[0-9]+$ ]] || return 1 printf '%s\n' "$w" } img_for_ws() { local n="$1" case "$n" in 1|2|3|4|5|6|7|8|9) printf '%s/wallpaper%s.jpg\n' "$PICTURES" "$n" ;; *) return 1 ;; esac } ensure_link_for() { local mon="$1" local ws="$2" local n img n="$(ws_num "$ws")" || return 0 img="$(img_for_ws "$n")" || return 0 if [[ ! -f "$img" ]]; then log "Missing image for ws=$n: $img" return 0 fi ln -sf "$img" "$CURRENT/$mon.jpg" log "Link: $CURRENT/$mon.jpg -> $img" } reload() { # wpaperctl reload-wallpaper exists on v1.2.1 ${pkgs.wpaperd}/bin/wpaperctl reload-wallpaper >/dev/null 2>&1 || true } apply_initial() { ${pkgs.hyprland}/bin/hyprctl -j monitors \ | ${pkgs.jq}/bin/jq -r '.[] | "\(.name)\t\(.activeWorkspace.name)"' \ | while IFS=$'\t' read -r mon ws; do [[ -n "$mon" && -n "$ws" ]] || continue ensure_link_for "$mon" "$ws" || true done reload } handle_line() { case "$1" in focusedmon\>\>*) # focusedmon>>MONNAME,WORKSPACENAME local payload="''${1#focusedmon>>}" local mon="''${payload%%,*}" local ws="''${payload#*,}" [[ -n "$mon" && -n "$ws" ]] || return 0 ensure_link_for "$mon" "$ws" || true reload ;; esac } apply_initial log "Listening on $SOCK" exec ${pkgs.socat}/bin/socat -U - "UNIX-CONNECT:$SOCK" \ | while IFS= read -r line; do handle_line "$line" || true done ''; }; # Start wpaperd (reads XDG_CONFIG_HOME/wpaperd/config.toml by default) systemd.user.services.wpaperd = { Unit = { Description = "wpaperd wallpaper daemon"; After = [ "graphical-session.target" ]; PartOf = [ "graphical-session.target" ]; }; Service = { ExecStart = "${pkgs.wpaperd}/bin/wpaperd"; Restart = "on-failure"; RestartSec = 1; }; Install.WantedBy = [ "graphical-session.target" ]; }; # Listener service systemd.user.services.wpaperd-workspace-wallpapers = { Unit = { Description = "Set wallpaper per monitor based on workspace number (1..9)"; After = [ "graphical-session.target" "wpaperd.service" ]; PartOf = [ "graphical-session.target" ]; }; Service = { ExecStart = "${pkgs.bash}/bin/bash ${config.xdg.configHome}/${scriptRel}"; Restart = "on-failure"; RestartSec = 1; }; Install.WantedBy = [ "graphical-session.target" ]; }; }