feat: partial implementation of wallpaper settings
Will take some time to refactor the rest
This commit is contained in:
parent
14a4a23bc9
commit
a4eee2186c
14 changed files with 375 additions and 80 deletions
1
pages/desktop/src/lib.rs
Normal file
1
pages/desktop/src/lib.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod wallpaper;
|
||||
130
pages/desktop/src/wallpaper.rs
Normal file
130
pages/desktop/src/wallpaper.rs
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
pub use cosmic_bg_config::{Config, Entry, Output};
|
||||
use image::RgbaImage;
|
||||
use std::{
|
||||
collections::hash_map::DefaultHasher,
|
||||
fs::DirEntry,
|
||||
hash::{Hash, Hasher},
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use tokio::sync::mpsc::{self, Receiver};
|
||||
|
||||
pub fn config() -> Config {
|
||||
let helper = Config::helper().expect("failed to get helper for cosmic bg config");
|
||||
|
||||
match Config::load(&helper) {
|
||||
Ok(conf) => conf,
|
||||
Err(why) => {
|
||||
tracing::warn!(?why, "Config file error, falling back to defaults");
|
||||
Config::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(config: &mut Config, entry: Entry) {
|
||||
if let Ok(context) = Config::helper() {
|
||||
tracing::info!(
|
||||
"setting wallpaper for {} to {}",
|
||||
entry.output.to_string(),
|
||||
entry.source.display()
|
||||
);
|
||||
if let Err(why) = config.set_entry(&context, entry) {
|
||||
tracing::error!(?why, "failed to set background");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Path to directory where wallpaper thumbnails are stored.
|
||||
#[must_use]
|
||||
pub fn cache_dir() -> Option<PathBuf> {
|
||||
dirs::cache_dir().map(|path| {
|
||||
let cache = path.join("cosmic-settings/wallpapers");
|
||||
let _res = std::fs::create_dir_all(&cache);
|
||||
cache
|
||||
})
|
||||
}
|
||||
|
||||
/// Loads wallpapers in parallel by spawning tasks with a rayon thread pool.
|
||||
#[must_use]
|
||||
pub fn load_each_from_path(path: PathBuf) -> Receiver<(PathBuf, RgbaImage)> {
|
||||
let cache_dir = Arc::new(cache_dir());
|
||||
|
||||
let (tx, rx) = mpsc::channel(1);
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let mut paths = vec![path];
|
||||
|
||||
while let Some(path) = paths.pop() {
|
||||
if let Ok(dir) = path.read_dir() {
|
||||
for entry in dir.filter_map(Result::ok) {
|
||||
let Ok(file_type) = entry.file_type() else {
|
||||
continue
|
||||
};
|
||||
|
||||
let path = entry.path();
|
||||
|
||||
if file_type.is_dir() {
|
||||
paths.push(path);
|
||||
} else if file_type.is_file() {
|
||||
let tx = tx.clone();
|
||||
let cache_dir = cache_dir.clone();
|
||||
rayon::spawn_fifo(move || {
|
||||
let thumbnail =
|
||||
load_thumbnail(cache_dir.as_deref(), &path, &entry, 300, 169);
|
||||
if let Some(image) = thumbnail {
|
||||
let _res = tx.blocking_send((path, image));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
rx
|
||||
}
|
||||
|
||||
/// Generates and caches the thumbnail of a wallpaper.
|
||||
///
|
||||
///
|
||||
/// Caching reduces time required to load a wallpaper by 99%.
|
||||
#[must_use]
|
||||
pub fn load_thumbnail(
|
||||
cache_dir: Option<&Path>,
|
||||
path: &Path,
|
||||
entry: &DirEntry,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> Option<RgbaImage> {
|
||||
if let Some(cache_dir) = cache_dir {
|
||||
if let Ok(ctime) = entry.metadata().and_then(|meta| meta.created()) {
|
||||
// Search for thumbnail by a unique hash string.
|
||||
let mut hasher = DefaultHasher::new();
|
||||
path.hash(&mut hasher);
|
||||
ctime.hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
let thumbnail_path = cache_dir.join(format!("{hash:x}.png"));
|
||||
|
||||
// Load image from thumbnail if it exists and can be opened.
|
||||
if thumbnail_path.exists() {
|
||||
if let Ok(image) = image::open(&thumbnail_path) {
|
||||
return Some(image.into_rgba8());
|
||||
}
|
||||
}
|
||||
|
||||
// Create new thumbnail and save it if not.
|
||||
return image::open(path).ok().map(|mut image| {
|
||||
image = image.thumbnail_exact(width, height);
|
||||
let _res = image.save(&thumbnail_path);
|
||||
image.into_rgba8()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Generate thumbnail from wallpaper without saving it
|
||||
image::open(path).ok().map(|mut image| {
|
||||
image = image.thumbnail_exact(width, height);
|
||||
image.into_rgba8()
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue