add (unnecessary) watchdog to wallpaper-daemon

This commit is contained in:
2025-10-08 20:39:32 +02:00
parent d0c00410ae
commit fcb4dfb530
4 changed files with 120 additions and 66 deletions

View File

@@ -67,10 +67,9 @@ spicetify-maketplace # spotify themes
nwg-look # theme of GTK apps nwg-look # theme of GTK apps
catppuccin-gtk-theme-mocha # theme of GTK apps catppuccin-gtk-theme-mocha # theme of GTK apps
kvantum kvantum
qt5ct
qt6ct
# utils # utils
bc bc
jq jq
python-colorthief python-colorthief
python-watchdog

View File

@@ -1,5 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Select image file if none is provided
if [ -z "$1" ]; then if [ -z "$1" ]; then
image=$(zenity --file-selection --title="Open File" --file-filter="*.jpg *.jpeg *.png *.webp *.bmp *.jfif *.tiff *.avif *.heic *.heif") image=$(zenity --file-selection --title="Open File" --file-filter="*.jpg *.jpeg *.png *.webp *.bmp *.jfif *.tiff *.avif *.heic *.heif")
else else
@@ -9,6 +10,9 @@ fi
[ -z "$image" ] && exit 1 [ -z "$image" ] && exit 1
[ ! -f "$image" ] && exit 1 [ ! -f "$image" ] && exit 1
# Copy image to local wallpaper directory
ext=${image##*.} ext=${image##*.}
random_name=$(tr -dc 'a-zA-Z0-9' </dev/urandom | head -c 16) random_name=$(tr -dc 'a-zA-Z0-9' </dev/urandom | head -c 16)
current_dir="$HOME/.local/share/wallpaper/current" current_dir="$HOME/.local/share/wallpaper/current"
@@ -28,60 +32,62 @@ cp -f "$temp_img" "$image_copied" || (
exit 1 exit 1
) )
if [ "$XDG_CURRENT_DESKTOP" = "Hyprland" ]; then # Generate blurred wallpaper
hyprctl hyprpaper reload ,"$image_copied" || exit 1
echo "preload = $image_copied" >"$HOME/.config/hypr/hyprpaper.conf"
echo "wallpaper = , $image_copied" >>"$HOME/.config/hypr/hyprpaper.conf"
notify-send "Wallpaper Changed" "$image" blur_dir="$HOME/.local/share/wallpaper/blurred"
elif [ "$XDG_CURRENT_DESKTOP" = "niri" ]; then mkdir -p "$blur_dir" || (
killall swaybg echo "Could not create cache directory"
killall swww exit 1
)
rm -f "${blur_dir:?}"/blurred-*
blurred_image="$blur_dir/blurred-${random_name}.$ext"
blur_dir="$HOME/.local/share/wallpaper/blurred" ## Time consuming task (magick -blur) in background
mkdir -p "$blur_dir" || ( (
echo "Could not create cache directory" # notify-send "Generating Blurred Wallpaper" "This may take a few seconds..."
sigma=$(magick identify -format "%w %h" "$image_copied" | awk -v f=0.01 '{
m=($1>$2)?$1:$2;
s=m*f;
if(s<2) s=2;
if(s>200) s=200;
printf "%.2f", s
}')
### use a temporary file to avoid incomplete file being used
temp_blurred=$(mktemp --suffix=."$ext") || exit 1
trap 'rm -f "${temp_blurred}"' EXIT
magick "$image_copied" -blur 0x"$sigma" "$temp_blurred" || (
echo "Could not create blurred image"
exit 1
)
mv -f "$temp_blurred" "$blurred_image" || (
echo "Could not move blurred image to cache directory"
exit 1 exit 1
) )
rm -f "${blur_dir:?}"/blurred-*
blurred_image="$blur_dir/blurred-${random_name}.$ext"
# blur if [ "$XDG_CURRENT_DESKTOP" = "niri" ]; then
(
# notify-send "Generating Blurred Wallpaper" "This may take a few seconds..."
sigma=$(magick identify -format "%w %h" "$image_copied" | awk -v f=0.01 '{
m=($1>$2)?$1:$2;
s=m*f;
if(s<2) s=2;
if(s>200) s=200;
printf "%.2f", s
}')
temp_blurred=$(mktemp --suffix=."$ext") || exit 1
trap 'rm -f "${temp_blurred}"' EXIT
magick "$image_copied" -blur 0x"$sigma" "$temp_blurred" || (
echo "Could not create blurred image"
exit 1
)
mv -f "$temp_blurred" "$blurred_image" || (
echo "Could not move blurred image to cache directory"
exit 1
)
# nohup swaybg -i "$blurred_image" -m fill > /dev/null 2> /dev/null &
swww img -n backdrop "$blurred_image" --transition-type fade --transition-duration 2 > /dev/null 2> /dev/null swww img -n backdrop "$blurred_image" --transition-type fade --transition-duration 2 > /dev/null 2> /dev/null
fi
notify-send "Blurred Wallpaper Generated" "$blurred_image" notify-send "Blurred Wallpaper Generated" "$blurred_image"
) & ) &
# Apply wallpaper
if [ "$XDG_CURRENT_DESKTOP" = "Hyprland" ]; then
swww img -n background "$image_copied" --transition-type fade --transition-duration 2 > /dev/null 2> /dev/null swww img -n background "$image_copied" --transition-type fade --transition-duration 2 > /dev/null 2> /dev/null
notify-send "Wallpaper Changed" "$image" notify-send "Wallpaper Changed" "$image"
change-colortheme -i "$image_copied" || exit 1
elif [ "$XDG_CURRENT_DESKTOP" = "niri" ]; then
swww img -n background "$image_copied" --transition-type fade --transition-duration 2 > /dev/null 2> /dev/null
notify-send "Wallpaper Changed" "$image"
change-colortheme -i "$image_copied" || exit 1
else else
echo "Unsupported desktop environment: $XDG_CURRENT_DESKTOP" echo "Unsupported desktop environment: $XDG_CURRENT_DESKTOP"
exit 1 exit 1
fi fi
# notify-send "Extracting Colors from Wallpaper" "This may take a few seconds..."
change-colortheme -i "$image_copied" || exit 1

View File

@@ -1,3 +0,0 @@
#!/bin/sh
swayidle -w timeout 300 'hyprlock &' timeout 600 'niri msg action power-off-monitors' before-sleep 'hyprlock &'

View File

@@ -8,21 +8,24 @@ from sys import exit
from time import sleep from time import sleep
from os import environ from os import environ
from pathlib import Path from pathlib import Path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
NORMAL_WALLPAPER_DIR = Path("/home/kolkas/.local/share/wallpaper/current") NORMAL_WALLPAPER_DIR = Path("~/.local/share/wallpaper/current").expanduser()
BLURRED_WALLPAPER_DIR = Path("/home/kolkas/.local/share/wallpaper/blurred") BLURRED_WALLPAPER_DIR = Path("~/.local/share/wallpaper/blurred").expanduser()
def _get_first_file(dir: Path, pattern: str = "*") -> Path | None: def getFirstFile(dir: Path, pattern: str = "*") -> Path | None:
'''`find $dir -type f | head -n 1`'''
return next(dir.glob(pattern), None) return next(dir.glob(pattern), None)
def get_niri_socket(): def getNiriSocket():
return environ['NIRI_SOCKET'] return environ['NIRI_SOCKET']
def _log(msg: str): def _log(msg: str):
# print(msg) print(msg)
# logFIle = Path("/tmp/niri-autoblur.log") # logFIle = Path("/tmp/niri-autoblur.log")
# try: # try:
@@ -46,7 +49,7 @@ def swwwLoadImg(namespace: str, wallpaper: Path):
"--transition-duration", "--transition-duration",
"0.5", "0.5",
] ]
_log(" ".join(cmd)) _log(f"[SWWW] {" ".join(cmd)}")
ret = 0 ret = 0
try: try:
ret = subprocess.run(cmd, check=True).returncode ret = subprocess.run(cmd, check=True).returncode
@@ -104,6 +107,49 @@ class AutoBlur:
# init state # init state
# self.setBlurred(AutoBlur.initIsBlurred()) # self.setBlurred(AutoBlur.initIsBlurred())
# Start watching dirs
self.addWatchDir()
class WatchdogHandler(FileSystemEventHandler):
_callback = None
def __init__(self, callback):
if callback is None:
raise ValueError("callback cannot be None")
super().__init__()
self._callback = callback
def on_created(self, event):
if not event.is_directory:
src_path = str(event.src_path)
path = Path(src_path)
_log(f"[Watchdog] file created: {path}")
self._callback(path) # type: ignore
def on_moved(self, event):
if not event.is_directory:
dest_path = str(event.dest_path)
path = Path(dest_path)
_log(f"[Watchdog] file moved to: {path}")
self._callback(path) # type: ignore
def addWatchDir(self):
normalHandler = self.WatchdogHandler(self._onNormalDirEvent)
blurredHandler = self.WatchdogHandler(self._onBlurredDirEvent)
observer = Observer()
observer.schedule(normalHandler, str(self._normalDir), recursive=False)
observer.schedule(blurredHandler, str(self._blurredDir), recursive=False)
observer.start()
_log(f"[Watchdog] watching dirs: {self._normalDir}, {self._blurredDir}")
def _onNormalDirEvent(self, path: Path):
if not self._isBlurred.is_set():
self._apply(path)
def _onBlurredDirEvent(self, path: Path):
if self._isBlurred.is_set():
self._apply(path)
@staticmethod @staticmethod
def initIsBlurred() -> bool: def initIsBlurred() -> bool:
'''[ $(niri msg focused-window | wc -l) -gt 1 ]''' '''[ $(niri msg focused-window | wc -l) -gt 1 ]'''
@@ -118,9 +164,9 @@ class AutoBlur:
def setBlurred(self, isBlurred: bool) -> None: def setBlurred(self, isBlurred: bool) -> None:
# Cache state, avoid starting thread unnecessarily # Cache state, avoid starting thread unnecessarily
# if not self._isFirst and self._isBlurred.is_set() == isBlurred: if not self._isFirst and self._isBlurred.is_set() == isBlurred:
# _log("[AutoBlur] state unchanged") _log("[AutoBlur] state unchanged")
# return return
self._isFirst = False self._isFirst = False
if isBlurred: if isBlurred:
@@ -138,9 +184,9 @@ class AutoBlur:
'''Wait until wallpapers are ready & apply the correct one according to the current state''' '''Wait until wallpapers are ready & apply the correct one according to the current state'''
while True: while True:
if self._isBlurred.is_set(): if self._isBlurred.is_set():
wallpaper = _get_first_file(self._blurredDir) wallpaper = getFirstFile(self._blurredDir)
else: else:
wallpaper = _get_first_file(self._normalDir) wallpaper = getFirstFile(self._normalDir)
if wallpaper is not None and wallpaper.exists(): if wallpaper is not None and wallpaper.exists():
if self._apply(wallpaper): if self._apply(wallpaper):
@@ -195,11 +241,11 @@ def handleEvent(event_name, payload):
_log(f"[EventHandler] unhandled event: {event_name}") _log(f"[EventHandler] unhandled event: {event_name}")
def print_event(eventName, payload): def printEvent(eventName, payload):
_log(f"[EventHandler] event: {eventName}, payload:\n{json.dumps(payload, indent=2, ensure_ascii=False)}") _log(f"[EventHandler] event: {eventName}, payload:\n{json.dumps(payload, indent=2, ensure_ascii=False)}")
def connect_niri(niriSocket: str, handler) -> bool: def connectNiri(niriSocket: str, handler) -> bool:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try: try:
sock.connect(niriSocket) sock.connect(niriSocket)
@@ -260,6 +306,9 @@ def connect_niri(niriSocket: str, handler) -> bool:
if __name__ == "__main__": if __name__ == "__main__":
# connectNiri(getNiriSocket(), printEvent)
# exit(0)
desktop = environ.get("XDG_CURRENT_DESKTOP", "") desktop = environ.get("XDG_CURRENT_DESKTOP", "")
if desktop == "niri": if desktop == "niri":
_log("[Main] running in Niri") _log("[Main] running in Niri")
@@ -272,22 +321,22 @@ if __name__ == "__main__":
_log("[Main] loading initial wallpapers") _log("[Main] loading initial wallpapers")
# Init wallpaper for backdrop # Init wallpaper for backdrop
blurred = _get_first_file(BLURRED_WALLPAPER_DIR) blurred = getFirstFile(BLURRED_WALLPAPER_DIR)
if blurred: if blurred:
swwwLoadImg("backdrop", blurred) swwwLoadImg("backdrop", blurred)
# Init wallpaper for background # Init wallpaper for background
normal = _get_first_file(NORMAL_WALLPAPER_DIR) normal = getFirstFile(NORMAL_WALLPAPER_DIR)
if normal: if normal:
swwwLoadImg("background", normal) swwwLoadImg("background", normal)
# Connect to Niri socket # Connect to Niri socket
_log(f"[Main] connecting to Niri socket") _log(f"[Main] connecting to Niri socket")
niri_socket = get_niri_socket() niri_socket = getNiriSocket()
if not niri_socket: if not niri_socket:
_log("[Main] NIRI_SOCKET environment variable is not set.") _log("[Main] NIRI_SOCKET environment variable is not set.")
exit(1) exit(1)
if not connect_niri(niri_socket, handleEvent): if not connectNiri(niri_socket, handleEvent):
exit(1) exit(1)
elif desktop == "Hyprland": elif desktop == "Hyprland":
_log("[Main] running in Hyprland") _log("[Main] running in Hyprland")
@@ -297,10 +346,13 @@ if __name__ == "__main__":
sleep(1) # similarly sleep(1) # similarly
_log("[Main] loading initial wallpaper") _log("[Main] loading initial wallpaper")
normal = _get_first_file(NORMAL_WALLPAPER_DIR) normal = getFirstFile(NORMAL_WALLPAPER_DIR)
if normal: if normal:
swwwLoadImg("background", normal) swwwLoadImg("background", normal)
# Wait indefinitely # Wait indefinitely
while True: while True:
sleep(3600) sleep(3600)
else:
_log(f"[Main] unsupported desktop environment: {desktop}")
exit(1)