{ 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"; # where the listener script will live scriptRel = "hypr/scripts/wpaperd-workspace-1to9.sh"; # wpaperd reads this by default (XDG_CONFIG_HOME/wpaperd/config.toml) wpaperdConfRel = "wpaperd/config.toml"; 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; }; # Workspace/monitor listener: creates per-monitor symlink and reloads wpaperd xdg.configFile."${scriptRel}" = { executable = true; text = '' #!/usr/bin/env bash set -euo pipefail log() { echo "[wpaperd-ws] $*" >&2; } ROOT="${userAbsRoot}" PICTURES="${picturesDir}" CURRENT="${currentDir}" CONF="${config.xdg.configHome}/${wpaperdConfRel}" : "''${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 "successfully" 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 } # Create/refresh config.toml with one section per output, pointing at CURRENT/.jpg write_config_from_monitors() { local mons mons="$(${pkgs.hyprland}/bin/hyprctl -j monitors | ${pkgs.jq}/bin/jq -r '.[].name')" # Truncate + rebuild : >"$CONF" # Each output gets its own section (wpaperd-output(5)); force stretch mode # (If you want different mode later: "fit", "crop", etc.) while IFS= read -r mon; do [[ -n "$mon" ]] || continue cat >>"$CONF" < $img" } apply_for() { local mon="$1" local ws="$2" ensure_symlink_for "$mon" "$ws" write_config_from_monitors # wpaperctl supports reload-wallpaper on 1.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 apply_for "$mon" "$ws" || true done } 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 apply_for "$mon" "$ws" ;; 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 (uses ~/.config/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 that updates per-monitor symlink based on workspace and triggers reload 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" ]; }; }