diff --git a/src/caelestia/utils/paths.py b/src/caelestia/utils/paths.py index 3223b900..c908a403 100644 --- a/src/caelestia/utils/paths.py +++ b/src/caelestia/utils/paths.py @@ -3,9 +3,41 @@ import os import shutil import tempfile +from dataclasses import dataclass from pathlib import Path from typing import Any + +@dataclass(frozen=True) +class WallpaperPaths: + base: Path + path: Path + current: Path + thumbnail: Path + + +def resolve_wallpaper_paths(monitor: str | None) -> WallpaperPaths: + base = c_state_dir / "wallpapers" / (monitor or "default") + return WallpaperPaths( + base=base, + path=base / "path.txt", + current=base / "current", + thumbnail=base / "thumbnail.jpg", + ) + + +# TODO: Perhaps shift to using this for all the paths? example: +""" +@dataclass(frozen=True) +class XDGDirs: +config: Path = Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config")) +data: Path = Path(os.getenv("XDG_DATA_HOME", Path.home() / ".local/share")) +state: Path = Path(os.getenv("XDG_STATE_HOME", Path.home() / ".local/state")) +cache: Path = Path(os.getenv("XDG_CACHE_HOME", Path.home() / ".cache")) +pictures: Path = Path(os.getenv("XDG_PICTURES_DIR", Path.home() / "Pictures")) +videos: Path = Path(os.getenv("XDG_VIDEOS_DIR", Path.home() / "Videos")) + """ + config_dir: Path = Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config")) data_dir: Path = Path(os.getenv("XDG_DATA_HOME", Path.home() / ".local/share")) state_dir: Path = Path(os.getenv("XDG_STATE_HOME", Path.home() / ".local/state")) @@ -29,6 +61,7 @@ scheme_cache_dir: Path = c_cache_dir / "schemes" wallpapers_dir: Path = Path(os.getenv("CAELESTIA_WALLPAPERS_DIR", pictures_dir / "Wallpapers")) + wallpaper_path_path: Path = c_state_dir / "wallpaper/path.txt" wallpaper_link_path: Path = c_state_dir / "wallpaper/current" wallpaper_thumbnail_path: Path = c_state_dir / "wallpaper/thumbnail.jpg" diff --git a/src/caelestia/utils/wallpaper.py b/src/caelestia/utils/wallpaper.py index 02d3f4e8..67a98288 100644 --- a/src/caelestia/utils/wallpaper.py +++ b/src/caelestia/utils/wallpaper.py @@ -15,6 +15,8 @@ from caelestia.utils.material import get_colours_for_image from caelestia.utils.colourfulness import get_variant from caelestia.utils.paths import ( + WallpaperPaths, + resolve_wallpaper_paths, compute_hash, user_config_path, wallpaper_link_path, @@ -148,55 +150,89 @@ def convert_gif(wall: Path) -> Path: return output_path -def set_wallpaper(wall: Path, no_smart: bool) -> None: - # Make path absolute - wall = Path(wall).resolve() +# TODO: Perhaps place somewhere else: +# START ============================= - if not is_valid_image(wall): - raise ValueError(f'"{wall}" is not a valid image') - # Use gif's 1st frame for thumb only - wall_cache = convert_gif(wall) if wall.suffix.lower() == ".gif" else wall - - # Update files - wallpaper_path_path.parent.mkdir(parents=True, exist_ok=True) - wallpaper_path_path.write_text(str(wall)) - wallpaper_link_path.parent.mkdir(parents=True, exist_ok=True) - wallpaper_link_path.unlink(missing_ok=True) - wallpaper_link_path.symlink_to(wall) +def check_monitor_exists(monitorName: str) -> bool: + monitors = cast(list[dict[str, int]], message("monitors")) + for monitor in monitors: + if monitor.get("name") == monitorName: + return True + return False - cache = wallpapers_cache_dir / compute_hash(wall_cache) - # Generate thumbnail or get from cache - thumb = get_thumb(wall_cache, cache) - wallpaper_thumbnail_path.parent.mkdir(parents=True, exist_ok=True) - wallpaper_thumbnail_path.unlink(missing_ok=True) - wallpaper_thumbnail_path.symlink_to(thumb) +# ============================= END - scheme = get_scheme() - # Change mode and variant based on wallpaper colour - if scheme.name == "dynamic" and not no_smart: - smart_opts = get_smart_opts(wall_cache, cache) - scheme.mode = smart_opts["mode"] - scheme.variant = smart_opts["variant"] +def set_wallpaper(wall: Path, no_smart: bool, monitor: str | None = None) -> None: + # Make path absolute + wall = Path(wall).resolve() - # Update colours - scheme.update_colours() - apply_colours(scheme.colours, scheme.mode) + if not is_valid_image(wall): + raise ValueError(f'"{wall}" is not a valid image') - # Run custom post-hook if configured - try: - cfg = json.loads(user_config_path.read_text()).get("wallpaper", {}) - if post_hook := cfg.get("postHook"): - subprocess.run( - post_hook, - shell=True, - env={**os.environ, "WALLPAPER_PATH": str(wall)}, - stderr=subprocess.DEVNULL, - ) - except (FileNotFoundError, json.JSONDecodeError): - pass + if monitor is not None and check_monitor_exists(monitor): + wall_cache = convert_gif(wall) if wall.suffix.lower() == ".gif" else wall + cache = wallpapers_cache_dir / compute_hash(wall_cache) + thumb = get_thumb(wall_cache, cache) + + paths = resolve_wallpaper_paths(monitor) + paths.base.mkdir(parents=True, exist_ok=True) + paths.path.write_text(str(wall)) + paths.current.unlink(missing_ok=True) + paths.current.symlink_to(wall) + paths.thumbnail.unlink(missing_ok=True) + paths.thumbnail.symlink_to(thumb) + + # TODO: Insert code for dynamic color gen here. + # Base it on wallpaper on primary monitor? + # Base it on all wallpapers for all monitors? + + # HACK: Keep for backwards compatibility: + else: + # Use gif's 1st frame for thumb only + wall_cache = convert_gif(wall) if wall.suffix.lower() == ".gif" else wall + + # Update files + wallpaper_path_path.parent.mkdir(parents=True, exist_ok=True) + wallpaper_path_path.write_text(str(wall)) + wallpaper_link_path.parent.mkdir(parents=True, exist_ok=True) + wallpaper_link_path.unlink(missing_ok=True) + wallpaper_link_path.symlink_to(wall) + + cache = wallpapers_cache_dir / compute_hash(wall_cache) + + # Generate thumbnail or get from cache + thumb = get_thumb(wall_cache, cache) + wallpaper_thumbnail_path.parent.mkdir(parents=True, exist_ok=True) + wallpaper_thumbnail_path.unlink(missing_ok=True) + wallpaper_thumbnail_path.symlink_to(thumb) + + scheme = get_scheme() + + # Change mode and variant based on wallpaper colour + if scheme.name == "dynamic" and not no_smart: + smart_opts = get_smart_opts(wall_cache, cache) + scheme.mode = smart_opts["mode"] + scheme.variant = smart_opts["variant"] + + # Update colours + scheme.update_colours() + apply_colours(scheme.colours, scheme.mode) + + # Run custom post-hook if configured + try: + cfg = json.loads(user_config_path.read_text()).get("wallpaper", {}) + if post_hook := cfg.get("postHook"): + subprocess.run( + post_hook, + shell=True, + env={**os.environ, "WALLPAPER_PATH": str(wall)}, + stderr=subprocess.DEVNULL, + ) + except (FileNotFoundError, json.JSONDecodeError): + pass def set_random(args: Namespace) -> None: