#[cfg(target_os = "linux")]
use super::awww;
use crate::platform::{Platform, detect_platform};
use crate::wallpaper::backends::WallpaperBackend;
use anyhow::{Result, anyhow};
use std::sync::Arc;
use tracing::{debug, warn};
pub struct BackendRegistry {
backends: Vec<Arc<dyn WallpaperBackend + Send + Sync>>,
}
impl Default for BackendRegistry {
fn default() -> Self {
Self::new()
}
}
impl BackendRegistry {
pub fn new() -> Self {
let mut registry = Self { backends: Vec::new() };
registry.register_platform_backends();
registry
}
fn register_platform_backends(&mut self) {
match detect_platform() {
Ok(platform) => {
debug!("Detected platform: {}", platform);
self.register_backends_for_platform(&platform);
}
Err(e) => {
warn!("Failed to detect platform: {}, registering all backends", e);
self.register_all_backends();
}
}
debug!("Registered {} wallpaper backends", self.backends.len());
}
fn register_backends_for_platform(&mut self, platform: &Platform) {
match platform {
Platform::Linux(display_server) => {
#[cfg(target_os = "linux")]
self.register_linux_backends(display_server);
#[cfg(not(target_os = "linux"))]
{
let _ = display_server; warn!("Linux backends not available on this platform");
}
}
Platform::MacOS => {
#[cfg(target_os = "macos")]
self.register_macos_backends();
#[cfg(not(target_os = "macos"))]
warn!("macOS backends not available on this platform");
}
Platform::Windows => {
#[cfg(target_os = "windows")]
self.register_windows_backends();
#[cfg(not(target_os = "windows"))]
warn!("Windows backends not available on this platform");
}
}
}
fn register_all_backends(&mut self) {
#[cfg(target_os = "linux")]
{
use crate::platform::{LinuxDisplayServer, WaylandCompositor};
self.register_linux_backends(&LinuxDisplayServer::X11);
self.register_linux_backends(&LinuxDisplayServer::Wayland(WaylandCompositor::Generic));
}
#[cfg(target_os = "macos")]
self.register_macos_backends();
#[cfg(target_os = "windows")]
self.register_windows_backends();
#[cfg(target_os = "linux")]
self.register_awww_backend();
}
#[cfg(target_os = "linux")]
fn register_linux_backends(&mut self, display_server: &crate::platform::LinuxDisplayServer) {
use super::linux::*;
match display_server {
crate::platform::LinuxDisplayServer::Wayland(compositor) => {
debug!("Registering Wayland backends for compositor: {:?}", compositor);
use crate::wallpaper::backends::AwwwBackend;
self.register_backend(Arc::new(AwwwBackend::new()));
match compositor {
crate::platform::WaylandCompositor::Sway => {
self.register_backend(Arc::new(SwaybgBackend::new()));
}
crate::platform::WaylandCompositor::Hyprland => {
self.register_backend(Arc::new(HyprpaperBackend::new()));
}
crate::platform::WaylandCompositor::Gnome => {
debug!("GNOME detected — registering gsettings backend");
self.register_backend(Arc::new(super::gnome::GnomeBackend::new()));
}
crate::platform::WaylandCompositor::Kde => {
debug!("KDE detected — only awww backend is supported for Wayland/KDE");
}
crate::platform::WaylandCompositor::Generic => {
debug!("Generic Wayland compositor — only awww backend is supported");
}
}
}
crate::platform::LinuxDisplayServer::X11 => {
self.register_backend(Arc::new(FehBackend::new()));
self.register_backend(Arc::new(NitrogenBackend::new()));
self.register_backend(Arc::new(XwallpaperBackend::new()));
}
}
}
#[cfg(target_os = "macos")]
fn register_macos_backends(&mut self) {
use super::macos::*;
self.register_backend(Arc::new(MacOSWallpaperBackend::new()));
self.register_backend(Arc::new(SwiftNativeBackend::new()));
self.register_backend(Arc::new(AppleScriptBackend::new()));
}
#[cfg(target_os = "windows")]
fn register_windows_backends(&mut self) {
use super::windows::*;
self.register_backend(Arc::new(WindowsSystemParametersBackend::new()));
}
#[cfg(target_os = "linux")]
fn register_awww_backend(&mut self) {
self.register_backend(Arc::new(awww::AwwwBackend::new()));
}
fn register_backend(&mut self, backend: Arc<dyn WallpaperBackend + Send + Sync>) {
if backend.is_available() {
debug!("Registered backend: {} (priority: {})", backend.name(), backend.priority());
self.backends.push(backend);
} else {
debug!("Backend not available: {}", backend.name());
}
}
pub fn get_best_backend(&self) -> Result<Arc<dyn WallpaperBackend + Send + Sync>> {
if self.backends.is_empty() {
return Err(anyhow!(
"No wallpaper backends available. Please install a wallpaper setter like feh, swww, or awww"
));
}
let mut sorted_backends: Vec<_> = self.backends.iter().collect();
sorted_backends.sort_by_key(|backend| std::cmp::Reverse(backend.priority()));
for backend in &sorted_backends {
if let Err(e) = backend.validate() {
debug!("Backend {} failed validation: {}", backend.name(), e);
continue;
}
debug!("Selected backend: {}", backend.name());
return Ok((*backend).clone());
}
Err(anyhow!("No working wallpaper backends found"))
}
pub fn list_backends(&self) -> Vec<String> {
let mut backends: Vec<_> = self
.backends
.iter()
.map(|b| format!("{} (priority: {}, available: {})", b.name(), b.priority(), b.is_available()))
.collect();
backends.sort();
backends
}
pub fn available_backend_names(&self) -> Vec<String> {
self.backends.iter().filter(|b| b.is_available()).map(|b| b.name().to_string()).collect()
}
}