{ config, pkgs, lib, flakeRoot, ... }: let repoWallpaperDir = flakeRoot + "/assets/conf/desktop/wallpaper"; userRelRoot = "nixos_conf/wallpaperstuff/pictures"; userAbsRoot = "${config.home.homeDirectory}/${userRelRoot}"; scriptRel = "hypr/scripts/wpaperd-workspace-1to9.sh"; # Minimal wpaperd config: force stretch; we don't rely on it for selection, # but it helps ensure daemon is "properly configured". wpaperdConfigRel = "wpaperd/config.toml"; in { home.packages = [ pkgs.wpaperd pkgs.socat pkgs.jq ]; # Copy wallpapers from repo → ~/nixos_conf/wallpaperstuff home.file."${userRelRoot}" = { source = repoWallpaperDir; recursive = true; }; # Force stretch (and give wpaperd a valid config file) xdg.configFile."${wpaperdConfigRel}".text = '' [default] mode = "stretch" ''; # Workspace listener script xdg.configFile."${scriptRel}" = { executable = true; text = '' #!/usr/bin/env bash set -euo pipefail ROOT="${userAbsRoot}" : "''${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" log() { echo "[wpaperd-ws] $*" >&2; } # Wait up to ~10s for Hyprland socket (avoid exiting 0 and never restarting) for i in $(seq 1 100); 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' "$ROOT" "$n" ;; *) return 1 ;; esac } have_set=0 if ${pkgs.wpaperd}/bin/wpaperctl --help 2>&1 | ${pkgs.gnugrep}/bin/grep -qE '(\bset\b)'; then have_set=1 fi apply_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 if [[ "$have_set" -eq 1 ]]; then log "Setting $img on $mon (ws=$n)" ${pkgs.wpaperd}/bin/wpaperctl set "$img" "$mon" else log "ERROR: your wpaperctl doesn't expose 'set'. Check 'wpaperctl --help' and package version." log "Upstream supports: wpaperctl set /path/to/image.png [OUTPUT]" return 0 fi } 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 apply_for "$mon" "$ws" || true done } handle_line() { case "$1" in focusedmon\>\>*) local payload="''${1#focusedmon>>}" local mon="''${payload%%,*}" local ws="''${payload#*,}" apply_for "$mon" "$ws" || true ;; 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" done ''; }; # Start wpaperd (with config) 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"; }; Install.WantedBy = [ "graphical-session.target" ]; }; # Listener systemd.user.services.wpaperd-workspace-wallpapers = { Unit = { Description = "Set wallpaper 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" ]; }; }