hyprshell_core_lib/
theme_icon_cache.rs1use std::collections::BTreeSet;
3use std::env;
4use std::fs::read_dir;
5use std::path::{Path, PathBuf};
6use std::sync::{Mutex, MutexGuard, OnceLock};
7use std::time::Instant;
8use tracing::{debug, span, Level};
9
10fn get_icon_map() -> &'static Mutex<BTreeSet<Box<str>>> {
11 static MAP_LOCK: OnceLock<Mutex<BTreeSet<Box<str>>>> = OnceLock::new();
12 MAP_LOCK.get_or_init(|| Mutex::new(BTreeSet::new()))
13}
14
15pub fn init_icon_map(icon_names: Vec<String>, search_path: Vec<PathBuf>, threads: bool) {
16 let _span = span!(Level::TRACE, "init_icon_map").entered();
17 let mut map = get_icon_map().lock().expect("Failed to lock icon map");
18 let instant = Instant::now();
19
20 debug!("found {} icons from theme", icon_names.len());
21 for icon in icon_names {
22 map.insert(icon.into_boxed_str());
23 }
24 drop(map);
25
26 if env::var_os("HYPRSHELL_NO_ALL_ICONS").is_none() {
28 for path in search_path {
29 if path.exists() {
30 if threads {
31 std::thread::spawn(move || {
32 let paths = collect_unique_files_recursive(&path);
33 debug!(
34 "found {} icons from filesystem in {path:?} paths (using threads)",
35 paths.len()
36 );
37 let mut map2 = get_icon_map().lock().expect("Failed to lock icon map");
38 map2.extend(paths);
39 drop(map2)
40 });
41 } else {
42 let paths = collect_unique_files_recursive(&path);
43 debug!(
44 "found {} icons from filesystem in {path:?} paths",
45 paths.len()
46 );
47 let mut map2 = get_icon_map().lock().expect("Failed to lock icon map");
48 map2.extend(paths);
49 drop(map2)
50 }
51 }
52 }
53 }
54 debug!("icon map filled in {:?}", instant.elapsed());
55}
56
57pub fn get_all_icons<'a>() -> MutexGuard<'a, BTreeSet<Box<str>>> {
58 get_icon_map().lock().expect("Failed to lock icon map")
59}
60
61pub fn theme_has_icon_name(name: &str) -> bool {
62 let map = get_icon_map().lock().expect("Failed to lock icon map");
63 map.contains(&Box::from(name))
64}
65fn collect_unique_files_recursive(dir: &Path) -> Vec<Box<str>> {
66 let mut files = Vec::with_capacity(5000);
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 let boxed_name = name.into_owned().into_boxed_str();
82 files.push(boxed_name.clone());
83 names.insert(boxed_name);
84 }
85 }
86 }
87 }
88 }
89 }
90
91 files
92}