diff --git a/Cargo.lock b/Cargo.lock index 069f385..bac1510 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,12 +23,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "ahash" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" - [[package]] name = "ahash" version = "0.7.6" @@ -40,15 +34,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - [[package]] name = "aliasable" version = "0.1.3" @@ -87,23 +72,6 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" -[[package]] -name = "async-trait" -version = "0.1.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84f9ebcc6c1f5b8cb160f6990096a5c127f423fcb6e1ccc46c370cbdfb75dfc" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async_once" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ce4f10ea3abcd6617873bae9f91d1c5332b4a778bd9ce34d0cd517474c1de82" - [[package]] name = "autocfg" version = "1.1.0" @@ -173,44 +141,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" -[[package]] -name = "cached" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5877db5d1af7fae60d06b5db9430b68056a69b3582a0be8e3691e87654aeb6" -dependencies = [ - "async-trait", - "async_once", - "cached_proc_macro", - "cached_proc_macro_types", - "futures", - "hashbrown 0.13.2", - "instant", - "lazy_static", - "once_cell", - "thiserror", - "tokio", -] - -[[package]] -name = "cached_proc_macro" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10ca87c81aaa3a949dbbe2b5e6c2c45dbc94ba4897e45ea31ff9ec5087be3dc" -dependencies = [ - "cached_proc_macro_types", - "darling", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "cached_proc_macro_types" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" - [[package]] name = "cc" version = "1.0.79" @@ -529,15 +459,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "dlv-list" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68df3f2b690c1b86e65ef7830956aededf3cb0a16f898f79b9a6f421a7b6211b" -dependencies = [ - "rand 0.8.5", -] - [[package]] name = "either" version = "1.8.1" @@ -612,15 +533,6 @@ dependencies = [ "color-maps", ] -[[package]] -name = "file-locker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91c8a37357ab5d51f1a8053900fe8374283449695c5418b66f45deece50705f" -dependencies = [ - "nix 0.25.1", -] - [[package]] name = "flagset" version = "0.4.3" @@ -706,83 +618,24 @@ dependencies = [ "ttf-parser", ] -[[package]] -name = "freedesktop_entry_parser" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db9c27b72f19a99a895f8ca89e2d26e4ef31013376e56fdafef697627306c3e4" -dependencies = [ - "nom", - "thiserror", -] - [[package]] name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -[[package]] -name = "futures" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" -dependencies = [ - "futures-core", - "futures-sink", -] - [[package]] name = "futures-core" version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" -[[package]] -name = "futures-io" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" - [[package]] name = "futures-sink" version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" -[[package]] -name = "futures-task" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" - -[[package]] -name = "futures-util" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" -dependencies = [ - "futures-core", - "futures-sink", - "futures-task", - "pin-project-lite", - "pin-utils", -] - [[package]] name = "getrandom" version = "0.2.8" @@ -855,27 +708,12 @@ dependencies = [ "crunchy", ] -[[package]] -name = "hashbrown" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" -dependencies = [ - "ahash 0.4.7", -] - [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" - [[package]] name = "heck" version = "0.4.1" @@ -971,7 +809,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", - "hashbrown 0.12.3", + "hashbrown", "serde", ] @@ -1066,29 +904,6 @@ version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" -[[package]] -name = "linicon" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee8c5653188a809616c97296180a0547a61dba205bcdcbdd261dbd022a25fd9" -dependencies = [ - "file-locker", - "freedesktop_entry_parser", - "linicon-theme", - "memmap2", - "thiserror", -] - -[[package]] -name = "linicon-theme" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4f8240c33bb08c5d8b8cdea87b683b05e61037aa76ff26bef40672cc6ecbb80" -dependencies = [ - "freedesktop_entry_parser", - "rust-ini", -] - [[package]] name = "link-cplusplus" version = "1.0.8" @@ -1159,15 +974,6 @@ dependencies = [ "libc", ] -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - [[package]] name = "memoffset" version = "0.7.1" @@ -1186,12 +992,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniz_oxide" version = "0.6.2" @@ -1240,20 +1040,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "nix" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" -dependencies = [ - "autocfg", - "bitflags", - "cfg-if", - "libc", - "memoffset 0.6.5", - "pin-utils", -] - [[package]] name = "nix" version = "0.26.2" @@ -1268,16 +1054,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1364,16 +1140,6 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" -[[package]] -name = "ordered-multimap" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c672c7ad9ec066e428c00eb917124a06f08db19e2584de982cc34b1f4c12485" -dependencies = [ - "dlv-list", - "hashbrown 0.9.1", -] - [[package]] name = "os_str_bytes" version = "6.4.1" @@ -1566,7 +1332,6 @@ dependencies = [ name = "protostar" version = "0.4.0" dependencies = [ - "cached", "clap", "color-eyre", "directories", @@ -1575,11 +1340,9 @@ dependencies = [ "glam 0.22.0", "image", "lazy_static", - "linicon", "manifest-dir-macros", "mint", - "nix 0.26.2", - "regex", + "nix", "resvg", "rustc-hash", "serde", @@ -1725,8 +1488,6 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ - "aho-corasick", - "memchr", "regex-syntax", ] @@ -1804,16 +1565,6 @@ dependencies = [ "xmlparser", ] -[[package]] -name = "rust-ini" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63471c4aa97a1cf8332a5f97709a79a4234698de6a1f5087faf66f2dae810e22" -dependencies = [ - "cfg-if", - "ordered-multimap", -] - [[package]] name = "rustc-demangle" version = "0.1.21" @@ -2485,7 +2236,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "371436099f2980de56dc385b615696d3eabbdac9649a72b85f9d75f68474fa9c" dependencies = [ - "ahash 0.7.6", + "ahash", "byteorder", "lazy_static", "parking_lot 0.11.2", diff --git a/README.md b/README.md index bf01292..d811646 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,6 @@ TODO: 3. ~~Use XDG_CACHE_DIR to rasterize svgs (done)~~ 4. Make sure it's using the current icon theme 5. Design a better app launcher ui + +TO RUN DOCK +cargo run --example sirius diff --git a/examples/sirius.rs b/examples/sirius.rs new file mode 100644 index 0000000..fa02356 --- /dev/null +++ b/examples/sirius.rs @@ -0,0 +1,165 @@ +#![allow(dead_code)] + +use color_eyre::eyre::Result; +use glam::Vec3; +use manifest_dir_macros::directory_relative_path; +use protostar::protostar::ProtoStar; +use stardust_xr_fusion::{ + client::{Client, FrameInfo, RootHandler}, + core::values::Transform, + drawable::{MaterialParameter, Model, ResourceID}, + node::NodeError, input::{InputData, InputDataType}, spatial::Spatial, fields::BoxField, +}; +use stardust_xr_molecules::{touch_plane::TouchPlane, Grabbable, GrabData}; + + + +#[tokio::main(flavor = "current_thread")] +async fn main() -> Result<()> { + color_eyre::install()?; + let (client, event_loop) = Client::connect_with_async_loop().await?; + client.set_base_prefixes(&[directory_relative_path!("res")]); + + let _wrapped_root = client.wrap_root(Sirius::new(&client)?)?; + + tokio::select! { + _ = tokio::signal::ctrl_c() => (), + e = event_loop => e??, + } + Ok(()) +} + +struct Star { + cli: ProtoStar +} + +impl Star { + fn new(parent: &Spatial, _name: Option<&str>,path: String) -> Option { + let cli = ProtoStar::new_raw(parent, None, path).unwrap(); + Some(Star { + cli, + }) + } +} + +impl RootHandler for Star { + fn frame(&mut self, info: FrameInfo) { + self.cli.frame(info); + } +} + +const LOCATION_OF_TELESCOPE: &str = "/home/bc/repos/stardust/"; +const EXECUTABLES: [&str; 3] = [ + "magnetar", + "atmosphere", + "manifold", +]; + +struct Sirius { + touch_plane: TouchPlane, + model: Model, + root: Spatial, + clients: Vec, + visibility: bool, + grabbable: Grabbable, + +} +impl Sirius { + fn new(client: &Client) -> Result { + let mut client_list: Vec<(Option<&str>, String)> = Vec::new(); + for executable in EXECUTABLES { + client_list.push((Some(executable), format!("{}telescope/repos/{}/target/release/{}", LOCATION_OF_TELESCOPE, executable, executable))); + } + let root = Spatial::create(client.get_root(), Transform::default(), false).unwrap(); + + let field = BoxField::create(&root, Transform::default(), Vec3::from([0.1;3])).unwrap(); + let grabbable = Grabbable::new(&root, Transform::default(), &field, GrabData::default())?; + let touch_plane = TouchPlane::new(grabbable.content_parent(), Transform::default(), [0.1; 2], 0.03)?; + let mut clients = Vec::new(); + for clientkv in client_list { + clients.push(Star::new(grabbable.content_parent(), clientkv.0, clientkv.1).unwrap()); + } + let model = Model::create( + grabbable.content_parent(), + Transform::default(), + &ResourceID::new_namespaced("protostar", "button"), + )?; + field.set_spatial_parent(grabbable.content_parent())?; + let visibility = false; + + Ok(Sirius { touch_plane, model , root, clients, visibility, grabbable}) + } + +// fn left_hand(input_data: &InputData, _: &()) -> bool { +// match &input_data.input { +// InputDataType::Hand(h) => !h.right, +// _ => false, +// } +// } +} +impl RootHandler for Sirius { + fn frame(&mut self, info: FrameInfo) { + + for app in &mut self.clients { + app.frame(info); + } + + self.grabbable.update(&info); + self.touch_plane.update(); + if self.touch_plane.touch_started() { + println!("Touch started"); + self.visibility = !self.visibility; + match self.visibility { + true => for star in self.clients.iter().enumerate() { + let mut starpos = (star.0 as f32 +1.0)/10.0; + match starpos % 0.2 == 0.0 { + true => starpos = -starpos/2.0, + false => starpos = (starpos - 0.1)/2.0, + } + println!("{}", starpos); + star.1.cli.content_parent().set_position(Some(&self.grabbable.content_parent()), Vec3::from([starpos,0.1,0.0])).ok(); + }, + false => for star in &self.clients { + star.cli.content_parent().set_position(Some(&self.grabbable.content_parent()), Vec3::from([0.0,0.0,0.0])).ok(); + }, + + } + let color = [0.0, 1.0, 0.0, 1.0]; + self.model + .set_material_parameter(0, "color", MaterialParameter::Color(color)) + .unwrap(); + self.model + .set_material_parameter( + 0, + "emission_factor", + MaterialParameter::Color(color.map(|c| c * 0.75)), + ) + .unwrap(); + } + + if self.touch_plane.touch_stopped() { + println!("Touch ended"); + let color = [1.0, 0.0, 0.0, 1.0]; + self.model + .set_material_parameter(0, "color", MaterialParameter::Color(color)) + .unwrap(); + self.model + .set_material_parameter( + 0, + "emission_factor", + MaterialParameter::Color(color.map(|c| c * 0.5)), + ) + .unwrap(); + } + } +} + +fn position(data: &InputData) -> Vec3 { + match &data.input { + InputDataType::Hand(h) => h.palm.position.into(), + InputDataType::Pointer(w) => w.deepest_point.into(), + InputDataType::Tip(t) => t.origin.into(), + } + + +} diff --git a/res/protostar/button.glb b/res/protostar/button.glb new file mode 100644 index 0000000..ed080ba Binary files /dev/null and b/res/protostar/button.glb differ diff --git a/src/xdg.rs b/src/xdg.rs index 0ff4bf1..395a1f5 100644 --- a/src/xdg.rs +++ b/src/xdg.rs @@ -1,5 +1,4 @@ use color_eyre::eyre::Result; -use lazy_static::lazy_static; use linicon; use regex::Regex; use resvg::render; @@ -13,90 +12,33 @@ use std::ffi::OsString; use std::fs::create_dir_all; use std::fs::File; use std::io::{BufRead, BufReader, ErrorKind}; -use std::io::{Read, Write}; +use std::os::unix::fs::symlink; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::Mutex; use std::{env, fs}; use walkdir::WalkDir; - -#[serde_as] -#[derive(Deserialize, Serialize)] -struct ImageCache { - path: PathBuf, - #[serde_as(as = "Vec<(_, _)>")] - pub map: HashMap, -} - -impl ImageCache { - fn new(path: PathBuf) -> Self { - if let Ok(mut file) = File::open(&path) { - let mut buf = vec![]; - if file.read_to_end(&mut buf).is_ok() { - if let Ok(cache) = serde_json::from_slice(&buf[..]) { - return cache; - } - } - } - - //There was no file, or the file failed to load, create a new World. - ImageCache { - path, - map: HashMap::new(), - } - } - - fn insert(&mut self, k: String, v: PathBuf) { - self.map.insert(k, v); - } - - fn save(&self) { - let mut f = File::create(&self.path).unwrap(); - let buf = serde_json::to_vec(&self).unwrap(); - f.write_all(&buf[..]).unwrap(); - } -} - -lazy_static! { - static ref IMAGE_CACHE: Mutex = Mutex::new(ImageCache::new( - get_image_cache_dir().join("imagechache.map") - )); -} - fn get_data_dirs() -> Vec { let xdg_data_dirs_str = std::env::var("XDG_DATA_DIRS").unwrap_or_default(); - let xdg_data_dirs = xdg_data_dirs_str + // Append the applications directory to each data directory + let app_dirs = xdg_data_dirs .split(":") - .filter_map(|dir| PathBuf::from_str(dir).ok()); + .map(|dir| Path::new(dir).join("applications")); - let data_home = dirs::home_dir() - .unwrap_or(PathBuf::from_str("/usr/share/").expect( - "No XDG_DATA_DIR set, no HOME directory found and no /usr/share direcotry found", - )) + // Get the user's local applications directory + let local_app_dir = dirs::home_dir() + .unwrap() .join(".local") - .join("share"); + .join("share") + .join("applications"); - xdg_data_dirs - .chain([data_home].into_iter()) - .filter(|dir| dir.exists() && dir.is_dir()) - .collect() -} - -fn get_app_dirs() -> Vec { - get_data_dirs() - .into_iter() - .map(|dir| dir.join("applications")) - .filter(|dir| dir.exists() && dir.is_dir()) - .collect() -} - -pub fn get_desktop_files() -> Vec { let desktop_extension = OsString::from_str("desktop").unwrap(); + // Get the list of directories to search - let app_dirs = get_app_dirs(); app_dirs - .into_iter() + .chain(Some(local_app_dir)) + .filter(|dir| dir.exists() && dir.is_dir()) .flat_map(|dir| { // Follow symlinks and recursively search directories WalkDir::new(dir) @@ -137,10 +79,6 @@ pub fn parse_desktop_file(path: PathBuf) -> Result { let mut command = None; let mut categories = Vec::new(); let mut icon = None; - let mut no_display = false; - let mut desktop_entry_found = false; - - let re = Regex::new(r"^\[([^\]]*)\]$").unwrap(); // Loop through each line of the file for line in reader.lines() { @@ -154,14 +92,6 @@ pub fn parse_desktop_file(path: PathBuf) -> Result { continue; } - if let Some(captures) = re.captures(&line) { - let entry = captures.get(1).unwrap(); - desktop_entry_found = entry.as_str().contains("Desktop Entry"); - } - - if !desktop_entry_found { - continue; - } // Split the line into a key-value pair by looking for the first "=" character let parts = line.split_once('='); let (key, value) = match parts { @@ -181,12 +111,6 @@ pub fn parse_desktop_file(path: PathBuf) -> Result { .collect() } "Icon" => icon = Some(value.to_string()), - "NoDisplay" => { - no_display = match value { - "true" => true, - _ => false, - } - } _ => (), // Ignore unknown keys } } @@ -198,7 +122,6 @@ pub fn parse_desktop_file(path: PathBuf) -> Result { command, categories, icon, - no_display, }) } @@ -230,96 +153,77 @@ pub struct DesktopFile { pub command: Option, pub categories: Vec, pub icon: Option, - pub no_display: bool, } impl DesktopFile { - pub fn get_raw_icons(&self) -> Vec { + pub fn get_raw_icons(&self) -> Vec { // Get the name of the icon from the DesktopFile struct let Some(icon_name) = self.icon.as_ref() else { return Vec::new(); }; let test_icon_path = self.path.join(Path::new(icon_name)); if test_icon_path.exists() { - if let Some(icon) = Icon::from_path(test_icon_path, 128) { + return RawIconType::from_path(test_icon_path) + .map(|i| vec![i]) + .unwrap_or_default(); + } + + let cache_icon_path = get_image_cache_dir().join(icon_name).canonicalize(); + if cache_icon_path.is_ok() { + if let Some(icon) = Icon::from_path(cache_icon_path.unwrap(), 128) { return vec![icon]; } } - if let Some(cache_icon_path) = IMAGE_CACHE.lock().unwrap().map.get(icon_name) { - if cache_icon_path.exists() { - if let Some(icon) = Icon::from_path(cache_icon_path.to_owned(), 128) { - return vec![icon]; - } - } - } + // Get the current icon theme from the XDG_ICON_THEME environment variable, or use "hicolor" as the default theme if the variable is not defined + let icon_theme = env::var_os("XDG_ICON_THEME").unwrap_or("hicolor".into()); - let mut icons_iter = linicon::lookup_icon(icon_name) - .use_fallback_themes(false) - .peekable(); + // Get the XDG_DATA_HOME and XDG_DATA_DIRS environment variables, and split the XDG_DATA_DIRS variable into a list of directories + let Some(xdg_data_dirs) = env::var_os("XDG_DATA_DIRS") else { return Vec::new(); }; + let Ok(binding) = xdg_data_dirs.into_string() else { return Vec::new(); }; + let xdg_data_dirs = binding.split(":").map(Path::new); - if icons_iter.peek().is_none() { - //dbg!("No icons found in current theme"); - icons_iter = linicon::lookup_icon(icon_name).peekable(); - } - - let sized_png: Vec = icons_iter - .filter_map(|i| i.ok()) - .filter(|i| i.icon_type != linicon::IconType::XMP) //TODO: support XMP - .map(|i| Icon::from_path(i.path, i.max_size - 2).unwrap()) - .collect(); - sized_png + // Concatenate the XDG_DATA_HOME and XDG_DATA_DIRS directories with the default path for icon themes + xdg_data_dirs // XDG_DATA_DIRS directories + .flat_map(|dir| { + let icons_path = dir.join("icons").join(&icon_theme); + ICON_SIZES + .iter() + .map(|path| icons_path.join(path).join("apps")) + .collect::>() + }) + .filter_map(|dir| { + let dir = fs::read_dir(dir).ok()?; + Some( + dir.filter_map(|e| e.ok()) + .map(|file| file.path()) + .filter(|file| file.file_stem() == Some(&icon_name)), + ) + }) + .flatten() + .filter_map(RawIconType::from_path) + .collect() } } -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct Icon { - pub icon_type: IconType, - pub path: PathBuf, - pub size: u16, +#[derive(Debug, PartialEq, Eq)] +pub enum RawIconType { + Png(PathBuf), + Svg(PathBuf), + Gltf(PathBuf), } - -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum IconType { - Png, - Svg, - Gltf, -} -impl Icon { - pub fn from_path(path: PathBuf, size: u16) -> Option { - let icon_type = match path.extension().and_then(|ext| ext.to_str()) { - Some("png") => IconType::Png, - Some("svg") => IconType::Svg, - Some("glb") | Some("gltf") => IconType::Gltf, - _ => return None, - }; - return Some(Icon { - icon_type, - path, - size, - }); +impl RawIconType { + pub fn from_path(path: PathBuf) -> Option { + match path.extension().and_then(|ext| ext.to_str()) { + Some("png") => Some(RawIconType::Png(path)), + Some("svg") => Some(RawIconType::Svg(path)), + Some("glb") | Some("gltf") => Some(RawIconType::Gltf(path)), + _ => None, + } } pub fn cached_process(self, size: u16) -> Result { - if !IMAGE_CACHE.lock().unwrap().map.contains_key( - &self - .path - .with_extension("") - .file_name() - .unwrap() - .to_str() - .unwrap() - .to_owned(), - ) { - dbg!("Saving value in the DB"); - IMAGE_CACHE.lock().unwrap().insert( - self.path - .with_extension("") - .file_name() - .unwrap() - .to_str() - .unwrap() - .to_owned(), - self.path.clone(), - ); - IMAGE_CACHE.lock().unwrap().save(); + let new_path = + get_image_cache_dir().join(self.path.with_extension("").file_name().unwrap()); + if !new_path.exists() { + _ = symlink(self.path.clone(), new_path); } match self.icon_type { IconType::Svg => Ok(Icon::from_path(get_png_from_svg(self.path, size)?, size).unwrap()), @@ -337,7 +241,6 @@ fn test_get_icon_path() { command: None, categories: vec![], icon: Some("krita".into()), - no_display: false, }; // Call the get_icon_path() function with a size argument and store the result @@ -345,15 +248,12 @@ fn test_get_icon_path() { dbg!(&icon_paths); // Assert that the get_icon_path() function returns the expected result - assert!(icon_paths.contains( - &Icon::from_path( - PathBuf::from("/usr/share/icons/hicolor/32x32/apps/krita.png"), - 32 - ) - .unwrap() - )); + assert!(icon_paths.contains(&RawIconType::Png(PathBuf::from( + "/usr/share/icons/hicolor/16x16/apps/krita.png" + )))); } +#[cached] pub fn get_image_cache_dir() -> PathBuf { let cache_dir; if let Ok(xdg_cache_home) = std::env::var("XDG_CACHE_HOME") { @@ -367,26 +267,26 @@ pub fn get_image_cache_dir() -> PathBuf { return image_cache_dir; } -pub fn get_png_from_svg(svg_path: impl AsRef, size: u16) -> Result { +pub fn render_svg_to_png( + cache_dir: impl AsRef, + svg_path: impl AsRef, + size: u32, +) -> Result { let svg_path = fs::canonicalize(svg_path)?; - let svg_data = fs::read(svg_path.as_path())?; - let tree = Tree::from_data(svg_data.as_slice(), &resvg::usvg::Options::default()) - .map_err(|_| ErrorKind::InvalidData)?; - - let png_path = get_image_cache_dir().join(format!( - "{}-{}.png", - svg_path.file_name().unwrap().to_str().unwrap(), - svg_data.len() - )); - - if png_path.exists() { - return Ok(png_path); - } - - let mut pixmap = Pixmap::new(size.into(), size.into()).unwrap(); + let tree = Tree::from_data( + fs::read(svg_path.as_path())?.as_slice(), + &resvg::usvg::Options::default(), + ) + .map_err(|_| ErrorKind::InvalidData)?; + create_dir_all(cache_dir.as_ref())?; + let png_path = cache_dir + .as_ref() + .join(svg_path.file_name().unwrap()) + .with_extension("png"); + let mut pixmap = Pixmap::new(size, size).unwrap(); render( &tree, - FitTo::Width(size.into()), + FitTo::Width(size), Transform::identity(), pixmap.as_mut(), ); @@ -410,7 +310,7 @@ fn test_render_svg_to_png() { fs::write(&svg_path, test_svg_data).unwrap(); // Call the function with the test input and output paths and a size of 200 - let png_path = get_png_from_svg(&svg_path, 200).unwrap(); + let png_path = render_svg_to_png(".", &svg_path, 200).unwrap(); dbg!(&png_path); // Check that the output file exists