hyprshell_core_lib/
theme_icon_cache.rs1use std::collections::BTreeSet;
4use std::env;
5use std::fs::read_dir;
6use std::path::{Path, PathBuf};
7use std::sync::{Mutex, MutexGuard, OnceLock};
8use std::time::Instant;
9use tracing::{Level, debug, span};
10
11fn get_icon_map() -> &'static Mutex<BTreeSet<Box<str>>> {
12 static MAP_LOCK: OnceLock<Mutex<BTreeSet<Box<str>>>> = OnceLock::new();
13 MAP_LOCK.get_or_init(|| Mutex::new(BTreeSet::new()))
14}
15
16pub fn init_icon_map(icon_names: Vec<String>, search_path: Vec<PathBuf>, in_background: bool) {
17 let _span = span!(Level::TRACE, "init_icon_map").entered();
18 let mut map = get_icon_map().lock().expect("Failed to lock icon map");
19 let instant = Instant::now();
20
21 debug!("found {} icons from theme", icon_names.len());
22 for icon in icon_names {
23 map.insert(icon.into_boxed_str());
24 }
25 drop(map);
26
27 if env::var_os("HYPRSHELL_NO_ALL_ICONS").is_none() {
29 for path in search_path {
30 if path.exists() {
31 if in_background {
32 std::thread::spawn(move || {
33 let paths = collect_unique_files_recursive(&path);
34 debug!(
35 "found {} icons from filesystem in {path:?} paths (in background)",
36 paths.len()
37 );
38 let mut map2 = get_icon_map().lock().expect("Failed to lock icon map");
39 map2.extend(paths);
40 drop(map2)
41 });
42 } else {
43 let paths = collect_unique_files_recursive(&path);
44 debug!(
45 "found {} icons from filesystem in {path:?} paths",
46 paths.len()
47 );
48 let mut map2 = get_icon_map().lock().expect("Failed to lock icon map");
49 map2.extend(paths);
50 drop(map2)
51 }
52 }
53 }
54 }
55 debug!("icon map filled in {:?}", instant.elapsed());
56}
57
58pub fn get_all_icons<'a>() -> MutexGuard<'a, BTreeSet<Box<str>>> {
59 get_icon_map().lock().expect("Failed to lock icon map")
60}
61
62pub fn theme_has_icon_name(name: &str) -> bool {
63 let map = get_icon_map().lock().expect("Failed to lock icon map");
64 map.contains(&Box::from(name))
65}
66fn collect_unique_files_recursive(dir: &Path) -> BTreeSet<Box<str>> {
67 let mut names = BTreeSet::new();
68 let mut dirs_to_visit = vec![dir.to_path_buf()];
69
70 while let Some(current_dir) = dirs_to_visit.pop() {
71 if current_dir.is_dir() {
72 if let Ok(entries) = read_dir(¤t_dir) {
73 for entry in entries.flatten() {
74 let path = entry.path();
75 if path.is_dir() {
76 dirs_to_visit.push(path);
77 } else if let Some(name_osstr) = path.file_stem() {
78 let name = name_osstr.to_string_lossy();
80 if !name.is_empty() && !names.contains(&*name) {
81 names.insert(name.into_owned().into_boxed_str());
82 }
83 }
84 }
85 }
86 }
87 }
88 names
89}