From 570f89bebabd6f47d9cc9dce137405c5d04ff76f Mon Sep 17 00:00:00 2001 From: nik012003 Date: Wed, 22 Feb 2023 01:20:37 +0100 Subject: [PATCH 01/26] Added icon caching --- README.md | 10 ++++ examples/app_grid.rs | 1 + src/protostar.rs | 3 +- src/xdg.rs | 113 ++++++++++++++++++++++++++++--------------- 4 files changed, 88 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 7879546..bf01292 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,12 @@ # protostar + Prototype application launcher + +TODO: + +1) ~~Make it work with all icons (should be done)~~ + +2. ~~If the right sized png exists, then use that, otherwise rasterize it (a bit janky but it works)~~ +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 diff --git a/examples/app_grid.rs b/examples/app_grid.rs index 82bb2e0..4de4154 100644 --- a/examples/app_grid.rs +++ b/examples/app_grid.rs @@ -41,6 +41,7 @@ impl AppGrid { let apps = get_desktop_files() .into_iter() .filter_map(|d| parse_desktop_file(d).ok()) + .filter(|d| !d.no_display) .enumerate() .filter(|(i, _)| *i <= APP_LIMIT) .filter_map(|(i, a)| { diff --git a/src/protostar.rs b/src/protostar.rs index 9d3b92c..aae7f3d 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -62,7 +62,7 @@ impl ProtoStar { RawIconType::Gltf(_) => true, }) .or(last_icon) - .map(|i| dbg!(i.process(64)).ok()) + .map(|i| i.process(128).ok()) .ok_or_else(|| eyre!("No compatible icons found"))?; Self::new_raw( parent, @@ -142,6 +142,7 @@ impl RootHandler for ProtoStar { self.icon_shrink = Some(Tweener::quart_in_out(1.0, 0.0, 0.25)); let future = startup_settings.generate_startup_token().unwrap(); let executable = dbg!(self.execute_command.clone()); + //TODO: split the executable string for the args tokio::task::spawn(async move { std::env::set_var("STARDUST_STARTUP_TOKEN", future.await.unwrap()); if unsafe { fork() }.unwrap().is_parent() { diff --git a/src/xdg.rs b/src/xdg.rs index bd18044..ea24c0d 100644 --- a/src/xdg.rs +++ b/src/xdg.rs @@ -4,37 +4,50 @@ use resvg::tiny_skia::{Pixmap, Transform}; use resvg::usvg::{FitTo, Tree}; use std::ffi::OsString; use std::fs::create_dir_all; -use std::io::{BufRead, BufReader, ErrorKind}; +use std::io::{BufRead, BufReader, ErrorKind, self}; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{env, fs}; use walkdir::WalkDir; +use sha2::{Sha224, Digest}; -const ICON_SIZES: &[&str] = &["128x128", "scalable", "256x256", "64x64", "32x32"]; +const ICON_SIZES: &[&str] = &["64x64", "32x32", "scalable", "128x128"]; + +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 + .split(":") + .filter_map(|dir| PathBuf::from_str(dir).ok()); + + 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")) + .join(".local") + .join("share"); + + 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 { - // Get the XDG data directories - let xdg_data_dirs = - std::env::var("XDG_DATA_DIRS").unwrap_or("/usr/local/share:/usr/share".to_string()); - - // Append the applications directory to each data directory - let app_dirs = xdg_data_dirs - .split(":") - .map(|dir| Path::new(dir).join("applications")); - - // Get the user's local applications directory - let local_app_dir = dirs::home_dir() - .unwrap() - .join(".local") - .join("share") - .join("applications"); - let desktop_extension = OsString::from_str("desktop").unwrap(); - // Get the list of directories to search + let app_dirs = get_app_dirs(); + dbg!(&app_dirs); app_dirs - .chain(Some(local_app_dir)) - .filter(|dir| dir.exists() && dir.is_dir()) + .into_iter() .flat_map(|dir| { // Follow symlinks and recursively search directories WalkDir::new(dir) @@ -75,6 +88,7 @@ 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; // Loop through each line of the file for line in reader.lines() { @@ -107,6 +121,10 @@ 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 } } @@ -118,6 +136,7 @@ pub fn parse_desktop_file(path: PathBuf) -> Result { command, categories, icon, + no_display, }) } @@ -149,6 +168,7 @@ 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 { @@ -167,12 +187,11 @@ impl DesktopFile { let icon_theme = env::var_os("XDG_ICON_THEME").unwrap_or("hicolor".into()); // 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); + let xdg_data_dirs = get_data_dirs(); // Concatenate the XDG_DATA_HOME and XDG_DATA_DIRS directories with the default path for icon themes xdg_data_dirs // XDG_DATA_DIRS directories + .into_iter() .flat_map(|dir| { let icons_path = dir.join("icons").join(&icon_theme); ICON_SIZES @@ -214,9 +233,7 @@ impl RawIconType { match self { RawIconType::Png(path) => Ok(Icon::Png(path)), RawIconType::Svg(path) => { - let png_path = path.with_extension("png"); - render_svg_to_png(path, &png_path, size)?; - Ok(Icon::Png(png_path)) + Ok(Icon::Png(get_png_from_svg(&path, size)?)) } RawIconType::Gltf(path) => Ok(Icon::Gltf(path)), } @@ -232,6 +249,7 @@ 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 @@ -240,7 +258,7 @@ fn test_get_icon_path() { // Assert that the get_icon_path() function returns the expected result assert!(icon_paths.contains(&RawIconType::Png(PathBuf::from( - "/usr/share/icons/hicolor/16x16/apps/krita.png" + "/usr/share/icons/hicolor/32x32/apps/krita.png" )))); } @@ -250,22 +268,41 @@ pub enum Icon { Gltf(PathBuf), } -pub fn render_svg_to_png( - cache_dir: impl AsRef, - svg_path: impl AsRef, - size: u32, -) -> Result { +pub fn get_png_from_svg(svg_path: impl AsRef, size: u32,) -> Result { let svg_path = fs::canonicalize(svg_path)?; 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()) + + let cache_dir; + if let Ok(xdg_cache_home) = std::env::var("XDG_CACHE_HOME") { + cache_dir = PathBuf::from_str(&xdg_cache_home).unwrap_or( + dirs::home_dir().unwrap().join(".cache") + ) + } else { + cache_dir = dirs::home_dir().unwrap().join(".cache"); + } + + let image_cache_dir = cache_dir.join("protostar_icon_cache"); + + create_dir_all(&image_cache_dir).expect("Could not create image cache directory"); + + //TODO: come up with a better way to cache images system + let mut hasher = Sha224::new(); + let mut svg_file = fs::File::open(&svg_path)?; + io::copy(&mut svg_file, &mut hasher)?; + let hash_bytes = hasher.finalize(); + + let png_path = image_cache_dir + .join(format!("{}-{:02x}",svg_path.with_extension("").file_name().unwrap().to_str().unwrap(), hash_bytes)) .with_extension("png"); + + if png_path.exists() { + return Ok(png_path) + } + let mut pixmap = Pixmap::new(size, size).unwrap(); render( &tree, @@ -293,7 +330,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 = render_svg_to_png(".", &svg_path, 200).unwrap(); + let png_path = get_png_from_svg(&svg_path, 200).unwrap(); dbg!(&png_path); // Check that the output file exists From 64b95441c08c6e57dd8f5f21a14e963d55784720 Mon Sep 17 00:00:00 2001 From: Nicola Guerrera Date: Wed, 22 Feb 2023 21:07:40 +0100 Subject: [PATCH 02/26] Using linicon crate for icons Removed file --- Cargo.lock | 134 +++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 1 + src/protostar.rs | 19 +++---- src/xdg.rs | 96 +++++++++++++-------------------- 4 files changed, 176 insertions(+), 74 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 61ed0a0..6a1d772 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,6 +23,12 @@ 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" @@ -325,7 +331,7 @@ dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset", + "memoffset 0.7.1", "scopeguard", ] @@ -423,6 +429,15 @@ 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.0" @@ -496,6 +511,15 @@ 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" @@ -575,6 +599,16 @@ 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" @@ -665,6 +699,15 @@ 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 = "heck" version = "0.4.0" @@ -829,6 +872,29 @@ version = "0.2.138" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" +[[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.7" @@ -899,6 +965,15 @@ 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" @@ -908,6 +983,12 @@ 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.5.4" @@ -965,6 +1046,20 @@ 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.1" @@ -974,11 +1069,21 @@ dependencies = [ "bitflags", "cfg-if", "libc", - "memoffset", + "memoffset 0.7.1", "pin-utils", "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" @@ -1065,6 +1170,16 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +[[package]] +name = "ordered-multimap" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c672c7ad9ec066e428c00eb917124a06f08db19e2584de982cc34b1f4c12485" +dependencies = [ + "dlv-list", + "hashbrown", +] + [[package]] name = "os_str_bytes" version = "6.4.1" @@ -1266,9 +1381,10 @@ dependencies = [ "glam 0.22.0", "image", "lazy_static", + "linicon", "manifest-dir-macros", "mint", - "nix", + "nix 0.26.1", "resvg", "rustc-hash", "stardust-xr-fusion", @@ -1497,6 +1613,16 @@ 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" @@ -2090,7 +2216,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "371436099f2980de56dc385b615696d3eabbdac9649a72b85f9d75f68474fa9c" dependencies = [ - "ahash", + "ahash 0.7.6", "byteorder", "lazy_static", "parking_lot 0.11.2", diff --git a/Cargo.toml b/Cargo.toml index 9b2ccf8..030ab86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ ez-pixmap = "0.2.2" glam = { version = "0.22.0", features = ["mint"] } image = "0.24.5" lazy_static = "1.4.0" +linicon = "2.3.0" manifest-dir-macros = "0.1.16" mint = "0.5.9" nix = "0.26.1" diff --git a/src/protostar.rs b/src/protostar.rs index aae7f3d..9de8a71 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -18,8 +18,8 @@ use tween::{QuartInOut, Tweener}; use ustr::ustr; fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { - let model = match icon { - Icon::Png(path) => { + return match &icon.icon_type { + RawIconType::Png(path) => { let model = Model::create( parent, Transform::from_rotation(Quat::from_rotation_y(PI)), @@ -30,15 +30,15 @@ fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { "diffuse", MaterialParameter::Texture(ResourceID::Direct(path.clone())), )?; - model + Ok(model) } - Icon::Gltf(path) => Model::create( + RawIconType::Gltf(path) => Ok(Model::create( parent, Transform::from_scale([0.05; 3]), &ResourceID::new_direct(path)?, - )?, + )?), + _ => panic!("asd"), }; - Ok(model) } pub struct ProtoStar { @@ -52,11 +52,12 @@ pub struct ProtoStar { impl ProtoStar { pub fn create_from_desktop_file(parent: &Spatial, desktop_file: DesktopFile) -> Result { // dbg!(&desktop_file); - let mut raw_icons = desktop_file.get_raw_icons(); + dbg!(&desktop_file); + let mut raw_icons = dbg!(desktop_file.get_raw_icons()); let last_icon = raw_icons.pop(); let icon = raw_icons .into_iter() - .find(|i| match i { + .find(|i| match i.icon_type { RawIconType::Png(_) => false, RawIconType::Svg(_) => false, RawIconType::Gltf(_) => true, @@ -75,7 +76,7 @@ impl ProtoStar { parent, Transform::default(), match icon.as_ref() { - Some(Icon::Png(_)) => [0.05, 0.0665, 0.005], + Some(_) => [0.05, 0.0665, 0.005], _ => [0.05; 3], }, )?; diff --git a/src/xdg.rs b/src/xdg.rs index ea24c0d..04f81f2 100644 --- a/src/xdg.rs +++ b/src/xdg.rs @@ -10,8 +10,7 @@ use std::str::FromStr; use std::{env, fs}; use walkdir::WalkDir; use sha2::{Sha224, Digest}; - -const ICON_SIZES: &[&str] = &["64x64", "32x32", "scalable", "128x128"]; +use linicon; fn get_data_dirs() -> Vec { let xdg_data_dirs_str = std::env::var("XDG_DATA_DIRS") @@ -108,7 +107,7 @@ pub fn parse_desktop_file(path: PathBuf) -> Result { Some((key, value)) => (key, value), None => continue, }; - + // Parse the key-value pair based on the key match key { "Name" => name = Some(value.to_string()), @@ -171,71 +170,54 @@ pub struct DesktopFile { 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() { - return RawIconType::from_path(test_icon_path) - .map(|i| vec![i]) - .unwrap_or_default(); + if let Some(icon) = Icon::from_path(test_icon_path,128) { + return vec![icon] + } } - - let Ok(icon_name) = OsString::from_str(icon_name) else { return Vec::new(); }; - - // 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()); - - // Get the XDG_DATA_HOME and XDG_DATA_DIRS environment variables, and split the XDG_DATA_DIRS variable into a list of directories - let xdg_data_dirs = get_data_dirs(); - - // Concatenate the XDG_DATA_HOME and XDG_DATA_DIRS directories with the default path for icon themes - xdg_data_dirs // XDG_DATA_DIRS directories - .into_iter() - .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() + + let mut icons_iter= linicon::lookup_icon(icon_name); + + 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 } } +#[derive(Debug, PartialEq, Eq)] +pub struct Icon { + pub icon_type: RawIconType, + pub size: u16, +} + #[derive(Debug, PartialEq, Eq)] pub enum RawIconType { Png(PathBuf), Svg(PathBuf), Gltf(PathBuf), } -impl RawIconType { - pub fn from_path(path: PathBuf) -> Option { - match path.extension().and_then(|ext| ext.to_str()) { +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") => Some(RawIconType::Png(path)), Some("svg") => Some(RawIconType::Svg(path)), Some("glb") | Some("gltf") => Some(RawIconType::Gltf(path)), - _ => None, - } + _ => {return None}, + }.unwrap(); + return Some(Icon{icon_type,size}) } - pub fn process(self, size: u32) -> Result { - match self { - RawIconType::Png(path) => Ok(Icon::Png(path)), - RawIconType::Svg(path) => { - Ok(Icon::Png(get_png_from_svg(&path, size)?)) - } - RawIconType::Gltf(path) => Ok(Icon::Gltf(path)), + pub fn process(self, size: u16) -> Result { + match self.icon_type { + RawIconType::Svg(path) => Ok(Icon::from_path(get_png_from_svg(&path, size)?,size).unwrap()), + _ => Ok(self), } } } @@ -257,18 +239,10 @@ fn test_get_icon_path() { dbg!(&icon_paths); // Assert that the get_icon_path() function returns the expected result - assert!(icon_paths.contains(&RawIconType::Png(PathBuf::from( - "/usr/share/icons/hicolor/32x32/apps/krita.png" - )))); + assert!(icon_paths.contains(&Icon::from_path(PathBuf::from("/usr/share/icons/hicolor/32x32/apps/krita.png"),32).unwrap())); } -#[derive(Debug, PartialEq, Eq)] -pub enum Icon { - Png(PathBuf), - Gltf(PathBuf), -} - -pub fn get_png_from_svg(svg_path: impl AsRef, size: u32,) -> Result { +pub fn get_png_from_svg(svg_path: impl AsRef, size: u16,) -> Result { let svg_path = fs::canonicalize(svg_path)?; let tree = Tree::from_data( fs::read(svg_path.as_path())?.as_slice(), @@ -303,10 +277,10 @@ pub fn get_png_from_svg(svg_path: impl AsRef, size: u32,) -> Result Date: Wed, 22 Feb 2023 23:37:48 +0100 Subject: [PATCH 03/26] Added text --- examples/app_grid.rs | 54 ++++++++++++++++++++++++-------------------- src/protostar.rs | 36 ++++++++++++++++++----------- src/xdg.rs | 37 ++++++++++++++---------------- 3 files changed, 70 insertions(+), 57 deletions(-) diff --git a/examples/app_grid.rs b/examples/app_grid.rs index 4de4154..658c17a 100644 --- a/examples/app_grid.rs +++ b/examples/app_grid.rs @@ -1,4 +1,5 @@ use color_eyre::eyre::Result; +use glam::Quat; use manifest_dir_macros::directory_relative_path; use mint::Vector3; use protostar::{ @@ -7,10 +8,10 @@ use protostar::{ }; use stardust_xr_fusion::{ client::{Client, FrameInfo, RootHandler}, - spatial::Spatial, + spatial::Spatial, drawable::{Text, TextStyle, Bounds, TextFit, Alignment}, core::values::Transform, }; -const APP_LIMIT: usize = 50; +const APP_LIMIT: usize = 100; const APP_SIZE: f32 = 0.05; const GRID_PADDING: f32 = 0.01; @@ -35,8 +36,10 @@ async fn main() -> Result<()> { struct AppGrid { apps: Vec, + //style: TextStyle, } impl AppGrid { + fn new(client: &Client) -> Self { let apps = get_desktop_files() .into_iter() @@ -53,6 +56,7 @@ impl AppGrid { 0.0, ], a, + //style, ) }) .collect::>(); @@ -67,44 +71,46 @@ impl RootHandler for AppGrid { } } struct App { - // _text: Text, + _text: Text, _desktop_file: DesktopFile, protostar: ProtoStar, } + impl App { fn new( parent: &Spatial, position: impl Into>, desktop_file: DesktopFile, + //style: TextStyle, ) -> Option { let position = position.into(); - + let style = TextStyle { + character_height: APP_SIZE * 0.1, + bounds: Some(Bounds { + bounds: [APP_SIZE; 2].into(), + fit: TextFit::Wrap, + bounds_align: Alignment::XCenter | Alignment::YCenter, + }), + text_align: Alignment::XCenter | Alignment::YCenter, + ..Default::default() + }; let protostar = ProtoStar::create_from_desktop_file(parent, desktop_file.clone()).ok()?; - // let text = Text::create( - // protostar.content_parent(), - // Transform::from_position_rotation( - // [0.0, 0.0, APP_SIZE / 2.0], - // Quat::from_rotation_y(PI), - // ), - // desktop_file.name.as_deref().unwrap_or("Unknown"), - // TextStyle { - // character_height: APP_SIZE * 0.1, - // bounds: Some(Bounds { - // bounds: [APP_SIZE; 2].into(), - // fit: TextFit::Wrap, - // bounds_align: Alignment::XCenter | Alignment::YCenter, - // }), - // text_align: Alignment::XCenter | Alignment::YCenter, - // ..Default::default() - // }, - // ) - // .unwrap(); + let text = Text::create( + protostar.content_parent(), + Transform::from_position_rotation( + [0.0, 0.0, APP_SIZE / 2.0], + Quat::from_rotation_y(3.14), + ), + desktop_file.name.as_deref().unwrap_or("Unknown"), + style, + ) + .unwrap(); protostar .content_parent() .set_position(None, position) .unwrap(); Some(App { - // _text: text, + _text: text, _desktop_file: desktop_file, protostar, }) diff --git a/src/protostar.rs b/src/protostar.rs index 9de8a71..7197703 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -1,4 +1,4 @@ -use crate::xdg::{DesktopFile, Icon, RawIconType}; +use crate::xdg::{DesktopFile, Icon, IconType}; use color_eyre::eyre::{eyre, Result}; use glam::Quat; use mint::Vector3; @@ -19,7 +19,7 @@ use ustr::ustr; fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { return match &icon.icon_type { - RawIconType::Png(path) => { + IconType::Png(path) => { let model = Model::create( parent, Transform::from_rotation(Quat::from_rotation_y(PI)), @@ -32,7 +32,7 @@ fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { )?; Ok(model) } - RawIconType::Gltf(path) => Ok(Model::create( + IconType::Gltf(path) => Ok(Model::create( parent, Transform::from_scale([0.05; 3]), &ResourceID::new_direct(path)?, @@ -52,19 +52,29 @@ pub struct ProtoStar { impl ProtoStar { pub fn create_from_desktop_file(parent: &Spatial, desktop_file: DesktopFile) -> Result { // dbg!(&desktop_file); - dbg!(&desktop_file); - let mut raw_icons = dbg!(desktop_file.get_raw_icons()); - let last_icon = raw_icons.pop(); - let icon = raw_icons + let raw_icons = desktop_file.get_raw_icons(); + let mut icon = raw_icons + .clone() .into_iter() .find(|i| match i.icon_type { - RawIconType::Png(_) => false, - RawIconType::Svg(_) => false, - RawIconType::Gltf(_) => true, + IconType::Gltf(_) => true, + _ => false, }) - .or(last_icon) - .map(|i| i.process(128).ok()) - .ok_or_else(|| eyre!("No compatible icons found"))?; + .or( + raw_icons + .into_iter() + .max_by_key(|i| i.size) + ); + + match icon{ + Some(i) => { + icon = match i.process(128) { + Ok(i) => Some(i), + _ => None, + }}, + None => {}, + } + Self::new_raw( parent, icon, diff --git a/src/xdg.rs b/src/xdg.rs index 04f81f2..1530faa 100644 --- a/src/xdg.rs +++ b/src/xdg.rs @@ -9,7 +9,6 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{env, fs}; use walkdir::WalkDir; -use sha2::{Sha224, Digest}; use linicon; fn get_data_dirs() -> Vec { @@ -44,7 +43,6 @@ 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(); - dbg!(&app_dirs); app_dirs .into_iter() .flat_map(|dir| { @@ -180,8 +178,13 @@ impl DesktopFile { } } - let mut icons_iter= linicon::lookup_icon(icon_name); + let mut icons_iter= linicon::lookup_icon(icon_name).use_fallback_themes(false).peekable(); + 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 @@ -191,14 +194,14 @@ impl DesktopFile { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct Icon { - pub icon_type: RawIconType, + pub icon_type: IconType, pub size: u16, } -#[derive(Debug, PartialEq, Eq)] -pub enum RawIconType { +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum IconType { Png(PathBuf), Svg(PathBuf), Gltf(PathBuf), @@ -206,9 +209,9 @@ pub enum RawIconType { 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") => Some(RawIconType::Png(path)), - Some("svg") => Some(RawIconType::Svg(path)), - Some("glb") | Some("gltf") => Some(RawIconType::Gltf(path)), + Some("png") => Some(IconType::Png(path)), + Some("svg") => Some(IconType::Svg(path)), + Some("glb") | Some("gltf") => Some(IconType::Gltf(path)), _ => {return None}, }.unwrap(); return Some(Icon{icon_type,size}) @@ -216,7 +219,7 @@ impl Icon { pub fn process(self, size: u16) -> Result { match self.icon_type { - RawIconType::Svg(path) => Ok(Icon::from_path(get_png_from_svg(&path, size)?,size).unwrap()), + IconType::Svg(path) => Ok(Icon::from_path(get_png_from_svg(&path, size)?,size).unwrap()), _ => Ok(self), } } @@ -244,8 +247,9 @@ fn test_get_icon_path() { pub fn get_png_from_svg(svg_path: impl AsRef, size: u16,) -> Result { let svg_path = fs::canonicalize(svg_path)?; + let svg_data = fs::read(svg_path.as_path())?; let tree = Tree::from_data( - fs::read(svg_path.as_path())?.as_slice(), + svg_data.as_slice(), &resvg::usvg::Options::default(), ) .map_err(|_| ErrorKind::InvalidData)?; @@ -262,16 +266,9 @@ pub fn get_png_from_svg(svg_path: impl AsRef, size: u16,) -> Result Date: Thu, 23 Feb 2023 13:39:48 +0100 Subject: [PATCH 04/26] Improved caching of icons --- Cargo.lock | 174 +++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 1 + examples/app_grid.rs | 2 +- src/protostar.rs | 32 +++++--- src/xdg.rs | 65 +++++++++------- 5 files changed, 228 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6a1d772..aed1c08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -84,6 +84,23 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +[[package]] +name = "async-trait" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" +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" @@ -147,6 +164,44 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +[[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.78" @@ -394,6 +449,41 @@ dependencies = [ "syn", ] +[[package]] +name = "darling" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0808e1bd8671fb44a113a14e13497557533369847788fa2ae912b6ebfce9fa8" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "data-url" version = "0.2.0" @@ -578,6 +668,12 @@ dependencies = [ "spin", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "fontconfig-parser" version = "0.5.1" @@ -616,16 +712,65 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] -name = "futures-core" -version = "0.3.25" +name = "futures" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" + +[[package]] +name = "futures-io" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" + +[[package]] +name = "futures-task" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" + +[[package]] +name = "futures-util" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +dependencies = [ + "futures-core", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", +] [[package]] name = "getrandom" @@ -708,6 +853,12 @@ dependencies = [ "ahash 0.4.7", ] +[[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.0" @@ -756,6 +907,12 @@ dependencies = [ "cxx-build", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "image" version = "0.24.5" @@ -1177,7 +1334,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c672c7ad9ec066e428c00eb917124a06f08db19e2584de982cc34b1f4c12485" dependencies = [ "dlv-list", - "hashbrown", + "hashbrown 0.9.1", ] [[package]] @@ -1362,9 +1519,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] @@ -1373,6 +1530,7 @@ dependencies = [ name = "protostar" version = "0.4.0" dependencies = [ + "cached", "clap", "color-eyre", "directories", diff --git a/Cargo.toml b/Cargo.toml index 030ab86..8943d72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "0.4.0" edition = "2021" [dependencies] +cached = "0.42.0" clap = { version = "4.1.3", features = ["derive"] } color-eyre = "0.6.2" directories = "4.0.1" diff --git a/examples/app_grid.rs b/examples/app_grid.rs index 658c17a..5cb8c95 100644 --- a/examples/app_grid.rs +++ b/examples/app_grid.rs @@ -11,7 +11,7 @@ use stardust_xr_fusion::{ spatial::Spatial, drawable::{Text, TextStyle, Bounds, TextFit, Alignment}, core::values::Transform, }; -const APP_LIMIT: usize = 100; +const APP_LIMIT: usize = 300; const APP_SIZE: f32 = 0.05; const GRID_PADDING: f32 = 0.01; diff --git a/src/protostar.rs b/src/protostar.rs index 7197703..4ad7c3d 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -18,26 +18,34 @@ use tween::{QuartInOut, Tweener}; use ustr::ustr; fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { + return match &icon.icon_type { - IconType::Png(path) => { + IconType::Png => { + let t = Transform::from_rotation_scale(Quat::from_rotation_x(PI/2.0),[0.03,0.03,0.03]); + let model = Model::create( parent, - Transform::from_rotation(Quat::from_rotation_y(PI)), - &ResourceID::new_namespaced("protostar", "cartridge"), + t, + &ResourceID::new_namespaced("protostar", "hexagon/hexagon"), )?; model.set_material_parameter( - 0, + 1, + "color", + MaterialParameter::Color([0.0,1.0,1.0,1.0]), + )?; + model.set_material_parameter( + 2, "diffuse", - MaterialParameter::Texture(ResourceID::Direct(path.clone())), + MaterialParameter::Texture(ResourceID::Direct(icon.path.clone())), )?; Ok(model) } - IconType::Gltf(path) => Ok(Model::create( + IconType::Gltf => Ok(Model::create( parent, Transform::from_scale([0.05; 3]), - &ResourceID::new_direct(path)?, + &ResourceID::new_direct(icon.path.clone())?, )?), - _ => panic!("asd"), + _ => panic!("Invalid Icon Type"), }; } @@ -57,7 +65,7 @@ impl ProtoStar { .clone() .into_iter() .find(|i| match i.icon_type { - IconType::Gltf(_) => true, + IconType::Gltf => true, _ => false, }) .or( @@ -68,7 +76,7 @@ impl ProtoStar { match icon{ Some(i) => { - icon = match i.process(128) { + icon = match i.cached_process(128) { Ok(i) => Some(i), _ => None, }}, @@ -105,8 +113,8 @@ impl ProtoStar { .unwrap_or_else(|| { Ok(Model::create( grabbable.content_parent(), - Transform::from_scale([0.05; 3]), - &ResourceID::new_namespaced("protostar", "default_icon"), + Transform::from_rotation_scale(Quat::from_rotation_x(PI/2.0),[0.03,0.03,0.03]), + &ResourceID::new_namespaced("protostar", "hexagon/hexagon"), )?) })?; Ok(ProtoStar { diff --git a/src/xdg.rs b/src/xdg.rs index 1530faa..ce7c2ac 100644 --- a/src/xdg.rs +++ b/src/xdg.rs @@ -3,13 +3,15 @@ use resvg::render; use resvg::tiny_skia::{Pixmap, Transform}; use resvg::usvg::{FitTo, Tree}; use std::ffi::OsString; -use std::fs::create_dir_all; -use std::io::{BufRead, BufReader, ErrorKind, self}; +use std::fs::{create_dir_all}; +use std::os::unix::fs::{symlink}; +use std::io::{BufRead, BufReader, ErrorKind}; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{env, fs}; use walkdir::WalkDir; use linicon; +use cached::proc_macro::cached; fn get_data_dirs() -> Vec { let xdg_data_dirs_str = std::env::var("XDG_DATA_DIRS") @@ -178,6 +180,11 @@ impl DesktopFile { } } + let cache_icon_path = get_image_cache_dir().join(icon_name).canonicalize(); + if cache_icon_path.is_ok() { + return vec![Icon::from_path(cache_icon_path.unwrap(), 128).unwrap()] + } + let mut icons_iter= linicon::lookup_icon(icon_name).use_fallback_themes(false).peekable(); if icons_iter.peek().is_none(){ @@ -197,29 +204,34 @@ impl DesktopFile { #[derive(Debug, PartialEq, Eq, Clone)] pub struct Icon { pub icon_type: IconType, + pub path: PathBuf, pub size: u16, } #[derive(Debug, PartialEq, Eq, Clone)] pub enum IconType { - Png(PathBuf), - Svg(PathBuf), - Gltf(PathBuf), + 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") => Some(IconType::Png(path)), - Some("svg") => Some(IconType::Svg(path)), - Some("glb") | Some("gltf") => Some(IconType::Gltf(path)), + Some("png") => Some(IconType::Png), + Some("svg") => Some(IconType::Svg), + Some("glb") | Some("gltf") => Some(IconType::Gltf), _ => {return None}, }.unwrap(); - return Some(Icon{icon_type,size}) + return Some(Icon{icon_type,path,size}) } - pub fn process(self, size: u16) -> Result { + pub fn cached_process(self, size: u16) -> Result { + 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(path) => Ok(Icon::from_path(get_png_from_svg(&path, size)?,size).unwrap()), + IconType::Svg => Ok(Icon::from_path(get_png_from_svg(self.path, size)?,size).unwrap()), _ => Ok(self), } } @@ -245,6 +257,22 @@ fn test_get_icon_path() { assert!(icon_paths.contains(&Icon::from_path(PathBuf::from("/usr/share/icons/hicolor/32x32/apps/krita.png"),32).unwrap())); } +#[cached] +pub fn get_image_cache_dir() -> PathBuf { + let cache_dir; + if let Ok(xdg_cache_home) = std::env::var("XDG_CACHE_HOME") { + cache_dir = PathBuf::from_str(&xdg_cache_home).unwrap_or( + dirs::home_dir().unwrap().join(".cache") + ) + } else { + cache_dir = dirs::home_dir().unwrap().join(".cache"); + } + let image_cache_dir = cache_dir.join("protostar_icon_cache"); + create_dir_all(&image_cache_dir).expect("Could not create image cache directory"); + return image_cache_dir +} + + pub fn get_png_from_svg(svg_path: impl AsRef, size: u16,) -> Result { let svg_path = fs::canonicalize(svg_path)?; let svg_data = fs::read(svg_path.as_path())?; @@ -254,20 +282,7 @@ pub fn get_png_from_svg(svg_path: impl AsRef, size: u16,) -> Result Date: Thu, 23 Feb 2023 13:49:29 +0100 Subject: [PATCH 05/26] Added texture to hexagons --- src/protostar.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protostar.rs b/src/protostar.rs index 4ad7c3d..5c4d8a4 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -34,7 +34,7 @@ fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { MaterialParameter::Color([0.0,1.0,1.0,1.0]), )?; model.set_material_parameter( - 2, + 0, "diffuse", MaterialParameter::Texture(ResourceID::Direct(icon.path.clone())), )?; From 1b840aa4ea863c3a34cdb4487236c68d8e2ba31c Mon Sep 17 00:00:00 2001 From: nik012003 Date: Fri, 24 Feb 2023 09:01:43 +0100 Subject: [PATCH 06/26] Initial work on Hexagon --- examples/hexagon_launcher.rs | 138 +++++++++++++++++++++++++++++++++++ src/protostar.rs | 6 +- 2 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 examples/hexagon_launcher.rs diff --git a/examples/hexagon_launcher.rs b/examples/hexagon_launcher.rs new file mode 100644 index 0000000..a1db21b --- /dev/null +++ b/examples/hexagon_launcher.rs @@ -0,0 +1,138 @@ +use std::path::PathBuf; + +use color_eyre::eyre::Result; +use glam::Quat; +use manifest_dir_macros::directory_relative_path; +use mint::Vector3; +use protostar::{ + protostar::ProtoStar, + xdg::{get_desktop_files, parse_desktop_file, DesktopFile}, +}; +use stardust_xr_molecules::fusion::{ + client::{Client, FrameInfo, RootHandler}, + spatial::Spatial, drawable::{Text, TextStyle, Bounds, TextFit, Alignment}, core::values::Transform, +}; +use tween::TweenTime; + +const APP_LIMIT: usize = 18; +const APP_SIZE: f32 = 0.055; + +#[tokio::main(flavor = "current_thread")] +async fn main() -> Result<()> { + color_eyre::install().unwrap(); + tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .pretty() + .init(); + let (client, event_loop) = Client::connect_with_async_loop().await?; + client.set_base_prefixes(&[directory_relative_path!("res")]); + + let _root = client.wrap_root(AppHexGrid::new(&client))?; + + tokio::select! { + _ = tokio::signal::ctrl_c() => (), + e = event_loop => e??, + }; + Ok(()) +} + +struct AppHexGrid { + apps: Vec, +} +impl AppHexGrid { + + fn new(client: &Client) -> Self { + let mut desktop_files: Vec = get_desktop_files() + .into_iter() + .filter_map(|d| parse_desktop_file(d).ok()) + .filter(|d| !d.no_display) + .collect(); + + + let mut circles = 1; + let mut current_num = 6; + let mut target = 6; + desktop_files.sort_by_key(|d| d.clone().name.unwrap()); + dbg!(&desktop_files); + let apps :Vec= desktop_files + .into_iter() + .enumerate() + .filter(|(i,_)| *i < APP_LIMIT) + .filter_map(|(i,d)|{ + let angle = ((3.14*2.0)/current_num.to_f32())*(i%current_num).to_f32(); + + let x = (target-i)%circles; // this gives 0,3,2,1 but I need 0,1,2,3 + dbg!(x); + let m = circles.to_f32()*APP_SIZE - (x.to_f32()*0.5*(circles-1).to_f32()*(APP_SIZE/2.0)); + + let position = [angle.sin()*m,angle.cos()*m,0.0]; + + if (i+1) == target { + circles += 1; + current_num += 6; + target = target + current_num; + } + + return App::new(client.get_root(),position,d); + }) + .collect(); + AppHexGrid { apps } + } +} +impl RootHandler for AppHexGrid { + fn frame(&mut self, info: FrameInfo) { + for app in &mut self.apps { + app.frame(info); + } + } +} +struct App { + _text: Text, + _desktop_file: DesktopFile, + protostar: ProtoStar, +} + +impl App { + fn new( + parent: &Spatial, + position: impl Into>, + desktop_file: DesktopFile, + ) -> Option { + let position = position.into(); + let style = TextStyle { + character_height: APP_SIZE * 0.1, + bounds: Some(Bounds { + bounds: [APP_SIZE; 2].into(), + fit: TextFit::Wrap, + bounds_align: Alignment::XCenter | Alignment::YCenter, + }), + text_align: Alignment::XCenter | Alignment::YCenter, + ..Default::default() + }; + let protostar = ProtoStar::create_from_desktop_file(parent, desktop_file.clone()).ok()?; + let text = Text::create( + protostar.content_parent(), + Transform::from_position_rotation( + [0.0, 0.0, APP_SIZE / 2.0], + Quat::from_rotation_y(3.14), + ), + desktop_file.name.as_deref().unwrap_or("Unknown"), + style, + ) + .unwrap(); + protostar + .content_parent() + .set_position(None, position) + .unwrap(); + Some(App { + _text: text, + _desktop_file: desktop_file, + protostar, + }) + } +} +impl RootHandler for App { + fn frame(&mut self, info: FrameInfo) { + self.protostar.frame(info); + } +} diff --git a/src/protostar.rs b/src/protostar.rs index 5c4d8a4..beed0a5 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -21,7 +21,7 @@ fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { return match &icon.icon_type { IconType::Png => { - let t = Transform::from_rotation_scale(Quat::from_rotation_x(PI/2.0),[0.03,0.03,0.03]); + let t = Transform::from_rotation_scale(Quat::from_rotation_x(PI/2.0)*Quat::from_rotation_y(PI),[0.03,0.03,0.03]); let model = Model::create( parent, @@ -113,7 +113,7 @@ impl ProtoStar { .unwrap_or_else(|| { Ok(Model::create( grabbable.content_parent(), - Transform::from_rotation_scale(Quat::from_rotation_x(PI/2.0),[0.03,0.03,0.03]), + Transform::from_rotation_scale(Quat::from_xyzw(0.0,0.707,0.707,0.0),[0.03,0.03,0.03]), &ResourceID::new_namespaced("protostar", "hexagon/hexagon"), )?) })?; @@ -158,7 +158,7 @@ impl RootHandler for ProtoStar { startup_settings .set_root(self.grabbable.content_parent()) .unwrap(); - self.icon_shrink = Some(Tweener::quart_in_out(1.0, 0.0, 0.25)); + self.icon_shrink = Some(Tweener::quart_in_out(0.03, 0.0, 0.25)); //TODO make the scale a parameter let future = startup_settings.generate_startup_token().unwrap(); let executable = dbg!(self.execute_command.clone()); //TODO: split the executable string for the args From 5ce578f9679718ce957c2d5adc462d51dd8d82f4 Mon Sep 17 00:00:00 2001 From: Nicola Guerrera Date: Fri, 24 Feb 2023 16:28:35 +0100 Subject: [PATCH 07/26] Finalized Hex design --- examples/hexagon_launcher.rs | 77 +++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/examples/hexagon_launcher.rs b/examples/hexagon_launcher.rs index a1db21b..88e3c0b 100644 --- a/examples/hexagon_launcher.rs +++ b/examples/hexagon_launcher.rs @@ -1,5 +1,3 @@ -use std::path::PathBuf; - use color_eyre::eyre::Result; use glam::Quat; use manifest_dir_macros::directory_relative_path; @@ -14,8 +12,37 @@ use stardust_xr_molecules::fusion::{ }; use tween::TweenTime; -const APP_LIMIT: usize = 18; -const APP_SIZE: f32 = 0.055; +const APP_SIZE: f32 = 0.065; +#[derive(Clone)] +struct Cube { + q: isize, + r: isize, + s: isize, +} + +const CUBE_CENTER: Cube = Cube{q:0,r:0,s:0}; +const CUBE_DIRECTION_VECTORS: [Cube; 6] = [ + Cube{q:1, r:0, s:-1}, Cube{q:1, r:-1, s:0}, Cube{q:0, r:-1, s:1}, + Cube{q:-1, r:0, s:1}, Cube{q:-1, r:1, s:0}, Cube{q:0, r:1, s:-1}, +]; + +impl Cube { + fn get_coords(&self) -> [f32; 3]{ + let x: f32 = 3.0/2.0 * APP_SIZE.to_f32()/2.0 * (-self.q-self.s).to_f32(); + let y = 3.0_f32.sqrt() * APP_SIZE.to_f32()/2.0 * ( (-self.q-self.s).to_f32()/2.0 + self.s.to_f32()); + [x,y,0.0] + } +} + +fn cube_add(hex: Cube, vec:Cube) -> Cube{ + Cube{q:(hex.q + vec.q), r:(hex.r + vec.r), s:(hex.s + vec.s)} +} +fn cube_neighbor(cube: Cube, direction:usize) -> Cube{ + cube_add(cube, CUBE_DIRECTION_VECTORS[direction].clone()) +} +fn cube_scale(hex: Cube, factor:isize) -> Cube { + Cube{q:(hex.q * factor), r:(hex.r * factor), s:(hex.s * factor)} +} #[tokio::main(flavor = "current_thread")] async fn main() -> Result<()> { @@ -49,33 +76,21 @@ impl AppHexGrid { .collect(); - let mut circles = 1; - let mut current_num = 6; - let mut target = 6; desktop_files.sort_by_key(|d| d.clone().name.unwrap()); dbg!(&desktop_files); - let apps :Vec= desktop_files - .into_iter() - .enumerate() - .filter(|(i,_)| *i < APP_LIMIT) - .filter_map(|(i,d)|{ - let angle = ((3.14*2.0)/current_num.to_f32())*(i%current_num).to_f32(); - - let x = (target-i)%circles; // this gives 0,3,2,1 but I need 0,1,2,3 - dbg!(x); - let m = circles.to_f32()*APP_SIZE - (x.to_f32()*0.5*(circles-1).to_f32()*(APP_SIZE/2.0)); - - let position = [angle.sin()*m,angle.cos()*m,0.0]; - - if (i+1) == target { - circles += 1; - current_num += 6; - target = target + current_num; - } - - return App::new(client.get_root(),position,d); - }) - .collect(); + let mut apps = Vec::new(); + let n_spirals = (1.0/6.0 * (-3.0 +(desktop_files.len().to_f32()*12.0).sqrt())).floor() as isize; + dbg!(n_spirals); + let mut iter = desktop_files.into_iter(); + for radius in 1..n_spirals{ + let mut hex = cube_add(CUBE_CENTER, cube_scale(CUBE_DIRECTION_VECTORS[4].clone(), radius)); + for i in 0..6{ + for j in 0..radius{ + apps.push(App::new(client.get_root(),hex.get_coords(),iter.next().unwrap()).unwrap()); + hex = cube_neighbor(hex, i) + } + } + } AppHexGrid { apps } } } @@ -99,7 +114,7 @@ impl App { desktop_file: DesktopFile, ) -> Option { let position = position.into(); - let style = TextStyle { + let style= TextStyle { character_height: APP_SIZE * 0.1, bounds: Some(Bounds { bounds: [APP_SIZE; 2].into(), @@ -113,7 +128,7 @@ impl App { let text = Text::create( protostar.content_parent(), Transform::from_position_rotation( - [0.0, 0.0, APP_SIZE / 2.0], + [0.0, 0.0, 0.004], Quat::from_rotation_y(3.14), ), desktop_file.name.as_deref().unwrap_or("Unknown"), From 32627089fb9a6025c691243f6362c0b62126fbd1 Mon Sep 17 00:00:00 2001 From: nik012003 Date: Fri, 24 Feb 2023 22:13:45 +0100 Subject: [PATCH 08/26] Cleaned up hexagon code --- examples/hexagon_launcher.rs | 56 ++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/examples/hexagon_launcher.rs b/examples/hexagon_launcher.rs index 88e3c0b..a3ee830 100644 --- a/examples/hexagon_launcher.rs +++ b/examples/hexagon_launcher.rs @@ -14,34 +14,40 @@ use tween::TweenTime; const APP_SIZE: f32 = 0.065; #[derive(Clone)] -struct Cube { +struct Hex { q: isize, r: isize, s: isize, } -const CUBE_CENTER: Cube = Cube{q:0,r:0,s:0}; -const CUBE_DIRECTION_VECTORS: [Cube; 6] = [ - Cube{q:1, r:0, s:-1}, Cube{q:1, r:-1, s:0}, Cube{q:0, r:-1, s:1}, - Cube{q:-1, r:0, s:1}, Cube{q:-1, r:1, s:0}, Cube{q:0, r:1, s:-1}, +const HEX_CENTER: Hex = Hex{q:0,r:0,s:0}; +const HEX_DIRECTION_VECTORS: [Hex; 6] = [ + Hex{q:1, r:0, s:-1}, Hex{q:1, r:-1, s:0}, Hex{q:0, r:-1, s:1}, + Hex{q:-1, r:0, s:1}, Hex{q:-1, r:1, s:0}, Hex{q:0, r:1, s:-1}, ]; -impl Cube { +impl Hex { + fn new(q:isize, r:isize, s:isize) -> Self{ + Hex{q:q, r:r, s:s} + } + fn get_coords(&self) -> [f32; 3]{ let x: f32 = 3.0/2.0 * APP_SIZE.to_f32()/2.0 * (-self.q-self.s).to_f32(); let y = 3.0_f32.sqrt() * APP_SIZE.to_f32()/2.0 * ( (-self.q-self.s).to_f32()/2.0 + self.s.to_f32()); [x,y,0.0] } -} -fn cube_add(hex: Cube, vec:Cube) -> Cube{ - Cube{q:(hex.q + vec.q), r:(hex.r + vec.r), s:(hex.s + vec.s)} -} -fn cube_neighbor(cube: Cube, direction:usize) -> Cube{ - cube_add(cube, CUBE_DIRECTION_VECTORS[direction].clone()) -} -fn cube_scale(hex: Cube, factor:isize) -> Cube { - Cube{q:(hex.q * factor), r:(hex.r * factor), s:(hex.s * factor)} + fn add(self, vec:&Hex) -> Self{ + Hex::new(self.q + vec.q, self.r + vec.r, self.s + vec.s) + } + + fn neighbor(self, direction:usize) -> Self{ + self.add(&HEX_DIRECTION_VECTORS[direction]) + } + + fn scale(self, factor:isize) -> Self { + Hex::new(self.q * factor, self.r * factor, self.s * factor) + } } #[tokio::main(flavor = "current_thread")] @@ -75,21 +81,21 @@ impl AppHexGrid { .filter(|d| !d.no_display) .collect(); - desktop_files.sort_by_key(|d| d.clone().name.unwrap()); - dbg!(&desktop_files); + let mut apps = Vec::new(); - let n_spirals = (1.0/6.0 * (-3.0 +(desktop_files.len().to_f32()*12.0).sqrt())).floor() as isize; - dbg!(n_spirals); - let mut iter = desktop_files.into_iter(); - for radius in 1..n_spirals{ - let mut hex = cube_add(CUBE_CENTER, cube_scale(CUBE_DIRECTION_VECTORS[4].clone(), radius)); + let mut radius = 1; + while !desktop_files.is_empty() { + let mut hex = HEX_CENTER.add(&HEX_DIRECTION_VECTORS[4].clone().scale(radius)); for i in 0..6{ - for j in 0..radius{ - apps.push(App::new(client.get_root(),hex.get_coords(),iter.next().unwrap()).unwrap()); - hex = cube_neighbor(hex, i) + if desktop_files.is_empty() {break}; + for _ in 0..radius{ + if desktop_files.is_empty() {break}; + apps.push(App::new(client.get_root(),hex.get_coords(),desktop_files.pop().unwrap()).unwrap()); + hex = hex.neighbor(i); } } + radius += 1; } AppHexGrid { apps } } From 692fded2716b8f2640c170ba80bcb31243c4e02e Mon Sep 17 00:00:00 2001 From: nik012003 Date: Sat, 25 Feb 2023 00:36:07 +0100 Subject: [PATCH 09/26] Added detaching child process --- Cargo.lock | 10 ++++++ Cargo.toml | 1 + examples/hexagon_launcher.rs | 4 +-- src/protostar.rs | 60 +++++++++++++++++++++++------------- 4 files changed, 51 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aed1c08..f55c523 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -695,6 +695,15 @@ dependencies = [ "ttf-parser", ] +[[package]] +name = "fork" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9788ce090af4bf8d6e8f43d3f7d12305c787456387bd2d88856fcda3aa1f0dca" +dependencies = [ + "libc", +] + [[package]] name = "freedesktop_entry_parser" version = "1.3.0" @@ -1536,6 +1545,7 @@ dependencies = [ "directories", "dirs", "ez-pixmap", + "fork", "glam 0.22.0", "image", "lazy_static", diff --git a/Cargo.toml b/Cargo.toml index 8943d72..cb35d2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ color-eyre = "0.6.2" directories = "4.0.1" dirs = "4.0.0" ez-pixmap = "0.2.2" +fork = "0.1.20" glam = { version = "0.22.0", features = ["mint"] } image = "0.24.5" lazy_static = "1.4.0" diff --git a/examples/hexagon_launcher.rs b/examples/hexagon_launcher.rs index a3ee830..85cc8f3 100644 --- a/examples/hexagon_launcher.rs +++ b/examples/hexagon_launcher.rs @@ -32,8 +32,8 @@ impl Hex { } fn get_coords(&self) -> [f32; 3]{ - let x: f32 = 3.0/2.0 * APP_SIZE.to_f32()/2.0 * (-self.q-self.s).to_f32(); - let y = 3.0_f32.sqrt() * APP_SIZE.to_f32()/2.0 * ( (-self.q-self.s).to_f32()/2.0 + self.s.to_f32()); + let x = 3.0/2.0 * APP_SIZE/2.0 * (-self.q-self.s).to_f32(); + let y = 3.0_f32.sqrt() * APP_SIZE/2.0 * ( (-self.q-self.s).to_f32()/2.0 + self.s.to_f32()); [x,y,0.0] } diff --git a/src/protostar.rs b/src/protostar.rs index beed0a5..7693b57 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -2,20 +2,26 @@ use crate::xdg::{DesktopFile, Icon, IconType}; use color_eyre::eyre::{eyre, Result}; use glam::Quat; use mint::Vector3; -use nix::unistd::{execv, fork}; -use stardust_xr_fusion::{ - client::{Client, FrameInfo, RootHandler}, - core::values::Transform, - drawable::{MaterialParameter, Model, ResourceID}, - fields::BoxField, - node::NodeType, - spatial::Spatial, - startup_settings::StartupSettings, +use fork::{daemon, Fork, setsid}; +use std::process::{Command,Stdio}; +use std::os::unix::process::CommandExt; +use stardust_xr_molecules::{ + fusion::{ + client::{Client, FrameInfo, RootHandler}, + core::values::Transform, + drawable::{MaterialParameter, Model, ResourceID}, + fields::BoxField, + node::NodeType, + spatial::Spatial, + startup_settings::StartupSettings, + }, + GrabData, Grabbable, }; use stardust_xr_molecules::{GrabData, Grabbable}; use std::{f32::consts::PI, ffi::CStr, sync::Arc}; use tween::{QuartInOut, Tweener}; use ustr::ustr; +use nix::unistd::fork; fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { @@ -55,6 +61,7 @@ pub struct ProtoStar { field: BoxField, icon: Model, icon_shrink: Option>, + icon_grow: Option>, execute_command: String, } impl ProtoStar { @@ -123,6 +130,7 @@ impl ProtoStar { field, icon, icon_shrink: None, + icon_grow: None, execute_command, }) } @@ -140,9 +148,15 @@ impl RootHandler for ProtoStar { self.icon .set_scale(None, Vector3::from([scale; 3])) .unwrap(); - } else { - self.client.stop_loop(); + } + if let Some(icon_grow) = &mut self.icon_shrink { + if !icon_grow.is_finished(){ + let scale = icon_grow.move_by(info.delta); + self.icon + .set_scale(None, Vector3::from([scale; 3])) + .unwrap(); } + } } else if self.grabbable.grab_action().actor_stopped() { let startup_settings = StartupSettings::create(&self.field.client().unwrap()).unwrap(); self.icon @@ -164,19 +178,21 @@ impl RootHandler for ProtoStar { //TODO: split the executable string for the args tokio::task::spawn(async move { std::env::set_var("STARDUST_STARTUP_TOKEN", future.await.unwrap()); - if unsafe { fork() }.unwrap().is_parent() { - println!("Launching \"{}\"...", &executable); - execv::<&CStr>( - ustr("/bin/sh").as_cstr(), - &[ - ustr("/bin/sh").as_cstr(), - ustr("-c").as_cstr(), - ustr(&executable).as_cstr(), - ], - ) - .unwrap(); + unsafe { + Command::new(executable) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .pre_exec(|| { + setsid(); + Ok(()) + }) + .spawn() + .expect("Failed to start child process") } }); + self.icon_grow = Some(Tweener::quart_in_out(0.00, 0.03, 0.25)); //TODO make the scale a parameter + dbg!("reached here"); } } } From 8595199dcf63859601912c0a425980fb192b656e Mon Sep 17 00:00:00 2001 From: nik012003 Date: Sat, 25 Feb 2023 00:39:15 +0100 Subject: [PATCH 10/26] Cleaned up dependecies --- Cargo.lock | 10 ---- Cargo.toml | 1 - examples/app_grid.rs | 23 ++++---- examples/hexagon_launcher.rs | 80 ++++++++++++++----------- src/protostar.rs | 71 +++++++++++----------- src/xdg.rs | 110 ++++++++++++++++++++--------------- 6 files changed, 156 insertions(+), 139 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f55c523..aed1c08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -695,15 +695,6 @@ dependencies = [ "ttf-parser", ] -[[package]] -name = "fork" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9788ce090af4bf8d6e8f43d3f7d12305c787456387bd2d88856fcda3aa1f0dca" -dependencies = [ - "libc", -] - [[package]] name = "freedesktop_entry_parser" version = "1.3.0" @@ -1545,7 +1536,6 @@ dependencies = [ "directories", "dirs", "ez-pixmap", - "fork", "glam 0.22.0", "image", "lazy_static", diff --git a/Cargo.toml b/Cargo.toml index cb35d2a..8943d72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,6 @@ color-eyre = "0.6.2" directories = "4.0.1" dirs = "4.0.0" ez-pixmap = "0.2.2" -fork = "0.1.20" glam = { version = "0.22.0", features = ["mint"] } image = "0.24.5" lazy_static = "1.4.0" diff --git a/examples/app_grid.rs b/examples/app_grid.rs index 5cb8c95..33da3d2 100644 --- a/examples/app_grid.rs +++ b/examples/app_grid.rs @@ -8,7 +8,9 @@ use protostar::{ }; use stardust_xr_fusion::{ client::{Client, FrameInfo, RootHandler}, - spatial::Spatial, drawable::{Text, TextStyle, Bounds, TextFit, Alignment}, core::values::Transform, + core::values::Transform, + drawable::{Alignment, Bounds, Text, TextFit, TextStyle}, + spatial::Spatial, }; const APP_LIMIT: usize = 300; @@ -39,7 +41,6 @@ struct AppGrid { //style: TextStyle, } impl AppGrid { - fn new(client: &Client) -> Self { let apps = get_desktop_files() .into_iter() @@ -96,15 +97,15 @@ impl App { }; let protostar = ProtoStar::create_from_desktop_file(parent, desktop_file.clone()).ok()?; let text = Text::create( - protostar.content_parent(), - Transform::from_position_rotation( - [0.0, 0.0, APP_SIZE / 2.0], - Quat::from_rotation_y(3.14), - ), - desktop_file.name.as_deref().unwrap_or("Unknown"), - style, - ) - .unwrap(); + protostar.content_parent(), + Transform::from_position_rotation( + [0.0, 0.0, APP_SIZE / 2.0], + Quat::from_rotation_y(3.14), + ), + desktop_file.name.as_deref().unwrap_or("Unknown"), + style, + ) + .unwrap(); protostar .content_parent() .set_position(None, position) diff --git a/examples/hexagon_launcher.rs b/examples/hexagon_launcher.rs index 85cc8f3..8a83eef 100644 --- a/examples/hexagon_launcher.rs +++ b/examples/hexagon_launcher.rs @@ -8,7 +8,9 @@ use protostar::{ }; use stardust_xr_molecules::fusion::{ client::{Client, FrameInfo, RootHandler}, - spatial::Spatial, drawable::{Text, TextStyle, Bounds, TextFit, Alignment}, core::values::Transform, + core::values::Transform, + drawable::{Alignment, Bounds, Text, TextFit, TextStyle}, + spatial::Spatial, }; use tween::TweenTime; @@ -20,32 +22,37 @@ struct Hex { s: isize, } -const HEX_CENTER: Hex = Hex{q:0,r:0,s:0}; +const HEX_CENTER: Hex = Hex { q: 0, r: 0, s: 0 }; const HEX_DIRECTION_VECTORS: [Hex; 6] = [ - Hex{q:1, r:0, s:-1}, Hex{q:1, r:-1, s:0}, Hex{q:0, r:-1, s:1}, - Hex{q:-1, r:0, s:1}, Hex{q:-1, r:1, s:0}, Hex{q:0, r:1, s:-1}, + Hex { q: 1, r: 0, s: -1 }, + Hex { q: 1, r: -1, s: 0 }, + Hex { q: 0, r: -1, s: 1 }, + Hex { q: -1, r: 0, s: 1 }, + Hex { q: -1, r: 1, s: 0 }, + Hex { q: 0, r: 1, s: -1 }, ]; impl Hex { - fn new(q:isize, r:isize, s:isize) -> Self{ - Hex{q:q, r:r, s:s} + fn new(q: isize, r: isize, s: isize) -> Self { + Hex { q: q, r: r, s: s } } - fn get_coords(&self) -> [f32; 3]{ - let x = 3.0/2.0 * APP_SIZE/2.0 * (-self.q-self.s).to_f32(); - let y = 3.0_f32.sqrt() * APP_SIZE/2.0 * ( (-self.q-self.s).to_f32()/2.0 + self.s.to_f32()); - [x,y,0.0] + fn get_coords(&self) -> [f32; 3] { + let x = 3.0 / 2.0 * APP_SIZE / 2.0 * (-self.q - self.s).to_f32(); + let y = + 3.0_f32.sqrt() * APP_SIZE / 2.0 * ((-self.q - self.s).to_f32() / 2.0 + self.s.to_f32()); + [x, y, 0.0] } - fn add(self, vec:&Hex) -> Self{ + fn add(self, vec: &Hex) -> Self { Hex::new(self.q + vec.q, self.r + vec.r, self.s + vec.s) } - fn neighbor(self, direction:usize) -> Self{ + fn neighbor(self, direction: usize) -> Self { self.add(&HEX_DIRECTION_VECTORS[direction]) } - fn scale(self, factor:isize) -> Self { + fn scale(self, factor: isize) -> Self { Hex::new(self.q * factor, self.r * factor, self.s * factor) } } @@ -73,7 +80,6 @@ struct AppHexGrid { apps: Vec, } impl AppHexGrid { - fn new(client: &Client) -> Self { let mut desktop_files: Vec = get_desktop_files() .into_iter() @@ -84,19 +90,30 @@ impl AppHexGrid { desktop_files.sort_by_key(|d| d.clone().name.unwrap()); let mut apps = Vec::new(); - let mut radius = 1; + let mut radius = 1; while !desktop_files.is_empty() { let mut hex = HEX_CENTER.add(&HEX_DIRECTION_VECTORS[4].clone().scale(radius)); - for i in 0..6{ - if desktop_files.is_empty() {break}; - for _ in 0..radius{ - if desktop_files.is_empty() {break}; - apps.push(App::new(client.get_root(),hex.get_coords(),desktop_files.pop().unwrap()).unwrap()); - hex = hex.neighbor(i); - } + for i in 0..6 { + if desktop_files.is_empty() { + break; + }; + for _ in 0..radius { + if desktop_files.is_empty() { + break; + }; + apps.push( + App::new( + client.get_root(), + hex.get_coords(), + desktop_files.pop().unwrap(), + ) + .unwrap(), + ); + hex = hex.neighbor(i); + } } radius += 1; - } + } AppHexGrid { apps } } } @@ -120,7 +137,7 @@ impl App { desktop_file: DesktopFile, ) -> Option { let position = position.into(); - let style= TextStyle { + let style = TextStyle { character_height: APP_SIZE * 0.1, bounds: Some(Bounds { bounds: [APP_SIZE; 2].into(), @@ -132,15 +149,12 @@ impl App { }; let protostar = ProtoStar::create_from_desktop_file(parent, desktop_file.clone()).ok()?; let text = Text::create( - protostar.content_parent(), - Transform::from_position_rotation( - [0.0, 0.0, 0.004], - Quat::from_rotation_y(3.14), - ), - desktop_file.name.as_deref().unwrap_or("Unknown"), - style, - ) - .unwrap(); + protostar.content_parent(), + Transform::from_position_rotation([0.0, 0.0, 0.004], Quat::from_rotation_y(3.14)), + desktop_file.name.as_deref().unwrap_or("Unknown"), + style, + ) + .unwrap(); protostar .content_parent() .set_position(None, position) diff --git a/src/protostar.rs b/src/protostar.rs index 7693b57..157f089 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -2,9 +2,7 @@ use crate::xdg::{DesktopFile, Icon, IconType}; use color_eyre::eyre::{eyre, Result}; use glam::Quat; use mint::Vector3; -use fork::{daemon, Fork, setsid}; -use std::process::{Command,Stdio}; -use std::os::unix::process::CommandExt; +use nix::unistd::setsid; use stardust_xr_molecules::{ fusion::{ client::{Client, FrameInfo, RootHandler}, @@ -17,17 +15,18 @@ use stardust_xr_molecules::{ }, GrabData, Grabbable, }; -use stardust_xr_molecules::{GrabData, Grabbable}; -use std::{f32::consts::PI, ffi::CStr, sync::Arc}; +use std::os::unix::process::CommandExt; +use std::process::{Command, Stdio}; +use std::{f32::consts::PI, sync::Arc}; use tween::{QuartInOut, Tweener}; -use ustr::ustr; -use nix::unistd::fork; fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { - return match &icon.icon_type { IconType::Png => { - let t = Transform::from_rotation_scale(Quat::from_rotation_x(PI/2.0)*Quat::from_rotation_y(PI),[0.03,0.03,0.03]); + let t = Transform::from_rotation_scale( + Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI), + [0.03, 0.03, 0.03], + ); let model = Model::create( parent, @@ -37,7 +36,7 @@ fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { model.set_material_parameter( 1, "color", - MaterialParameter::Color([0.0,1.0,1.0,1.0]), + MaterialParameter::Color([0.0, 1.0, 1.0, 1.0]), )?; model.set_material_parameter( 0, @@ -75,19 +74,16 @@ impl ProtoStar { IconType::Gltf => true, _ => false, }) - .or( - raw_icons - .into_iter() - .max_by_key(|i| i.size) - ); + .or(raw_icons.into_iter().max_by_key(|i| i.size)); - match icon{ + match icon { Some(i) => { icon = match i.cached_process(128) { Ok(i) => Some(i), _ => None, - }}, - None => {}, + } + } + None => {} } Self::new_raw( @@ -120,7 +116,10 @@ impl ProtoStar { .unwrap_or_else(|| { Ok(Model::create( grabbable.content_parent(), - Transform::from_rotation_scale(Quat::from_xyzw(0.0,0.707,0.707,0.0),[0.03,0.03,0.03]), + Transform::from_rotation_scale( + Quat::from_xyzw(0.0, 0.707, 0.707, 0.0), + [0.03, 0.03, 0.03], + ), &ResourceID::new_namespaced("protostar", "hexagon/hexagon"), )?) })?; @@ -148,15 +147,15 @@ impl RootHandler for ProtoStar { self.icon .set_scale(None, Vector3::from([scale; 3])) .unwrap(); - } - if let Some(icon_grow) = &mut self.icon_shrink { - if !icon_grow.is_finished(){ - let scale = icon_grow.move_by(info.delta); - self.icon - .set_scale(None, Vector3::from([scale; 3])) - .unwrap(); } - } + if let Some(icon_grow) = &mut self.icon_shrink { + if !icon_grow.is_finished() { + let scale = icon_grow.move_by(info.delta); + self.icon + .set_scale(None, Vector3::from([scale; 3])) + .unwrap(); + } + } } else if self.grabbable.grab_action().actor_stopped() { let startup_settings = StartupSettings::create(&self.field.client().unwrap()).unwrap(); self.icon @@ -180,15 +179,15 @@ impl RootHandler for ProtoStar { std::env::set_var("STARDUST_STARTUP_TOKEN", future.await.unwrap()); unsafe { Command::new(executable) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .pre_exec(|| { - setsid(); - Ok(()) - }) - .spawn() - .expect("Failed to start child process") + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .pre_exec(|| { + setsid(); + Ok(()) + }) + .spawn() + .expect("Failed to start child process") } }); self.icon_grow = Some(Tweener::quart_in_out(0.00, 0.03, 0.25)); //TODO make the scale a parameter diff --git a/src/xdg.rs b/src/xdg.rs index ce7c2ac..f9bd6af 100644 --- a/src/xdg.rs +++ b/src/xdg.rs @@ -1,29 +1,29 @@ +use cached::proc_macro::cached; use color_eyre::eyre::Result; +use linicon; use resvg::render; use resvg::tiny_skia::{Pixmap, Transform}; use resvg::usvg::{FitTo, Tree}; use std::ffi::OsString; -use std::fs::{create_dir_all}; -use std::os::unix::fs::{symlink}; +use std::fs::create_dir_all; use std::io::{BufRead, BufReader, ErrorKind}; +use std::os::unix::fs::symlink; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{env, fs}; use walkdir::WalkDir; -use linicon; -use cached::proc_macro::cached; fn get_data_dirs() -> Vec { - let xdg_data_dirs_str = std::env::var("XDG_DATA_DIRS") - .unwrap_or_default(); + let xdg_data_dirs_str = std::env::var("XDG_DATA_DIRS").unwrap_or_default(); - let xdg_data_dirs = xdg_data_dirs_str + let xdg_data_dirs = xdg_data_dirs_str .split(":") .filter_map(|dir| PathBuf::from_str(dir).ok()); 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")) + .unwrap_or(PathBuf::from_str("/usr/share/").expect( + "No XDG_DATA_DIR set, no HOME directory found and no /usr/share direcotry found", + )) .join(".local") .join("share"); @@ -33,7 +33,7 @@ fn get_data_dirs() -> Vec { .collect() } -fn get_app_dirs() -> Vec{ +fn get_app_dirs() -> Vec { get_data_dirs() .into_iter() .map(|dir| dir.join("applications")) @@ -107,7 +107,7 @@ pub fn parse_desktop_file(path: PathBuf) -> Result { Some((key, value)) => (key, value), None => continue, }; - + // Parse the key-value pair based on the key match key { "Name" => name = Some(value.to_string()), @@ -120,10 +120,12 @@ pub fn parse_desktop_file(path: PathBuf) -> Result { .collect() } "Icon" => icon = Some(value.to_string()), - "NoDisplay" => no_display = match value{ - "true" => true, - _ => false - }, + "NoDisplay" => { + no_display = match value { + "true" => true, + _ => false, + } + } _ => (), // Ignore unknown keys } } @@ -175,27 +177,29 @@ impl DesktopFile { 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 vec![icon] + if let Some(icon) = Icon::from_path(test_icon_path, 128) { + return vec![icon]; } } - + let cache_icon_path = get_image_cache_dir().join(icon_name).canonicalize(); if cache_icon_path.is_ok() { - return vec![Icon::from_path(cache_icon_path.unwrap(), 128).unwrap()] + return vec![Icon::from_path(cache_icon_path.unwrap(), 128).unwrap()]; } - let mut icons_iter= linicon::lookup_icon(icon_name).use_fallback_themes(false).peekable(); - - if icons_iter.peek().is_none(){ + let mut icons_iter = linicon::lookup_icon(icon_name) + .use_fallback_themes(false) + .peekable(); + + if icons_iter.peek().is_none() { //dbg!("No icons found in current theme"); - icons_iter= linicon::lookup_icon(icon_name).peekable(); + icons_iter = linicon::lookup_icon(icon_name).peekable(); } - let sized_png : Vec = icons_iter + 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()) + .map(|i| Icon::from_path(i.path, i.max_size - 2).unwrap()) .collect(); sized_png } @@ -215,23 +219,29 @@ pub enum IconType { Gltf, } impl Icon { - pub fn from_path(path: PathBuf, size: u16) -> Option{ + pub fn from_path(path: PathBuf, size: u16) -> Option { let icon_type = match path.extension().and_then(|ext| ext.to_str()) { Some("png") => Some(IconType::Png), Some("svg") => Some(IconType::Svg), Some("glb") | Some("gltf") => Some(IconType::Gltf), - _ => {return None}, - }.unwrap(); - return Some(Icon{icon_type,path,size}) + _ => return None, + } + .unwrap(); + return Some(Icon { + icon_type, + path, + size, + }); } pub fn cached_process(self, size: u16) -> Result { - let new_path = get_image_cache_dir().join(self.path.with_extension("").file_name().unwrap()); - if !new_path.exists(){ + 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()), + IconType::Svg => Ok(Icon::from_path(get_png_from_svg(self.path, size)?, size).unwrap()), _ => Ok(self), } } @@ -254,39 +264,43 @@ 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( + &Icon::from_path( + PathBuf::from("/usr/share/icons/hicolor/32x32/apps/krita.png"), + 32 + ) + .unwrap() + )); } #[cached] pub fn get_image_cache_dir() -> PathBuf { let cache_dir; if let Ok(xdg_cache_home) = std::env::var("XDG_CACHE_HOME") { - cache_dir = PathBuf::from_str(&xdg_cache_home).unwrap_or( - dirs::home_dir().unwrap().join(".cache") - ) + cache_dir = + PathBuf::from_str(&xdg_cache_home).unwrap_or(dirs::home_dir().unwrap().join(".cache")) } else { cache_dir = dirs::home_dir().unwrap().join(".cache"); } let image_cache_dir = cache_dir.join("protostar_icon_cache"); create_dir_all(&image_cache_dir).expect("Could not create image cache directory"); - return image_cache_dir + return image_cache_dir; } - -pub fn get_png_from_svg(svg_path: impl AsRef, size: u16,) -> Result { +pub fn get_png_from_svg(svg_path: impl AsRef, size: u16) -> 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())); + 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) + return Ok(png_path); } let mut pixmap = Pixmap::new(size.into(), size.into()).unwrap(); From ba4e86ad794cf56c875cc8ff4eba981c8f3e15d0 Mon Sep 17 00:00:00 2001 From: nik012003 Date: Sat, 25 Feb 2023 00:58:04 +0100 Subject: [PATCH 11/26] Updated fusion and molecules --- examples/hexagon_launcher.rs | 2 +- src/protostar.rs | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/hexagon_launcher.rs b/examples/hexagon_launcher.rs index 8a83eef..9b72695 100644 --- a/examples/hexagon_launcher.rs +++ b/examples/hexagon_launcher.rs @@ -6,7 +6,7 @@ use protostar::{ protostar::ProtoStar, xdg::{get_desktop_files, parse_desktop_file, DesktopFile}, }; -use stardust_xr_molecules::fusion::{ +use stardust_xr_fusion::{ client::{Client, FrameInfo, RootHandler}, core::values::Transform, drawable::{Alignment, Bounds, Text, TextFit, TextStyle}, diff --git a/src/protostar.rs b/src/protostar.rs index 157f089..373d3a3 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -3,8 +3,7 @@ use color_eyre::eyre::{eyre, Result}; use glam::Quat; use mint::Vector3; use nix::unistd::setsid; -use stardust_xr_molecules::{ - fusion::{ +use stardust_xr_fusion::{ client::{Client, FrameInfo, RootHandler}, core::values::Transform, drawable::{MaterialParameter, Model, ResourceID}, @@ -12,9 +11,8 @@ use stardust_xr_molecules::{ node::NodeType, spatial::Spatial, startup_settings::StartupSettings, - }, - GrabData, Grabbable, }; +use stardust_xr_molecules::{GrabData, Grabbable}; use std::os::unix::process::CommandExt; use std::process::{Command, Stdio}; use std::{f32::consts::PI, sync::Arc}; @@ -106,7 +104,7 @@ impl ProtoStar { Transform::default(), &field, GrabData { - max_distance: 0.025, + max_distance: 0.01, ..Default::default() }, )?; From fc1d0979a88ec3a8ba93c6c81089f29dd610a02b Mon Sep 17 00:00:00 2001 From: nik012003 Date: Sun, 26 Feb 2023 18:22:17 +0100 Subject: [PATCH 12/26] Make hexagons reappear --- examples/hexagon_launcher.rs | 2 +- src/main.rs | 3 +- src/protostar.rs | 57 ++++++++++++++++++++---------------- 3 files changed, 35 insertions(+), 27 deletions(-) diff --git a/examples/hexagon_launcher.rs b/examples/hexagon_launcher.rs index 9b72695..329d253 100644 --- a/examples/hexagon_launcher.rs +++ b/examples/hexagon_launcher.rs @@ -147,7 +147,7 @@ impl App { text_align: Alignment::XCenter | Alignment::YCenter, ..Default::default() }; - let protostar = ProtoStar::create_from_desktop_file(parent, desktop_file.clone()).ok()?; + let protostar = ProtoStar::create_from_desktop_file(parent, position, desktop_file.clone()).ok()?; let text = Text::create( protostar.content_parent(), Transform::from_position_rotation([0.0, 0.0, 0.004], Quat::from_rotation_y(3.14)), diff --git a/src/main.rs b/src/main.rs index 008a92f..6e00034 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,10 +38,11 @@ async fn main() -> Result<()> { let protostar = if let Some(desktop_file) = args.desktop_file { ProtoStar::create_from_desktop_file( client.get_root(), + [0.0,0.0,0.0], parse_desktop_file(desktop_file).map_err(|e| Report::msg(e))?, )? } else if let Some(command) = args.command { - ProtoStar::new_raw(client.get_root(), None, command)? + ProtoStar::new_raw(client.get_root(), [0.0,0.0,0.0],None, command)? } else { bail!("No command or desktop file, nothing to launch."); }; diff --git a/src/protostar.rs b/src/protostar.rs index 373d3a3..fa23c0b 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -54,6 +54,7 @@ fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { pub struct ProtoStar { client: Arc, + position: Vector3, grabbable: Grabbable, field: BoxField, icon: Model, @@ -62,7 +63,7 @@ pub struct ProtoStar { execute_command: String, } impl ProtoStar { - pub fn create_from_desktop_file(parent: &Spatial, desktop_file: DesktopFile) -> Result { + pub fn create_from_desktop_file(parent: &Spatial, position: impl Into>, desktop_file: DesktopFile) -> Result { // dbg!(&desktop_file); let raw_icons = desktop_file.get_raw_icons(); let mut icon = raw_icons @@ -86,11 +87,13 @@ impl ProtoStar { Self::new_raw( parent, + position, icon, desktop_file.command.ok_or_else(|| eyre!("No command"))?, ) } - pub fn new_raw(parent: &Spatial, icon: Option, execute_command: String) -> Result { + pub fn new_raw(parent: &Spatial, position: impl Into>, icon: Option, execute_command: String) -> Result { + let position = position.into(); let field = BoxField::create( parent, Transform::default(), @@ -123,6 +126,7 @@ impl ProtoStar { })?; Ok(ProtoStar { client: parent.client()?, + position, grabbable, field, icon, @@ -138,38 +142,43 @@ impl ProtoStar { impl RootHandler for ProtoStar { fn frame(&mut self, info: FrameInfo) { self.grabbable.update(&info); - if let Some(icon_shrink) = &mut self.icon_shrink { if !icon_shrink.is_finished() { let scale = icon_shrink.move_by(info.delta); self.icon .set_scale(None, Vector3::from([scale; 3])) .unwrap(); + } else { + self.icon_grow = Some(Tweener::quart_in_out(0.0001, 0.03, 0.25)); //TODO make the scale a parameter + self.icon_shrink = None; } - if let Some(icon_grow) = &mut self.icon_shrink { - if !icon_grow.is_finished() { - let scale = icon_grow.move_by(info.delta); - self.icon - .set_scale(None, Vector3::from([scale; 3])) - .unwrap(); - } + } else if let Some(icon_grow) = &mut self.icon_grow { + if !icon_grow.is_finished() { + let scale = icon_grow.move_by(info.delta); + self.icon + .set_scale(None, Vector3::from([scale; 3])) + .unwrap(); + } else { + self.icon.set_position(None, [0.0,0.0,0.0]).unwrap(); + self.icon.set_rotation(None, Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI) ).unwrap(); + self.icon_grow = None; } - } else if self.grabbable.grab_action().actor_stopped() { + }else if self.grabbable.grab_action().actor_stopped() { let startup_settings = StartupSettings::create(&self.field.client().unwrap()).unwrap(); - self.icon - .set_spatial_parent_in_place(self.client.get_root()) - .unwrap(); - self.grabbable - .content_parent() - .set_rotation( - Some(&self.field.client().unwrap().get_root()), - Quat::IDENTITY, - ) - .unwrap(); + //self.icon + // .set_spatial_parent_in_place(self.client.get_root()) + // .unwrap(); + //self.grabbable + // .content_parent() + // .set_rotation( + // Some(&self.field.client().unwrap().get_root()), + // Quat::IDENTITY, + // ) + // .unwrap(); startup_settings .set_root(self.grabbable.content_parent()) .unwrap(); - self.icon_shrink = Some(Tweener::quart_in_out(0.03, 0.0, 0.25)); //TODO make the scale a parameter + self.icon_shrink = Some(Tweener::quart_in_out(0.03, 0.0001, 0.25)); //TODO make the scale a parameter let future = startup_settings.generate_startup_token().unwrap(); let executable = dbg!(self.execute_command.clone()); //TODO: split the executable string for the args @@ -181,15 +190,13 @@ impl RootHandler for ProtoStar { .stdout(Stdio::null()) .stderr(Stdio::null()) .pre_exec(|| { - setsid(); + _ = setsid(); Ok(()) }) .spawn() .expect("Failed to start child process") } }); - self.icon_grow = Some(Tweener::quart_in_out(0.00, 0.03, 0.25)); //TODO make the scale a parameter - dbg!("reached here"); } } } From 08a9d8ed66afc47bee410070ff67cc9781163bae Mon Sep 17 00:00:00 2001 From: Nicola Guerrera Date: Sun, 26 Feb 2023 23:40:43 +0100 Subject: [PATCH 13/26] Repositioning --- Cargo.lock | 16 +++++--------- Cargo.toml | 4 ++-- examples/app_grid.rs | 6 +----- examples/hexagon_launcher.rs | 4 ---- src/protostar.rs | 42 ++++++++++++++++++++++-------------- 5 files changed, 34 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aed1c08..5857705 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,12 +66,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "anyhow" -version = "1.0.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" - [[package]] name = "arrayref" version = "0.3.6" @@ -1976,11 +1970,11 @@ dependencies = [ [[package]] name = "stardust-xr-fusion" -version = "0.38.0" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f6c2945a8246880297b1f059e48a8eeb6ba39c58f67e83c05725b86f869149" +checksum = "45a0a1ee96c4be1d4866e1920feb4fb732b810a6cc2e9f7eeff66f07f9c2f496" dependencies = [ - "anyhow", + "color-eyre", "color-rs", "enum_dispatch", "flagset", @@ -1999,9 +1993,9 @@ dependencies = [ [[package]] name = "stardust-xr-molecules" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ae6569520bc27bd6fc015e4e0a687bb730631dc039bb5c5c14c2ad40457cfac" +checksum = "4c16d448b75aff896519f94910a525bf5588a53cf54d5256a7eef05f604ffce4" dependencies = [ "color-rs", "glam 0.23.0", diff --git a/Cargo.toml b/Cargo.toml index 8943d72..8a15648 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,8 +19,8 @@ mint = "0.5.9" nix = "0.26.1" resvg = "0.29.0" rustc-hash = "1.1.0" -stardust-xr-fusion = "0.38.0" -stardust-xr-molecules = "0.21.0" +stardust-xr-fusion = "0.38.1" +stardust-xr-molecules = "0.21.1" tokio = { version = "1.24.1", features = ["full"] } tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } tween = "2.0.0" diff --git a/examples/app_grid.rs b/examples/app_grid.rs index 33da3d2..80d394f 100644 --- a/examples/app_grid.rs +++ b/examples/app_grid.rs @@ -95,7 +95,7 @@ impl App { text_align: Alignment::XCenter | Alignment::YCenter, ..Default::default() }; - let protostar = ProtoStar::create_from_desktop_file(parent, desktop_file.clone()).ok()?; + let protostar = ProtoStar::create_from_desktop_file(parent, position, desktop_file.clone()).ok()?; let text = Text::create( protostar.content_parent(), Transform::from_position_rotation( @@ -106,10 +106,6 @@ impl App { style, ) .unwrap(); - protostar - .content_parent() - .set_position(None, position) - .unwrap(); Some(App { _text: text, _desktop_file: desktop_file, diff --git a/examples/hexagon_launcher.rs b/examples/hexagon_launcher.rs index 329d253..0b8331c 100644 --- a/examples/hexagon_launcher.rs +++ b/examples/hexagon_launcher.rs @@ -155,10 +155,6 @@ impl App { style, ) .unwrap(); - protostar - .content_parent() - .set_position(None, position) - .unwrap(); Some(App { _text: text, _desktop_file: desktop_file, diff --git a/src/protostar.rs b/src/protostar.rs index fa23c0b..e73d2be 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -104,7 +104,7 @@ impl ProtoStar { )?; let grabbable = Grabbable::new( parent, - Transform::default(), + Transform::from_position(position), &field, GrabData { max_distance: 0.01, @@ -151,6 +151,9 @@ impl RootHandler for ProtoStar { } else { self.icon_grow = Some(Tweener::quart_in_out(0.0001, 0.03, 0.25)); //TODO make the scale a parameter self.icon_shrink = None; + self.grabbable.content_parent().set_position(Some(self.client.get_root()) , self.position).unwrap(); + self.grabbable.content_parent().set_rotation(Some(self.client.get_root()), Quat::default()).unwrap(); + self.icon.set_rotation(None, Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI) ).unwrap(); } } else if let Some(icon_grow) = &mut self.icon_grow { if !icon_grow.is_finished() { @@ -159,8 +162,6 @@ impl RootHandler for ProtoStar { .set_scale(None, Vector3::from([scale; 3])) .unwrap(); } else { - self.icon.set_position(None, [0.0,0.0,0.0]).unwrap(); - self.icon.set_rotation(None, Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI) ).unwrap(); self.icon_grow = None; } }else if self.grabbable.grab_action().actor_stopped() { @@ -179,22 +180,31 @@ impl RootHandler for ProtoStar { .set_root(self.grabbable.content_parent()) .unwrap(); self.icon_shrink = Some(Tweener::quart_in_out(0.03, 0.0001, 0.25)); //TODO make the scale a parameter - let future = startup_settings.generate_startup_token().unwrap(); + let distance_future = self.grabbable.content_parent().get_position_rotation_scale(self.client.get_root()).unwrap(); + let executable = dbg!(self.execute_command.clone()); + //TODO: split the executable string for the args tokio::task::spawn(async move { - std::env::set_var("STARDUST_STARTUP_TOKEN", future.await.unwrap()); - unsafe { - Command::new(executable) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .pre_exec(|| { - _ = setsid(); - Ok(()) - }) - .spawn() - .expect("Failed to start child process") + let distance_vector = distance_future.await.ok().unwrap().0; + let distance = distance_vector.x.abs() + distance_vector.y.abs() + distance_vector.z.abs(); + if dbg!(distance) > 1.0 { + let future = startup_settings.generate_startup_token().unwrap(); + + std::env::set_var("STARDUST_STARTUP_TOKEN", future.await.unwrap()); + + unsafe { + Command::new(executable) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .pre_exec(|| { + _ = setsid(); + Ok(()) + }) + .spawn() + .expect("Failed to start child process"); + } } }); } From e4f9762b9545a6c1cf458ec6f78795ffb151a69f Mon Sep 17 00:00:00 2001 From: Nicola Guerrera Date: Mon, 27 Feb 2023 16:11:28 +0100 Subject: [PATCH 14/26] Added button and animatations --- examples/app_grid.rs | 3 +- examples/hexagon_launcher.rs | 62 ++++++++++++++++-- src/main.rs | 4 +- src/protostar.rs | 120 ++++++++++++++++++++++------------- 4 files changed, 137 insertions(+), 52 deletions(-) diff --git a/examples/app_grid.rs b/examples/app_grid.rs index 80d394f..f892d58 100644 --- a/examples/app_grid.rs +++ b/examples/app_grid.rs @@ -95,7 +95,8 @@ impl App { text_align: Alignment::XCenter | Alignment::YCenter, ..Default::default() }; - let protostar = ProtoStar::create_from_desktop_file(parent, position, desktop_file.clone()).ok()?; + let protostar = + ProtoStar::create_from_desktop_file(parent, position, desktop_file.clone()).ok()?; let text = Text::create( protostar.content_parent(), Transform::from_position_rotation( diff --git a/examples/hexagon_launcher.rs b/examples/hexagon_launcher.rs index 0b8331c..7d00192 100644 --- a/examples/hexagon_launcher.rs +++ b/examples/hexagon_launcher.rs @@ -9,9 +9,12 @@ use protostar::{ use stardust_xr_fusion::{ client::{Client, FrameInfo, RootHandler}, core::values::Transform, - drawable::{Alignment, Bounds, Text, TextFit, TextStyle}, + drawable::{Alignment, Bounds, MaterialParameter, Model, ResourceID, Text, TextFit, TextStyle}, + node::NodeError, spatial::Spatial, }; +use stardust_xr_molecules::touch_plane::TouchPlane; +use std::f32::consts::PI; use tween::TweenTime; const APP_SIZE: f32 = 0.065; @@ -34,7 +37,7 @@ const HEX_DIRECTION_VECTORS: [Hex; 6] = [ impl Hex { fn new(q: isize, r: isize, s: isize) -> Self { - Hex { q: q, r: r, s: s } + Hex { q, r, s } } fn get_coords(&self) -> [f32; 3] { @@ -78,9 +81,11 @@ async fn main() -> Result<()> { struct AppHexGrid { apps: Vec, + button: Button, } impl AppHexGrid { fn new(client: &Client) -> Self { + let button = Button::new(client).unwrap(); let mut desktop_files: Vec = get_desktop_files() .into_iter() .filter_map(|d| parse_desktop_file(d).ok()) @@ -114,11 +119,29 @@ impl AppHexGrid { } radius += 1; } - AppHexGrid { apps } + AppHexGrid { apps, button } } } impl RootHandler for AppHexGrid { fn frame(&mut self, info: FrameInfo) { + self.button.touch_plane.update(); + if self.button.touch_plane.touch_started() { + dbg!("Touch started"); + let color = [0.0, 1.0, 0.0, 1.0]; + self.button + .model + .set_material_parameter(1, "color", MaterialParameter::Color(color)) + .unwrap(); + for app in &mut self.apps { + app.protostar.toggle(); + } + } else if self.button.touch_plane.touch_stopped() { + let color = [0.0, 0.0, 1.0, 1.0]; + self.button + .model + .set_material_parameter(1, "color", MaterialParameter::Color(color)) + .unwrap(); + } for app in &mut self.apps { app.frame(info); } @@ -147,7 +170,8 @@ impl App { text_align: Alignment::XCenter | Alignment::YCenter, ..Default::default() }; - let protostar = ProtoStar::create_from_desktop_file(parent, position, desktop_file.clone()).ok()?; + let protostar = + ProtoStar::create_from_desktop_file(parent, position, desktop_file.clone()).ok()?; let text = Text::create( protostar.content_parent(), Transform::from_position_rotation([0.0, 0.0, 0.004], Quat::from_rotation_y(3.14)), @@ -167,3 +191,33 @@ impl RootHandler for App { self.protostar.frame(info); } } + +struct Button { + touch_plane: TouchPlane, + model: Model, +} +impl Button { + fn new(client: &Client) -> Result { + let touch_plane = TouchPlane::new( + client.get_root(), + Transform::default(), + [APP_SIZE / 2.0, APP_SIZE / 2.0], + APP_SIZE / 2.0, + )?; + let model = Model::create( + client.get_root(), + Transform::from_rotation_scale( + Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI), + [0.03, 0.03, 0.03], + ), + &ResourceID::new_namespaced("protostar", "hexagon/hexagon"), + )?; + model + .set_material_parameter(1, "color", MaterialParameter::Color([0.0, 0.0, 1.0, 1.0])) + .unwrap(); + Ok(Button { touch_plane, model }) + } +} +impl RootHandler for Button { + fn frame(&mut self, _info: FrameInfo) {} +} diff --git a/src/main.rs b/src/main.rs index 6e00034..f59b05a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,11 +38,11 @@ async fn main() -> Result<()> { let protostar = if let Some(desktop_file) = args.desktop_file { ProtoStar::create_from_desktop_file( client.get_root(), - [0.0,0.0,0.0], + [0.0, 0.0, 0.0], parse_desktop_file(desktop_file).map_err(|e| Report::msg(e))?, )? } else if let Some(command) = args.command { - ProtoStar::new_raw(client.get_root(), [0.0,0.0,0.0],None, command)? + ProtoStar::new_raw(client.get_root(), [0.0, 0.0, 0.0], None, command)? } else { bail!("No command or desktop file, nothing to launch."); }; diff --git a/src/protostar.rs b/src/protostar.rs index e73d2be..8baedec 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -4,13 +4,13 @@ use glam::Quat; use mint::Vector3; use nix::unistd::setsid; use stardust_xr_fusion::{ - client::{Client, FrameInfo, RootHandler}, - core::values::Transform, - drawable::{MaterialParameter, Model, ResourceID}, - fields::BoxField, - node::NodeType, - spatial::Spatial, - startup_settings::StartupSettings, + client::{Client, FrameInfo, RootHandler}, + core::values::Transform, + drawable::{MaterialParameter, Model, ResourceID}, + fields::BoxField, + node::NodeType, + spatial::Spatial, + startup_settings::StartupSettings, }; use stardust_xr_molecules::{GrabData, Grabbable}; use std::os::unix::process::CommandExt; @@ -58,12 +58,17 @@ pub struct ProtoStar { grabbable: Grabbable, field: BoxField, icon: Model, - icon_shrink: Option>, - icon_grow: Option>, + grabbable_shrink: Option>, + grabbable_grow: Option>, execute_command: String, + currently_shown: bool, } impl ProtoStar { - pub fn create_from_desktop_file(parent: &Spatial, position: impl Into>, desktop_file: DesktopFile) -> Result { + pub fn create_from_desktop_file( + parent: &Spatial, + position: impl Into>, + desktop_file: DesktopFile, + ) -> Result { // dbg!(&desktop_file); let raw_icons = desktop_file.get_raw_icons(); let mut icon = raw_icons @@ -92,7 +97,12 @@ impl ProtoStar { desktop_file.command.ok_or_else(|| eyre!("No command"))?, ) } - pub fn new_raw(parent: &Spatial, position: impl Into>, icon: Option, execute_command: String) -> Result { + pub fn new_raw( + parent: &Spatial, + position: impl Into>, + icon: Option, + execute_command: String, + ) -> Result { let position = position.into(); let field = BoxField::create( parent, @@ -130,69 +140,89 @@ impl ProtoStar { grabbable, field, icon, - icon_shrink: None, - icon_grow: None, + grabbable_shrink: None, + grabbable_grow: None, execute_command, + currently_shown: true, }) } pub fn content_parent(&self) -> &Spatial { self.grabbable.content_parent() } + pub fn toggle(&mut self) { + if self.currently_shown { + self.grabbable_shrink = Some(Tweener::quart_in_out(1.0, 0.0001, 0.25)); //TODO make the scale a parameter + } else { + self.grabbable_grow = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25)); + } + self.currently_shown = !self.currently_shown; + } } impl RootHandler for ProtoStar { fn frame(&mut self, info: FrameInfo) { self.grabbable.update(&info); - if let Some(icon_shrink) = &mut self.icon_shrink { - if !icon_shrink.is_finished() { - let scale = icon_shrink.move_by(info.delta); - self.icon + + if let Some(grabbable_shrink) = &mut self.grabbable_shrink { + if !grabbable_shrink.is_finished() { + let scale = grabbable_shrink.move_by(info.delta); + self.grabbable + .content_parent() .set_scale(None, Vector3::from([scale; 3])) .unwrap(); } else { - self.icon_grow = Some(Tweener::quart_in_out(0.0001, 0.03, 0.25)); //TODO make the scale a parameter - self.icon_shrink = None; - self.grabbable.content_parent().set_position(Some(self.client.get_root()) , self.position).unwrap(); - self.grabbable.content_parent().set_rotation(Some(self.client.get_root()), Quat::default()).unwrap(); - self.icon.set_rotation(None, Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI) ).unwrap(); - } - } else if let Some(icon_grow) = &mut self.icon_grow { - if !icon_grow.is_finished() { - let scale = icon_grow.move_by(info.delta); + if self.currently_shown { + self.grabbable_grow = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25)); //TODO make the scale a parameter + } + self.grabbable_shrink = None; + self.grabbable + .content_parent() + .set_position(Some(self.client.get_root()), self.position) + .unwrap(); + self.grabbable + .content_parent() + .set_rotation(Some(self.client.get_root()), Quat::default()) + .unwrap(); self.icon + .set_rotation( + None, + Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI), + ) + .unwrap(); + } + } else if let Some(grabbable_grow) = &mut self.grabbable_grow { + if !grabbable_grow.is_finished() { + let scale = grabbable_grow.move_by(info.delta); + self.grabbable + .content_parent() .set_scale(None, Vector3::from([scale; 3])) .unwrap(); } else { - self.icon_grow = None; + self.grabbable_grow = None; } - }else if self.grabbable.grab_action().actor_stopped() { + } else if self.grabbable.grab_action().actor_stopped() { let startup_settings = StartupSettings::create(&self.field.client().unwrap()).unwrap(); - //self.icon - // .set_spatial_parent_in_place(self.client.get_root()) - // .unwrap(); - //self.grabbable - // .content_parent() - // .set_rotation( - // Some(&self.field.client().unwrap().get_root()), - // Quat::IDENTITY, - // ) - // .unwrap(); startup_settings .set_root(self.grabbable.content_parent()) .unwrap(); - self.icon_shrink = Some(Tweener::quart_in_out(0.03, 0.0001, 0.25)); //TODO make the scale a parameter - let distance_future = self.grabbable.content_parent().get_position_rotation_scale(self.client.get_root()).unwrap(); - + self.grabbable_shrink = Some(Tweener::quart_in_out(0.03, 0.0001, 0.25)); //TODO make the scale a parameter + let distance_future = self + .grabbable + .content_parent() + .get_position_rotation_scale(self.client.get_root()) + .unwrap(); + let executable = dbg!(self.execute_command.clone()); //TODO: split the executable string for the args tokio::task::spawn(async move { let distance_vector = distance_future.await.ok().unwrap().0; - let distance = distance_vector.x.abs() + distance_vector.y.abs() + distance_vector.z.abs(); - if dbg!(distance) > 1.0 { + let distance = + distance_vector.x.abs() + distance_vector.y.abs() + distance_vector.z.abs(); + if dbg!(distance) > 1.0 { let future = startup_settings.generate_startup_token().unwrap(); - + std::env::set_var("STARDUST_STARTUP_TOKEN", future.await.unwrap()); - + unsafe { Command::new(executable) .stdin(Stdio::null()) From a6994d5963bd0321f6b96c60729a4fede729495c Mon Sep 17 00:00:00 2001 From: Nicola Guerrera Date: Mon, 27 Feb 2023 17:51:53 +0100 Subject: [PATCH 15/26] Cooler animations --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- src/protostar.rs | 30 ++++++++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5857705..d5a9a42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1993,9 +1993,9 @@ dependencies = [ [[package]] name = "stardust-xr-molecules" -version = "0.21.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c16d448b75aff896519f94910a525bf5588a53cf54d5256a7eef05f604ffce4" +checksum = "5fe2979f1816d21c3d1a88351f92e1a011992b166072a5fbc73221d6d4557a8e" dependencies = [ "color-rs", "glam 0.23.0", diff --git a/Cargo.toml b/Cargo.toml index 8a15648..4abe98c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ nix = "0.26.1" resvg = "0.29.0" rustc-hash = "1.1.0" stardust-xr-fusion = "0.38.1" -stardust-xr-molecules = "0.21.1" +stardust-xr-molecules = "0.22.0" tokio = { version = "1.24.1", features = ["full"] } tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } tween = "2.0.0" diff --git a/src/protostar.rs b/src/protostar.rs index 8baedec..d4a5f3c 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -62,6 +62,7 @@ pub struct ProtoStar { grabbable_grow: Option>, execute_command: String, currently_shown: bool, + grabbabe_move: Option>, } impl ProtoStar { pub fn create_from_desktop_file( @@ -144,6 +145,7 @@ impl ProtoStar { grabbable_grow: None, execute_command, currently_shown: true, + grabbabe_move: None, }) } pub fn content_parent(&self) -> &Spatial { @@ -151,17 +153,39 @@ impl ProtoStar { } pub fn toggle(&mut self) { if self.currently_shown { - self.grabbable_shrink = Some(Tweener::quart_in_out(1.0, 0.0001, 0.25)); //TODO make the scale a parameter + self.grabbabe_move = Some(Tweener::quart_in_out(1.0, 0.0001, 0.25)); //TODO make the scale a parameter } else { - self.grabbable_grow = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25)); + self.grabbable + .content_parent() + .set_scale(None, Vector3::from([1.0; 3])) + .unwrap(); + self.grabbabe_move = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25)); } self.currently_shown = !self.currently_shown; } } impl RootHandler for ProtoStar { fn frame(&mut self, info: FrameInfo) { + self.grabbable.update(&info); + if let Some(grabbabe_move) = &mut self.grabbabe_move { + if !grabbabe_move.is_finished() { + let scale = grabbabe_move.move_by(info.delta); + self.grabbable + .content_parent() + .set_position(None, [self.position.x*scale, self.position.y*scale, self.position.z*scale]) + .unwrap(); + } else { + if grabbabe_move.final_value() == 0.0001 { + self.grabbable + .content_parent() + .set_scale(None, Vector3::from([0.001; 3])) + .unwrap(); + } + self.grabbabe_move = None; + } + } if let Some(grabbable_shrink) = &mut self.grabbable_shrink { if !grabbable_shrink.is_finished() { let scale = grabbable_shrink.move_by(info.delta); @@ -172,6 +196,8 @@ impl RootHandler for ProtoStar { } else { if self.currently_shown { self.grabbable_grow = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25)); //TODO make the scale a parameter + self.grabbable.cancel_angular_velocity(); + self.grabbable.cancel_linear_velocity(); } self.grabbable_shrink = None; self.grabbable From e88b16c5356bb95f828bf9d9df8c30c0617466a5 Mon Sep 17 00:00:00 2001 From: Nicola Guerrera Date: Tue, 28 Feb 2023 12:17:15 +0100 Subject: [PATCH 16/26] Desktop file parsing improvements --- Cargo.lock | 12 ++++++++++++ Cargo.toml | 3 ++- src/protostar.rs | 14 +++++++++----- src/xdg.rs | 14 +++++++++++++- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d5a9a42..7d11602 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,6 +40,15 @@ 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" @@ -1537,6 +1546,7 @@ dependencies = [ "manifest-dir-macros", "mint", "nix 0.26.1", + "regex", "resvg", "rustc-hash", "stardust-xr-fusion", @@ -1679,6 +1689,8 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", ] diff --git a/Cargo.toml b/Cargo.toml index 4abe98c..a1cd0f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ linicon = "2.3.0" manifest-dir-macros = "0.1.16" mint = "0.5.9" nix = "0.26.1" +regex = "1.7.1" resvg = "0.29.0" rustc-hash = "1.1.0" stardust-xr-fusion = "0.38.1" @@ -28,4 +29,4 @@ ustr = "0.9.0" walkdir = "2.3.2" [dev-dependencies] -tempdir = "0.3.7" \ No newline at end of file +tempdir = "0.3.7" diff --git a/src/protostar.rs b/src/protostar.rs index d4a5f3c..7d6fed8 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -3,6 +3,7 @@ use color_eyre::eyre::{eyre, Result}; use glam::Quat; use mint::Vector3; use nix::unistd::setsid; +use regex::Regex; use stardust_xr_fusion::{ client::{Client, FrameInfo, RootHandler}, core::values::Transform, @@ -174,7 +175,7 @@ impl RootHandler for ProtoStar { let scale = grabbabe_move.move_by(info.delta); self.grabbable .content_parent() - .set_position(None, [self.position.x*scale, self.position.y*scale, self.position.z*scale]) + .set_position(Some(self.client.get_root()), [self.position.x*scale, self.position.y*scale, self.position.z*scale]) .unwrap(); } else { if grabbabe_move.final_value() == 0.0001 { @@ -237,9 +238,9 @@ impl RootHandler for ProtoStar { .get_position_rotation_scale(self.client.get_root()) .unwrap(); - let executable = dbg!(self.execute_command.clone()); + let executable = self.execute_command.clone(); - //TODO: split the executable string for the args + //TODO: split the executable string for the args tokio::task::spawn(async move { let distance_vector = distance_future.await.ok().unwrap().0; let distance = @@ -248,9 +249,12 @@ impl RootHandler for ProtoStar { let future = startup_settings.generate_startup_token().unwrap(); std::env::set_var("STARDUST_STARTUP_TOKEN", future.await.unwrap()); - + let re = Regex::new(r"%[fFuUdDnNickvm]").unwrap(); + let exec = re.replace_all(&executable, ""); + let mut executable_array : Vec<&str> = dbg!(exec.split_whitespace().collect()); unsafe { - Command::new(executable) + Command::new(executable_array.remove(0)) + .args(executable_array) .stdin(Stdio::null()) .stdout(Stdio::null()) .stderr(Stdio::null()) diff --git a/src/xdg.rs b/src/xdg.rs index f9bd6af..f047082 100644 --- a/src/xdg.rs +++ b/src/xdg.rs @@ -12,7 +12,7 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{env, fs}; use walkdir::WalkDir; - +use regex::Regex; fn get_data_dirs() -> Vec { let xdg_data_dirs_str = std::env::var("XDG_DATA_DIRS").unwrap_or_default(); @@ -88,9 +88,13 @@ pub fn parse_desktop_file(path: PathBuf) -> Result { 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() { + let line = match line { Ok(line) => line, Err(err) => return Err(format!("Failed to read line: {}", err)), @@ -101,6 +105,14 @@ 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 { From 9c0504e6543e2f3e39f59ee8e1b613c43f2c7466 Mon Sep 17 00:00:00 2001 From: Nicola Guerrera Date: Wed, 1 Mar 2023 10:56:06 +0100 Subject: [PATCH 17/26] Clean up --- examples/hexagon_launcher.rs | 21 +++++++-------- src/protostar.rs | 50 +++++++++++++++++++++--------------- src/xdg.rs | 9 +++---- 3 files changed, 45 insertions(+), 35 deletions(-) diff --git a/examples/hexagon_launcher.rs b/examples/hexagon_launcher.rs index 7d00192..c337868 100644 --- a/examples/hexagon_launcher.rs +++ b/examples/hexagon_launcher.rs @@ -17,7 +17,9 @@ use stardust_xr_molecules::touch_plane::TouchPlane; use std::f32::consts::PI; use tween::TweenTime; -const APP_SIZE: f32 = 0.065; +const APP_SIZE: f32 = 0.06; +const PADDING: f32 = 0.005; + #[derive(Clone)] struct Hex { q: isize, @@ -41,9 +43,9 @@ impl Hex { } fn get_coords(&self) -> [f32; 3] { - let x = 3.0 / 2.0 * APP_SIZE / 2.0 * (-self.q - self.s).to_f32(); - let y = - 3.0_f32.sqrt() * APP_SIZE / 2.0 * ((-self.q - self.s).to_f32() / 2.0 + self.s.to_f32()); + let x = 3.0 / 2.0 * (APP_SIZE + PADDING) / 2.0 * (-self.q - self.s).to_f32(); + let y = 3.0_f32.sqrt() * (APP_SIZE + PADDING) / 2.0 + * ((-self.q - self.s).to_f32() / 2.0 + self.s.to_f32()); [x, y, 0.0] } @@ -126,7 +128,6 @@ impl RootHandler for AppHexGrid { fn frame(&mut self, info: FrameInfo) { self.button.touch_plane.update(); if self.button.touch_plane.touch_started() { - dbg!("Touch started"); let color = [0.0, 1.0, 0.0, 1.0]; self.button .model @@ -161,9 +162,9 @@ impl App { ) -> Option { let position = position.into(); let style = TextStyle { - character_height: APP_SIZE * 0.1, + character_height: (APP_SIZE + PADDING) * 0.1, bounds: Some(Bounds { - bounds: [APP_SIZE; 2].into(), + bounds: [(APP_SIZE + PADDING); 2].into(), fit: TextFit::Wrap, bounds_align: Alignment::XCenter | Alignment::YCenter, }), @@ -174,7 +175,7 @@ impl App { ProtoStar::create_from_desktop_file(parent, position, desktop_file.clone()).ok()?; let text = Text::create( protostar.content_parent(), - Transform::from_position_rotation([0.0, 0.0, 0.004], Quat::from_rotation_y(3.14)), + Transform::from_position_rotation([0.0, 0.0, 0.004], Quat::from_rotation_y(PI)), desktop_file.name.as_deref().unwrap_or("Unknown"), style, ) @@ -201,8 +202,8 @@ impl Button { let touch_plane = TouchPlane::new( client.get_root(), Transform::default(), - [APP_SIZE / 2.0, APP_SIZE / 2.0], - APP_SIZE / 2.0, + [(APP_SIZE + PADDING) / 2.0; 2], + (APP_SIZE + PADDING) / 2.0, )?; let model = Model::create( client.get_root(), diff --git a/src/protostar.rs b/src/protostar.rs index 7d6fed8..199c452 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -19,12 +19,15 @@ use std::process::{Command, Stdio}; use std::{f32::consts::PI, sync::Arc}; use tween::{QuartInOut, Tweener}; +const MODEL_SCALE: f32 = 0.03; +const ACTIVATION_DISTANCE: f32 = 1.0; + fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { return match &icon.icon_type { IconType::Png => { let t = Transform::from_rotation_scale( Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI), - [0.03, 0.03, 0.03], + [MODEL_SCALE; 3], ); let model = Model::create( @@ -61,9 +64,9 @@ pub struct ProtoStar { icon: Model, grabbable_shrink: Option>, grabbable_grow: Option>, + grabbabe_move: Option>, execute_command: String, currently_shown: bool, - grabbabe_move: Option>, } impl ProtoStar { pub fn create_from_desktop_file( @@ -130,8 +133,8 @@ impl ProtoStar { Ok(Model::create( grabbable.content_parent(), Transform::from_rotation_scale( - Quat::from_xyzw(0.0, 0.707, 0.707, 0.0), - [0.03, 0.03, 0.03], + Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI), + [MODEL_SCALE; 3], ), &ResourceID::new_namespaced("protostar", "hexagon/hexagon"), )?) @@ -157,9 +160,9 @@ impl ProtoStar { self.grabbabe_move = Some(Tweener::quart_in_out(1.0, 0.0001, 0.25)); //TODO make the scale a parameter } else { self.grabbable - .content_parent() - .set_scale(None, Vector3::from([1.0; 3])) - .unwrap(); + .content_parent() + .set_scale(None, Vector3::from([1.0; 3])) + .unwrap(); self.grabbabe_move = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25)); } self.currently_shown = !self.currently_shown; @@ -167,7 +170,6 @@ impl ProtoStar { } impl RootHandler for ProtoStar { fn frame(&mut self, info: FrameInfo) { - self.grabbable.update(&info); if let Some(grabbabe_move) = &mut self.grabbabe_move { @@ -175,14 +177,21 @@ impl RootHandler for ProtoStar { let scale = grabbabe_move.move_by(info.delta); self.grabbable .content_parent() - .set_position(Some(self.client.get_root()), [self.position.x*scale, self.position.y*scale, self.position.z*scale]) + .set_position( + Some(self.client.get_root()), + [ + self.position.x * scale, + self.position.y * scale, + self.position.z * scale, + ], + ) .unwrap(); } else { if grabbabe_move.final_value() == 0.0001 { self.grabbable - .content_parent() - .set_scale(None, Vector3::from([0.001; 3])) - .unwrap(); + .content_parent() + .set_scale(None, Vector3::from([0.001; 3])) + .unwrap(); } self.grabbabe_move = None; } @@ -196,7 +205,7 @@ impl RootHandler for ProtoStar { .unwrap(); } else { if self.currently_shown { - self.grabbable_grow = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25)); //TODO make the scale a parameter + self.grabbable_grow = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25)); self.grabbable.cancel_angular_velocity(); self.grabbable.cancel_linear_velocity(); } @@ -231,7 +240,7 @@ impl RootHandler for ProtoStar { startup_settings .set_root(self.grabbable.content_parent()) .unwrap(); - self.grabbable_shrink = Some(Tweener::quart_in_out(0.03, 0.0001, 0.25)); //TODO make the scale a parameter + self.grabbable_shrink = Some(Tweener::quart_in_out(MODEL_SCALE, 0.0001, 0.25)); let distance_future = self .grabbable .content_parent() @@ -243,18 +252,19 @@ impl RootHandler for ProtoStar { //TODO: split the executable string for the args tokio::task::spawn(async move { let distance_vector = distance_future.await.ok().unwrap().0; - let distance = - distance_vector.x.abs() + distance_vector.y.abs() + distance_vector.z.abs(); - if dbg!(distance) > 1.0 { + let distance = ((distance_vector.x.powi(2) + distance_vector.y.powi(2)).sqrt() + + distance_vector.z.powi(2)) + .sqrt(); + if dbg!(distance) > ACTIVATION_DISTANCE { let future = startup_settings.generate_startup_token().unwrap(); std::env::set_var("STARDUST_STARTUP_TOKEN", future.await.unwrap()); let re = Regex::new(r"%[fFuUdDnNickvm]").unwrap(); let exec = re.replace_all(&executable, ""); - let mut executable_array : Vec<&str> = dbg!(exec.split_whitespace().collect()); unsafe { - Command::new(executable_array.remove(0)) - .args(executable_array) + Command::new("sh") + .arg("-c") + .arg(exec.to_string()) .stdin(Stdio::null()) .stdout(Stdio::null()) .stderr(Stdio::null()) diff --git a/src/xdg.rs b/src/xdg.rs index f047082..e1b9f1d 100644 --- a/src/xdg.rs +++ b/src/xdg.rs @@ -1,6 +1,7 @@ use cached::proc_macro::cached; use color_eyre::eyre::Result; use linicon; +use regex::Regex; use resvg::render; use resvg::tiny_skia::{Pixmap, Transform}; use resvg::usvg::{FitTo, Tree}; @@ -12,7 +13,6 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{env, fs}; use walkdir::WalkDir; -use regex::Regex; fn get_data_dirs() -> Vec { let xdg_data_dirs_str = std::env::var("XDG_DATA_DIRS").unwrap_or_default(); @@ -88,13 +88,12 @@ pub fn parse_desktop_file(path: PathBuf) -> Result { let mut categories = Vec::new(); let mut icon = None; let mut no_display = false; - let mut desktop_entry_found = 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() { - let line = match line { Ok(line) => line, Err(err) => return Err(format!("Failed to read line: {}", err)), @@ -105,13 +104,13 @@ pub fn parse_desktop_file(path: PathBuf) -> Result { continue; } - if let Some(captures) = re.captures(&line){ + 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 + continue; } // Split the line into a key-value pair by looking for the first "=" character let parts = line.split_once('='); From db6637b77d854a7cd49272ab18b7907b3c0a1847 Mon Sep 17 00:00:00 2001 From: Nicola Guerrera Date: Thu, 2 Mar 2023 13:55:30 +0100 Subject: [PATCH 18/26] Improvements by Nova --- examples/hexagon_launcher.rs | 21 +------------- src/main.rs | 2 +- src/protostar.rs | 56 ++++++++++++++++++++++++------------ src/xdg.rs | 13 +++++---- 4 files changed, 47 insertions(+), 45 deletions(-) diff --git a/examples/hexagon_launcher.rs b/examples/hexagon_launcher.rs index c337868..9fd04ae 100644 --- a/examples/hexagon_launcher.rs +++ b/examples/hexagon_launcher.rs @@ -9,7 +9,7 @@ use protostar::{ use stardust_xr_fusion::{ client::{Client, FrameInfo, RootHandler}, core::values::Transform, - drawable::{Alignment, Bounds, MaterialParameter, Model, ResourceID, Text, TextFit, TextStyle}, + drawable::{MaterialParameter, Model, ResourceID}, node::NodeError, spatial::Spatial, }; @@ -149,7 +149,6 @@ impl RootHandler for AppHexGrid { } } struct App { - _text: Text, _desktop_file: DesktopFile, protostar: ProtoStar, } @@ -161,27 +160,9 @@ impl App { desktop_file: DesktopFile, ) -> Option { let position = position.into(); - let style = TextStyle { - character_height: (APP_SIZE + PADDING) * 0.1, - bounds: Some(Bounds { - bounds: [(APP_SIZE + PADDING); 2].into(), - fit: TextFit::Wrap, - bounds_align: Alignment::XCenter | Alignment::YCenter, - }), - text_align: Alignment::XCenter | Alignment::YCenter, - ..Default::default() - }; let protostar = ProtoStar::create_from_desktop_file(parent, position, desktop_file.clone()).ok()?; - let text = Text::create( - protostar.content_parent(), - Transform::from_position_rotation([0.0, 0.0, 0.004], Quat::from_rotation_y(PI)), - desktop_file.name.as_deref().unwrap_or("Unknown"), - style, - ) - .unwrap(); Some(App { - _text: text, _desktop_file: desktop_file, protostar, }) diff --git a/src/main.rs b/src/main.rs index f59b05a..a41c780 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,7 +42,7 @@ async fn main() -> Result<()> { parse_desktop_file(desktop_file).map_err(|e| Report::msg(e))?, )? } else if let Some(command) = args.command { - ProtoStar::new_raw(client.get_root(), [0.0, 0.0, 0.0], None, command)? + ProtoStar::new_raw(client.get_root(), [0.0, 0.0, 0.0], None, None, command)? } else { bail!("No command or desktop file, nothing to launch."); }; diff --git a/src/protostar.rs b/src/protostar.rs index 199c452..a279684 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -7,7 +7,7 @@ use regex::Regex; use stardust_xr_fusion::{ client::{Client, FrameInfo, RootHandler}, core::values::Transform, - drawable::{MaterialParameter, Model, ResourceID}, + drawable::{Alignment, MaterialParameter, Model, ResourceID, Text, TextStyle}, fields::BoxField, node::NodeType, spatial::Spatial, @@ -62,9 +62,10 @@ pub struct ProtoStar { grabbable: Grabbable, field: BoxField, icon: Model, + label: Option, grabbable_shrink: Option>, grabbable_grow: Option>, - grabbabe_move: Option>, + grabbable_move: Option>, execute_command: String, currently_shown: bool, } @@ -98,6 +99,7 @@ impl ProtoStar { Self::new_raw( parent, position, + desktop_file.name.as_deref(), icon, desktop_file.command.ok_or_else(|| eyre!("No command"))?, ) @@ -105,6 +107,7 @@ impl ProtoStar { pub fn new_raw( parent: &Spatial, position: impl Into>, + name: Option<&str>, icon: Option, execute_command: String, ) -> Result { @@ -139,31 +142,50 @@ impl ProtoStar { &ResourceID::new_namespaced("protostar", "hexagon/hexagon"), )?) })?; + + let label_style = TextStyle { + character_height: 0.15, + bounds: None, + text_align: Alignment::Center.into(), + ..Default::default() + }; + let label = name.and_then(|name| { + Text::create( + &icon, + Transform::from_position_rotation( + [0.0, 0.05, -0.6], + Quat::from_rotation_x(PI * 0.5), + ), + name, + label_style, + ) + .ok() + }); Ok(ProtoStar { client: parent.client()?, position, grabbable, field, + label, icon, grabbable_shrink: None, grabbable_grow: None, execute_command, currently_shown: true, - grabbabe_move: None, + grabbable_move: None, }) } pub fn content_parent(&self) -> &Spatial { self.grabbable.content_parent() } pub fn toggle(&mut self) { + self.grabbable.set_enabled(!self.currently_shown).unwrap(); if self.currently_shown { - self.grabbabe_move = Some(Tweener::quart_in_out(1.0, 0.0001, 0.25)); //TODO make the scale a parameter + self.grabbable_move = Some(Tweener::quart_in_out(1.0, 0.0001, 0.25)); //TODO make the scale a parameter } else { - self.grabbable - .content_parent() - .set_scale(None, Vector3::from([1.0; 3])) - .unwrap(); - self.grabbabe_move = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25)); + self.icon.set_enabled(true).unwrap(); + self.label.as_ref().map(|l| l.set_enabled(true).unwrap()); + self.grabbable_move = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25)); } self.currently_shown = !self.currently_shown; } @@ -172,9 +194,9 @@ impl RootHandler for ProtoStar { fn frame(&mut self, info: FrameInfo) { self.grabbable.update(&info); - if let Some(grabbabe_move) = &mut self.grabbabe_move { - if !grabbabe_move.is_finished() { - let scale = grabbabe_move.move_by(info.delta); + if let Some(grabbable_move) = &mut self.grabbable_move { + if !grabbable_move.is_finished() { + let scale = grabbable_move.move_by(info.delta); self.grabbable .content_parent() .set_position( @@ -187,13 +209,11 @@ impl RootHandler for ProtoStar { ) .unwrap(); } else { - if grabbabe_move.final_value() == 0.0001 { - self.grabbable - .content_parent() - .set_scale(None, Vector3::from([0.001; 3])) - .unwrap(); + if grabbable_move.final_value() == 0.0001 { + self.icon.set_enabled(false).unwrap(); + self.label.as_ref().map(|l| l.set_enabled(false).unwrap()); } - self.grabbabe_move = None; + self.grabbable_move = None; } } if let Some(grabbable_shrink) = &mut self.grabbable_shrink { diff --git a/src/xdg.rs b/src/xdg.rs index e1b9f1d..9390232 100644 --- a/src/xdg.rs +++ b/src/xdg.rs @@ -195,7 +195,9 @@ impl DesktopFile { let cache_icon_path = get_image_cache_dir().join(icon_name).canonicalize(); if cache_icon_path.is_ok() { - return vec![Icon::from_path(cache_icon_path.unwrap(), 128).unwrap()]; + if let Some(icon) = Icon::from_path(cache_icon_path.unwrap(), 128) { + return vec![icon]; + } } let mut icons_iter = linicon::lookup_icon(icon_name) @@ -232,12 +234,11 @@ pub enum IconType { 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") => Some(IconType::Png), - Some("svg") => Some(IconType::Svg), - Some("glb") | Some("gltf") => Some(IconType::Gltf), + Some("png") => IconType::Png, + Some("svg") => IconType::Svg, + Some("glb") | Some("gltf") => IconType::Gltf, _ => return None, - } - .unwrap(); + }; return Some(Icon { icon_type, path, From 2824e2dc2a14d1537ddf4272732c9a18c6304507 Mon Sep 17 00:00:00 2001 From: Nicola Guerrera Date: Thu, 2 Mar 2023 14:38:59 +0100 Subject: [PATCH 19/26] Fixed text --- src/protostar.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/protostar.rs b/src/protostar.rs index a279684..0706549 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -7,7 +7,7 @@ use regex::Regex; use stardust_xr_fusion::{ client::{Client, FrameInfo, RootHandler}, core::values::Transform, - drawable::{Alignment, MaterialParameter, Model, ResourceID, Text, TextStyle}, + drawable::{Alignment, MaterialParameter, Model, ResourceID, Text, TextStyle, Bounds, TextFit}, fields::BoxField, node::NodeType, spatial::Spatial, @@ -144,8 +144,12 @@ impl ProtoStar { })?; let label_style = TextStyle { - character_height: 0.15, - bounds: None, + character_height: MODEL_SCALE*4.0 , + bounds: Some(Bounds{ + bounds: [1.0; 2].into(), + fit: TextFit::Wrap, + bounds_align: Alignment::XCenter | Alignment::YCenter, + }), text_align: Alignment::Center.into(), ..Default::default() }; @@ -153,7 +157,7 @@ impl ProtoStar { Text::create( &icon, Transform::from_position_rotation( - [0.0, 0.05, -0.6], + [0.0, 0.1, -(MODEL_SCALE*8.0)], Quat::from_rotation_x(PI * 0.5), ), name, From 1c90ed98cfbaab930e8e3e89a9d570311a860cb7 Mon Sep 17 00:00:00 2001 From: Nicola Guerrera Date: Thu, 2 Mar 2023 16:03:29 +0100 Subject: [PATCH 20/26] make the hex launcher grabbable --- examples/app_grid.rs | 26 -------------------- examples/hexagon_launcher.rs | 39 ++++++++++++++++++++++-------- src/protostar.rs | 46 +++++++++++++++++++----------------- 3 files changed, 53 insertions(+), 58 deletions(-) diff --git a/examples/app_grid.rs b/examples/app_grid.rs index f892d58..36e55e6 100644 --- a/examples/app_grid.rs +++ b/examples/app_grid.rs @@ -1,5 +1,4 @@ use color_eyre::eyre::Result; -use glam::Quat; use manifest_dir_macros::directory_relative_path; use mint::Vector3; use protostar::{ @@ -8,8 +7,6 @@ use protostar::{ }; use stardust_xr_fusion::{ client::{Client, FrameInfo, RootHandler}, - core::values::Transform, - drawable::{Alignment, Bounds, Text, TextFit, TextStyle}, spatial::Spatial, }; @@ -72,7 +69,6 @@ impl RootHandler for AppGrid { } } struct App { - _text: Text, _desktop_file: DesktopFile, protostar: ProtoStar, } @@ -82,33 +78,11 @@ impl App { parent: &Spatial, position: impl Into>, desktop_file: DesktopFile, - //style: TextStyle, ) -> Option { let position = position.into(); - let style = TextStyle { - character_height: APP_SIZE * 0.1, - bounds: Some(Bounds { - bounds: [APP_SIZE; 2].into(), - fit: TextFit::Wrap, - bounds_align: Alignment::XCenter | Alignment::YCenter, - }), - text_align: Alignment::XCenter | Alignment::YCenter, - ..Default::default() - }; let protostar = ProtoStar::create_from_desktop_file(parent, position, desktop_file.clone()).ok()?; - let text = Text::create( - protostar.content_parent(), - Transform::from_position_rotation( - [0.0, 0.0, APP_SIZE / 2.0], - Quat::from_rotation_y(3.14), - ), - desktop_file.name.as_deref().unwrap_or("Unknown"), - style, - ) - .unwrap(); Some(App { - _text: text, _desktop_file: desktop_file, protostar, }) diff --git a/examples/hexagon_launcher.rs b/examples/hexagon_launcher.rs index 9fd04ae..318c68e 100644 --- a/examples/hexagon_launcher.rs +++ b/examples/hexagon_launcher.rs @@ -10,10 +10,11 @@ use stardust_xr_fusion::{ client::{Client, FrameInfo, RootHandler}, core::values::Transform, drawable::{MaterialParameter, Model, ResourceID}, + fields::BoxField, node::NodeError, spatial::Spatial, }; -use stardust_xr_molecules::touch_plane::TouchPlane; +use stardust_xr_molecules::{touch_plane::TouchPlane, GrabData, Grabbable}; use std::f32::consts::PI; use tween::TweenTime; @@ -110,7 +111,7 @@ impl AppHexGrid { }; apps.push( App::new( - client.get_root(), + button.grabbable.content_parent(), hex.get_coords(), desktop_files.pop().unwrap(), ) @@ -126,7 +127,7 @@ impl AppHexGrid { } impl RootHandler for AppHexGrid { fn frame(&mut self, info: FrameInfo) { - self.button.touch_plane.update(); + self.button.frame(info); if self.button.touch_plane.touch_started() { let color = [0.0, 1.0, 0.0, 1.0]; self.button @@ -176,30 +177,48 @@ impl RootHandler for App { struct Button { touch_plane: TouchPlane, + grabbable: Grabbable, model: Model, } impl Button { fn new(client: &Client) -> Result { - let touch_plane = TouchPlane::new( + let field = BoxField::create(client.get_root(), Transform::default(), [APP_SIZE; 3])?; + let grabbable = Grabbable::new( client.get_root(), Transform::default(), + &field, + GrabData { + max_distance: 0.01, + ..Default::default() + }, + )?; + field.set_spatial_parent(grabbable.content_parent())?; + let touch_plane = TouchPlane::new( + grabbable.content_parent(), + Transform::default(), [(APP_SIZE + PADDING) / 2.0; 2], (APP_SIZE + PADDING) / 2.0, )?; + let model = Model::create( - client.get_root(), + grabbable.content_parent(), Transform::from_rotation_scale( Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI), [0.03, 0.03, 0.03], ), &ResourceID::new_namespaced("protostar", "hexagon/hexagon"), )?; - model - .set_material_parameter(1, "color", MaterialParameter::Color([0.0, 0.0, 1.0, 1.0])) - .unwrap(); - Ok(Button { touch_plane, model }) + model.set_material_parameter(1, "color", MaterialParameter::Color([0.0, 0.0, 1.0, 1.0]))?; + Ok(Button { + touch_plane, + grabbable, + model, + }) } } impl RootHandler for Button { - fn frame(&mut self, _info: FrameInfo) {} + fn frame(&mut self, info: FrameInfo) { + self.touch_plane.update(); + self.grabbable.update(&info); + } } diff --git a/src/protostar.rs b/src/protostar.rs index 0706549..71e773a 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -5,18 +5,18 @@ use mint::Vector3; use nix::unistd::setsid; use regex::Regex; use stardust_xr_fusion::{ - client::{Client, FrameInfo, RootHandler}, + client::{FrameInfo, RootHandler}, core::values::Transform, - drawable::{Alignment, MaterialParameter, Model, ResourceID, Text, TextStyle, Bounds, TextFit}, + drawable::{Alignment, Bounds, MaterialParameter, Model, ResourceID, Text, TextFit, TextStyle}, fields::BoxField, node::NodeType, spatial::Spatial, startup_settings::StartupSettings, }; use stardust_xr_molecules::{GrabData, Grabbable}; +use std::f32::consts::PI; use std::os::unix::process::CommandExt; use std::process::{Command, Stdio}; -use std::{f32::consts::PI, sync::Arc}; use tween::{QuartInOut, Tweener}; const MODEL_SCALE: f32 = 0.03; @@ -57,7 +57,7 @@ fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { } pub struct ProtoStar { - client: Arc, + parent: Spatial, position: Vector3, grabbable: Grabbable, field: BoxField, @@ -112,14 +112,7 @@ impl ProtoStar { execute_command: String, ) -> Result { let position = position.into(); - let field = BoxField::create( - parent, - Transform::default(), - match icon.as_ref() { - Some(_) => [0.05, 0.0665, 0.005], - _ => [0.05; 3], - }, - )?; + let field = BoxField::create(parent, Transform::default(), [MODEL_SCALE * 2.0; 3])?; let grabbable = Grabbable::new( parent, Transform::from_position(position), @@ -129,6 +122,7 @@ impl ProtoStar { ..Default::default() }, )?; + grabbable.content_parent().set_spatial_parent(parent)?; field.set_spatial_parent(grabbable.content_parent())?; let icon = icon .map(|i| model_from_icon(grabbable.content_parent(), &i)) @@ -144,8 +138,8 @@ impl ProtoStar { })?; let label_style = TextStyle { - character_height: MODEL_SCALE*4.0 , - bounds: Some(Bounds{ + character_height: MODEL_SCALE * 4.0, + bounds: Some(Bounds { bounds: [1.0; 2].into(), fit: TextFit::Wrap, bounds_align: Alignment::XCenter | Alignment::YCenter, @@ -157,7 +151,7 @@ impl ProtoStar { Text::create( &icon, Transform::from_position_rotation( - [0.0, 0.1, -(MODEL_SCALE*8.0)], + [0.0, 0.1, -(MODEL_SCALE * 8.0)], Quat::from_rotation_x(PI * 0.5), ), name, @@ -166,7 +160,7 @@ impl ProtoStar { .ok() }); Ok(ProtoStar { - client: parent.client()?, + parent: parent.alias(), position, grabbable, field, @@ -204,7 +198,7 @@ impl RootHandler for ProtoStar { self.grabbable .content_parent() .set_position( - Some(self.client.get_root()), + Some(&self.parent), [ self.position.x * scale, self.position.y * scale, @@ -225,9 +219,13 @@ impl RootHandler for ProtoStar { let scale = grabbable_shrink.move_by(info.delta); self.grabbable .content_parent() - .set_scale(None, Vector3::from([scale; 3])) + .set_scale(Some(&self.parent), Vector3::from([scale; 3])) .unwrap(); } else { + self.grabbable + .content_parent() + .set_spatial_parent(&self.parent) + .unwrap(); if self.currently_shown { self.grabbable_grow = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25)); self.grabbable.cancel_angular_velocity(); @@ -236,11 +234,11 @@ impl RootHandler for ProtoStar { self.grabbable_shrink = None; self.grabbable .content_parent() - .set_position(Some(self.client.get_root()), self.position) + .set_position(Some(&self.parent), self.position) .unwrap(); self.grabbable .content_parent() - .set_rotation(Some(self.client.get_root()), Quat::default()) + .set_rotation(Some(&self.parent), Quat::default()) .unwrap(); self.icon .set_rotation( @@ -254,9 +252,13 @@ impl RootHandler for ProtoStar { let scale = grabbable_grow.move_by(info.delta); self.grabbable .content_parent() - .set_scale(None, Vector3::from([scale; 3])) + .set_scale(Some(&self.parent), Vector3::from([scale; 3])) .unwrap(); } else { + self.grabbable + .content_parent() + .set_spatial_parent(&self.parent) + .unwrap(); self.grabbable_grow = None; } } else if self.grabbable.grab_action().actor_stopped() { @@ -268,7 +270,7 @@ impl RootHandler for ProtoStar { let distance_future = self .grabbable .content_parent() - .get_position_rotation_scale(self.client.get_root()) + .get_position_rotation_scale(&self.parent) .unwrap(); let executable = self.execute_command.clone(); From d5728eeccca139bc21cd4d69ee7720edbc15b563 Mon Sep 17 00:00:00 2001 From: Nova Date: Wed, 8 Mar 2023 02:03:28 -0500 Subject: [PATCH 21/26] misc fixes --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- examples/hexagon_launcher.rs | 8 +++++++- src/protostar.rs | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d11602..4dccc73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2005,9 +2005,9 @@ dependencies = [ [[package]] name = "stardust-xr-molecules" -version = "0.22.0" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe2979f1816d21c3d1a88351f92e1a011992b166072a5fbc73221d6d4557a8e" +checksum = "44e840a893484f7c9445dde235090ad9cf829191f2bf2b913b5992cbf52ccbd8" dependencies = [ "color-rs", "glam 0.23.0", diff --git a/Cargo.toml b/Cargo.toml index a1cd0f8..a1e1c7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ regex = "1.7.1" resvg = "0.29.0" rustc-hash = "1.1.0" stardust-xr-fusion = "0.38.1" -stardust-xr-molecules = "0.22.0" +stardust-xr-molecules = "0.23.2" tokio = { version = "1.24.1", features = ["full"] } tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } tween = "2.0.0" diff --git a/examples/hexagon_launcher.rs b/examples/hexagon_launcher.rs index 318c68e..9b115bb 100644 --- a/examples/hexagon_launcher.rs +++ b/examples/hexagon_launcher.rs @@ -218,7 +218,13 @@ impl Button { } impl RootHandler for Button { fn frame(&mut self, info: FrameInfo) { - self.touch_plane.update(); self.grabbable.update(&info); + if self.grabbable.grab_action().actor_started() { + let _ = self.touch_plane.set_enabled(false); + } + if self.grabbable.grab_action().actor_stopped() { + let _ = self.touch_plane.set_enabled(true); + } + self.touch_plane.update(); } } diff --git a/src/protostar.rs b/src/protostar.rs index 71e773a..cda49e6 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -20,7 +20,7 @@ use std::process::{Command, Stdio}; use tween::{QuartInOut, Tweener}; const MODEL_SCALE: f32 = 0.03; -const ACTIVATION_DISTANCE: f32 = 1.0; +const ACTIVATION_DISTANCE: f32 = 0.5; fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { return match &icon.icon_type { From 549ae26d38344d39f2815b7ca67c7dad52ea9ab9 Mon Sep 17 00:00:00 2001 From: Nova Date: Mon, 1 May 2023 17:11:25 -0400 Subject: [PATCH 22/26] feat: update fusion --- Cargo.lock | 961 +++++++++++++++++++++-------------- Cargo.toml | 14 +- examples/hexagon_launcher.rs | 8 +- src/protostar.rs | 10 +- 4 files changed, 608 insertions(+), 385 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4dccc73..9183872 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,9 +42,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" dependencies = [ "memchr", ] @@ -76,10 +76,59 @@ dependencies = [ ] [[package]] -name = "arrayref" -version = "0.3.6" +name = "anstream" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" @@ -89,13 +138,13 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "async-trait" -version = "0.1.64" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -133,9 +182,9 @@ checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "bit_field" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" [[package]] name = "bitflags" @@ -145,15 +194,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" [[package]] name = "bytemuck" -version = "1.12.3" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" [[package]] name = "byteorder" @@ -163,15 +212,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cached" -version = "0.42.0" +version = "0.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5877db5d1af7fae60d06b5db9430b68056a69b3582a0be8e3691e87654aeb6" +checksum = "bc2fafddf188d13788e7099295a59b99e99b2148ab2195cae454e754cc099925" dependencies = [ "async-trait", "async_once", @@ -196,7 +245,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -207,9 +256,9 @@ checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" [[package]] name = "cc" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -219,9 +268,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" dependencies = [ "iana-time-zone", "js-sys", @@ -234,40 +283,45 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.3" +version = "4.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8d93d855ce6a0aa87b8473ef9169482f40abaa2e9e0993024c35c902cbd5920" +checksum = "8a1f23fa97e1d1641371b51f35535cb26959b8e27ab50d167a8b996b5bada819" dependencies = [ - "bitflags", + "clap_builder", "clap_derive", - "clap_lex", - "is-terminal", "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fdc5d93c358224b4d6867ef1356d740de2303e9892edc06c5340daeccd96bab" +dependencies = [ + "anstream", + "anstyle", + "bitflags", + "clap_lex", "strsim", - "termcolor", ] [[package]] name = "clap_derive" -version = "4.1.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" dependencies = [ "heck", - "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "clap_lex" -version = "0.3.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" -dependencies = [ - "os_str_bytes", -] +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" [[package]] name = "cluFlock" @@ -345,10 +399,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] -name = "core-foundation-sys" -version = "0.8.3" +name = "colorchoice" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "crc32fast" @@ -361,9 +421,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.6" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", @@ -371,9 +431,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -382,22 +442,22 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.13" +version = "0.9.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset 0.7.1", + "memoffset 0.8.0", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ "cfg-if", ] @@ -410,9 +470,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "cxx" -version = "1.0.83" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" dependencies = [ "cc", "cxxbridge-flags", @@ -422,9 +482,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.83" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" dependencies = [ "cc", "codespan-reporting", @@ -432,31 +492,31 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn", + "syn 2.0.15", ] [[package]] name = "cxxbridge-flags" -version = "1.0.83" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" [[package]] name = "cxxbridge-macro" -version = "1.0.83" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "darling" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0808e1bd8671fb44a113a14e13497557533369847788fa2ae912b6ebfce9fa8" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ "darling_core", "darling_macro", @@ -464,27 +524,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn", + "syn 1.0.109", ] [[package]] name = "darling_macro" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -495,31 +555,32 @@ checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" [[package]] name = "directories" -version = "4.0.1" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" dependencies = [ "dirs-sys", ] [[package]] name = "dirs" -version = "4.0.0" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.3.7" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", + "option-ext", "redox_users", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -533,9 +594,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "enum_dispatch" @@ -546,18 +607,18 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "errno" -version = "0.2.8" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -572,17 +633,18 @@ dependencies = [ [[package]] name = "exr" -version = "1.5.2" +version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eb5f255b5980bb0c8cf676b675d1a99be40f316881444f44e0462eaf5df5ded" +checksum = "bdd2162b720141a91a054640662d3edce3d50a944a50ffca5313cd951abb35b4" dependencies = [ "bit_field", "flume", - "half 2.1.0", + "half 2.2.1", "lebe", "miniz_oxide 0.6.2", + "rayon-core", "smallvec", - "threadpool", + "zune-inflate", ] [[package]] @@ -604,6 +666,15 @@ dependencies = [ "color-maps", ] +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +dependencies = [ + "simd-adler32", +] + [[package]] name = "file-locker" version = "1.1.2" @@ -618,6 +689,9 @@ name = "flagset" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda653ca797810c02f7ca4b804b40b8b95ae046eb989d356bce17919a8c25499" +dependencies = [ + "serde", +] [[package]] name = "flatbuffers" @@ -631,12 +705,12 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", - "miniz_oxide 0.6.2", + "miniz_oxide 0.7.1", ] [[package]] @@ -679,11 +753,11 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fontconfig-parser" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2be17a530a842f8a7a60f4397a08e8f08872849a5e31b20c7bd7301dac483296" +checksum = "4ab2e12762761366dcb876ab8b6e0cfa4797ddcd890575919f008b5ba655672a" dependencies = [ - "roxmltree 0.15.1", + "roxmltree", ] [[package]] @@ -716,9 +790,9 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "futures" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -730,9 +804,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -740,33 +814,33 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-io" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-core", "futures-sink", @@ -777,9 +851,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "js-sys", @@ -788,16 +862,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "gif" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" -dependencies = [ - "color_quant", - "weezl", -] - [[package]] name = "gif" version = "0.12.0" @@ -810,9 +874,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" [[package]] name = "glam" @@ -825,9 +889,9 @@ dependencies = [ [[package]] name = "glam" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e4afd9ad95555081e109fe1d21f2a30c691b5f0919c67dfa690a2e1eb6bd51c" +checksum = "ad83ab008a4fa3b31dfa713dd41b5a9bdea1e94e4cf1e2fc274ffbd49b0271d3" dependencies = [ "mint", ] @@ -840,9 +904,9 @@ checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "half" -version = "2.1.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad6a9459c9c30b177b925162351f97e7d967c7ea8bab3b8352805327daf45554" +checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" dependencies = [ "crunchy", ] @@ -856,6 +920,12 @@ 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" @@ -864,18 +934,9 @@ checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -887,17 +948,23 @@ dependencies = [ ] [[package]] -name = "iana-time-zone" -version = "0.1.53" +name = "hermit-abi" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "iana-time-zone" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows", ] [[package]] @@ -918,20 +985,20 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "image" -version = "0.24.5" +version = "0.24.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" +checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" dependencies = [ "bytemuck", "byteorder", "color_quant", "exr", - "gif 0.11.4", + "gif", "jpeg-decoder", "num-rational", "num-traits", "png", - "scoped_threadpool", + "qoi", "tiff", ] @@ -947,6 +1014,16 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + [[package]] name = "instant" version = "0.1.12" @@ -958,24 +1035,25 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.4" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ + "hermit-abi 0.3.1", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "is-terminal" -version = "0.4.2" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi 0.3.1", "io-lifetimes", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -989,9 +1067,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -1007,9 +1085,9 @@ dependencies = [ [[package]] name = "kurbo" -version = "0.9.0" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e119590a03caff1f7a582e8ee8c2164ddcc975791701188132fd1d1b518d3871" +checksum = "d676038719d1c892f91e6e85121550143c75880b42f7feff6d413a078cf91fb3" dependencies = [ "arrayvec", ] @@ -1028,9 +1106,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.138" +version = "0.2.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" [[package]] name = "linicon" @@ -1057,18 +1135,18 @@ dependencies = [ [[package]] name = "link-cplusplus" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" dependencies = [ "cc", ] [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "b64f40e5e03e0d54f03845c8197d0291253cdbedfb1cb46b13c2c117554a9f4c" [[package]] name = "lock_api" @@ -1091,14 +1169,23 @@ dependencies = [ [[package]] name = "manifest-dir-macros" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f08150cf2bab1fc47c2196f4f41173a27fcd0f684165e5458c0046b53a472e2f" +checksum = "450e5ef583bc05177c4975b9ea907047091a9f62e74e81fcafb99dbffac51e7e" dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.15", +] + +[[package]] +name = "map-range" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee4036c09f24ea403edcbb911a14dd641d13e181f85eff25fc3cbada6ef90f8" +dependencies = [ + "num-traits", ] [[package]] @@ -1118,9 +1205,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" -version = "0.5.8" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" dependencies = [ "libc", ] @@ -1143,21 +1230,21 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +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.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" -dependencies = [ - "adler", -] - [[package]] name = "miniz_oxide" version = "0.6.2" @@ -1167,6 +1254,16 @@ dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + [[package]] name = "mint" version = "0.5.9" @@ -1178,14 +1275,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -1222,9 +1319,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a58d1d356c6597d08cde02c2f09d785b09e28711837b1ed667dc652c08a694" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ "bitflags", "cfg-if", @@ -1286,49 +1383,55 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi 0.2.6", "libc", ] [[package]] name = "num_enum" -version = "0.5.7" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.7" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "object" -version = "0.30.2" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b8c786513eb403643f2a88c244c2aaa270ef2153f55094587d0c48a3cf22a83" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.16.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "ordered-multimap" @@ -1340,17 +1443,11 @@ dependencies = [ "hashbrown 0.9.1", ] -[[package]] -name = "os_str_bytes" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" - [[package]] name = "ouroboros" -version = "0.15.5" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbb50b356159620db6ac971c6d5c9ab788c9cc38a6f49619fca2a27acb062ca" +checksum = "e1358bd1558bd2a083fed428ffeda486fbfb323e698cdda7794259d592ca72db" dependencies = [ "aliasable", "ouroboros_macro", @@ -1358,15 +1455,15 @@ dependencies = [ [[package]] name = "ouroboros_macro" -version = "0.15.5" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0d9d1a6191c4f391f87219d1ea42b23f09ee84d64763cd05ee6ea88d9f384d" +checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" dependencies = [ "Inflector", "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1399,7 +1496,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.5", + "parking_lot_core 0.9.7", ] [[package]] @@ -1418,15 +1515,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.5" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -1452,7 +1549,7 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1469,14 +1566,15 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "png" -version = "0.17.6" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f0e7f4c94ec26ff209cee506314212639d6c91b80afb82984819fafce9df01c" +checksum = "aaeebc51f9e7d2c150d3f3bfeb667f2aa985db5ef1e3d212847bdedb488beeaa" dependencies = [ "bitflags", "crc32fast", + "fdeflate", "flate2", - "miniz_oxide 0.5.4", + "miniz_oxide 0.7.1", ] [[package]] @@ -1487,13 +1585,12 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro-crate" -version = "1.2.1" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "thiserror", - "toml", + "toml_edit", ] [[package]] @@ -1505,7 +1602,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -1522,9 +1619,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -1539,13 +1636,13 @@ dependencies = [ "directories", "dirs", "ez-pixmap", - "glam 0.22.0", + "glam 0.24.0", "image", "lazy_static", "linicon", "manifest-dir-macros", "mint", - "nix 0.26.1", + "nix 0.26.2", "regex", "resvg", "rustc-hash", @@ -1560,10 +1657,19 @@ dependencies = [ ] [[package]] -name = "quote" -version = "1.0.21" +name = "qoi" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -1628,9 +1734,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ "either", "rayon-core", @@ -1638,9 +1744,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -1685,13 +1791,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.7.1", ] [[package]] @@ -1700,14 +1806,20 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", ] [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" [[package]] name = "remove_dir_all" @@ -1724,7 +1836,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76888219c0881e22b0ceab06fddcfe83163cd81642bd60c7842387f9c968a72e" dependencies = [ - "gif 0.12.0", + "gif", "jpeg-decoder", "log", "pico-args", @@ -1739,9 +1851,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.34" +version = "0.8.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3603b7d71ca82644f79b5a06d1220e9a58ede60bd32255f698cb1af8838b8db3" +checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59" dependencies = [ "bytemuck", ] @@ -1753,21 +1865,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdc23d1ace03d6b8153c7d16f0708cd80b61ee8e80304954803354e67e40d150" dependencies = [ "log", - "roxmltree 0.18.0", + "roxmltree", "simplecss", "siphasher", "svgtypes 0.9.0", ] -[[package]] -name = "roxmltree" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9de9831a129b122e7e61f242db509fa9d0838008bf0b29bb0624669edfe48a" -dependencies = [ - "xmlparser", -] - [[package]] name = "roxmltree" version = "0.18.0" @@ -1789,9 +1892,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hash" @@ -1810,16 +1913,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.7" +version = "0.37.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03" +checksum = "8bbfc1d1c7c40c01715f47d71444744a81669ca84e8b63e25a55e169b1f86433" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1847,12 +1950,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scoped_threadpool" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" - [[package]] name = "scopeguard" version = "1.1.0" @@ -1861,45 +1958,45 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "scratch" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" [[package]] name = "semver" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "serde_repr" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e" +checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -1913,13 +2010,19 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" + [[package]] name = "simplecss" version = "0.2.1" @@ -1943,9 +2046,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -1953,18 +2056,16 @@ dependencies = [ [[package]] name = "spin" -version = "0.9.4" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" dependencies = [ "lock_api", ] [[package]] name = "stardust-xr" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "798fb96a582da0fe0c791cfc10005a5eff3fbe3024e73b139456e63eda1380f4" +version = "0.11.3" dependencies = [ "chrono", "cluFlock", @@ -1982,9 +2083,7 @@ dependencies = [ [[package]] name = "stardust-xr-fusion" -version = "0.38.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a0a1ee96c4be1d4866e1920feb4fb732b810a6cc2e9f7eeff66f07f9c2f496" +version = "0.39.1" dependencies = [ "color-eyre", "color-rs", @@ -2005,13 +2104,14 @@ dependencies = [ [[package]] name = "stardust-xr-molecules" -version = "0.23.2" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44e840a893484f7c9445dde235090ad9cf829191f2bf2b913b5992cbf52ccbd8" +checksum = "185291b7cf2368a9ef0deed9e5ebf2c49954e862d2747aeee96cc08a57796c1c" dependencies = [ "color-rs", - "glam 0.23.0", + "glam 0.24.0", "lazy_static", + "map-range", "mint", "rustc-hash", "serde", @@ -2023,9 +2123,7 @@ dependencies = [ [[package]] name = "stardust-xr-schemas" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bebfbddbcba7db7ce8180bb373c9ad5a7d58ff60ee213ca07562820d7e8aab0" +version = "1.4.1" dependencies = [ "flatbuffers", "flexbuffers", @@ -2083,15 +2181,26 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ffacedcdcf1da6579c907279b4f3c5492fbce99fbbf227f5ed270a589c2765" dependencies = [ - "kurbo 0.9.0", + "kurbo 0.9.4", "siphasher", ] [[package]] name = "syn" -version = "1.0.105" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", @@ -2110,51 +2219,43 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - [[package]] name = "tiff" version = "0.8.1" @@ -2179,9 +2280,9 @@ dependencies = [ [[package]] name = "tiny-skia" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfef3412c6975196fdfac41ef232f910be2bb37b9dd3313a49a1a6bc815a5bdb" +checksum = "df8493a203431061e901613751931f047d1971337153f96d0e5e363d6dbf6a67" dependencies = [ "arrayref", "arrayvec", @@ -2193,9 +2294,9 @@ dependencies = [ [[package]] name = "tiny-skia-path" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b5edac058fc98f51c935daea4d805b695b38e2f151241cad125ade2a2ac20d" +checksum = "adbfb5d3f3dd57a0e11d12f4f13d4ebbbc1b5c15b7ab0a156d030b21da5f677c" dependencies = [ "arrayref", "bytemuck", @@ -2204,14 +2305,13 @@ dependencies = [ [[package]] name = "tokio" -version = "1.25.0" +version = "1.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "num_cpus", "parking_lot 0.12.1", @@ -2219,27 +2319,35 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] -name = "toml" -version = "0.5.10" +name = "toml_datetime" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" + +[[package]] +name = "toml_edit" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" dependencies = [ - "serde", + "indexmap", + "toml_datetime", + "winnow", ] [[package]] @@ -2256,13 +2364,13 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -2298,9 +2406,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "matchers", "nu-ansi-term", @@ -2322,15 +2430,15 @@ checksum = "0609f771ad9c6155384897e1df4d948e692667cc0588548b68eb44d052b27633" [[package]] name = "tween" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f114398da254e78168e12edec0ece6b4ca15a97262ecd8e5efd5025e3fc30204" +checksum = "90aba0c5c9d07e12084c38dcbf0f4b16f74801b588650ad633f9d7e9a0143952" [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-bidi-mirroring" @@ -2352,9 +2460,9 @@ checksum = "2281c8c1d221438e373249e065ca4989c4c36952c211ff21a0ee91c44a3869e7" [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-script" @@ -2396,7 +2504,7 @@ dependencies = [ "data-url", "flate2", "imagesize", - "kurbo 0.9.0", + "kurbo 0.9.4", "log", "rctree", "rosvgtree", @@ -2410,7 +2518,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "195386e01bc35f860db024de275a76e7a31afdf975d18beb6d0e44764118b4db" dependencies = [ "fontdb", - "kurbo 0.9.0", + "kurbo 0.9.4", "log", "rustybuzz", "unicode-bidi", @@ -2419,6 +2527,12 @@ dependencies = [ "usvg", ] +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "valuable" version = "0.1.0" @@ -2433,12 +2547,11 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] @@ -2456,9 +2569,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2466,24 +2579,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2491,22 +2604,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "weezl" @@ -2546,61 +2659,154 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-sys" -version = "0.42.0" +name = "windows" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winnow" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5617da7e1f97bf363947d767b91aaf3c2bbc19db7fda9c65af1278713d58e0a2" +dependencies = [ + "memchr", +] [[package]] name = "xkbcommon" @@ -2616,3 +2822,12 @@ name = "xmlparser" version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] diff --git a/Cargo.toml b/Cargo.toml index a1e1c7e..401041d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,13 +4,13 @@ version = "0.4.0" edition = "2021" [dependencies] -cached = "0.42.0" +cached = "0.43.0" clap = { version = "4.1.3", features = ["derive"] } color-eyre = "0.6.2" -directories = "4.0.1" -dirs = "4.0.0" +directories = "5.0.0" +dirs = "5.0.0" ez-pixmap = "0.2.2" -glam = { version = "0.22.0", features = ["mint"] } +glam = { version = "0.24.0", features = ["mint"] } image = "0.24.5" lazy_static = "1.4.0" linicon = "2.3.0" @@ -20,8 +20,8 @@ nix = "0.26.1" regex = "1.7.1" resvg = "0.29.0" rustc-hash = "1.1.0" -stardust-xr-fusion = "0.38.1" -stardust-xr-molecules = "0.23.2" +stardust-xr-fusion = "0.39.2" +stardust-xr-molecules = "0.24.0" tokio = { version = "1.24.1", features = ["full"] } tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } tween = "2.0.0" @@ -29,4 +29,4 @@ ustr = "0.9.0" walkdir = "2.3.2" [dev-dependencies] -tempdir = "0.3.7" +tempdir = "0.3.7" \ No newline at end of file diff --git a/examples/hexagon_launcher.rs b/examples/hexagon_launcher.rs index 9b115bb..437aa7f 100644 --- a/examples/hexagon_launcher.rs +++ b/examples/hexagon_launcher.rs @@ -183,7 +183,7 @@ struct Button { impl Button { fn new(client: &Client) -> Result { let field = BoxField::create(client.get_root(), Transform::default(), [APP_SIZE; 3])?; - let grabbable = Grabbable::new( + let grabbable = Grabbable::create( client.get_root(), Transform::default(), &field, @@ -193,11 +193,13 @@ impl Button { }, )?; field.set_spatial_parent(grabbable.content_parent())?; - let touch_plane = TouchPlane::new( + let touch_plane = TouchPlane::create( grabbable.content_parent(), Transform::default(), [(APP_SIZE + PADDING) / 2.0; 2], (APP_SIZE + PADDING) / 2.0, + 0.0..1.0, + 0.0..1.0, )?; let model = Model::create( @@ -218,7 +220,7 @@ impl Button { } impl RootHandler for Button { fn frame(&mut self, info: FrameInfo) { - self.grabbable.update(&info); + let _ = self.grabbable.update(&info); if self.grabbable.grab_action().actor_started() { let _ = self.touch_plane.set_enabled(false); } diff --git a/src/protostar.rs b/src/protostar.rs index cda49e6..b86cbae 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -113,7 +113,7 @@ impl ProtoStar { ) -> Result { let position = position.into(); let field = BoxField::create(parent, Transform::default(), [MODEL_SCALE * 2.0; 3])?; - let grabbable = Grabbable::new( + let grabbable = Grabbable::create( parent, Transform::from_position(position), &field, @@ -190,7 +190,7 @@ impl ProtoStar { } impl RootHandler for ProtoStar { fn frame(&mut self, info: FrameInfo) { - self.grabbable.update(&info); + let _ = self.grabbable.update(&info); if let Some(grabbable_move) = &mut self.grabbable_move { if !grabbable_move.is_finished() { @@ -274,6 +274,7 @@ impl RootHandler for ProtoStar { .unwrap(); let executable = self.execute_command.clone(); + let client = self.content_parent().client().unwrap(); //TODO: split the executable string for the args tokio::task::spawn(async move { @@ -284,6 +285,11 @@ impl RootHandler for ProtoStar { if dbg!(distance) > ACTIVATION_DISTANCE { let future = startup_settings.generate_startup_token().unwrap(); + let env = client.get_connection_environment().unwrap().await.unwrap(); + for (k, v) in env.into_iter() { + std::env::set_var(k, v); + } + std::env::set_var("STARDUST_STARTUP_TOKEN", future.await.unwrap()); let re = Regex::new(r"%[fFuUdDnNickvm]").unwrap(); let exec = re.replace_all(&executable, ""); From 307df9829fa5da5868fb64d03a3499b7ec6f36f9 Mon Sep 17 00:00:00 2001 From: Nova Date: Mon, 1 May 2023 17:11:52 -0400 Subject: [PATCH 23/26] feat: update cargo.lock --- Cargo.lock | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9183872..0a50459 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2066,6 +2066,8 @@ dependencies = [ [[package]] name = "stardust-xr" version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce36e7f3285a6b1391a298c01bbbb92c9f3d77967a78d1beaac21c0043d23b" dependencies = [ "chrono", "cluFlock", @@ -2083,7 +2085,9 @@ dependencies = [ [[package]] name = "stardust-xr-fusion" -version = "0.39.1" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75214564f52e031f3873963b855cbd9a8253502ef18912480e8e84ad98a30c8" dependencies = [ "color-eyre", "color-rs", @@ -2123,7 +2127,9 @@ dependencies = [ [[package]] name = "stardust-xr-schemas" -version = "1.4.1" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bebfbddbcba7db7ce8180bb373c9ad5a7d58ff60ee213ca07562820d7e8aab0" dependencies = [ "flatbuffers", "flexbuffers", From d41e34d7461e8024f1136c00076ec916ca9f4a22 Mon Sep 17 00:00:00 2001 From: Nova Date: Thu, 11 May 2023 00:17:13 -0400 Subject: [PATCH 24/26] modular protostar --- Cargo.lock | 34 +- Cargo.toml | 9 +- assets/hexagon.blend | Bin 926728 -> 932488 bytes examples/hexagon_launcher.rs | 277 ++++- mono_crash.0.0.json | 1665 ++++++++++++++++++++++++++++ res/protostar/hexagon/hexagon.bin | Bin 2500 -> 2500 bytes res/protostar/hexagon/hexagon.gltf | 558 +++++----- src/application.rs | 100 ++ src/lib.rs | 1 + src/main.rs | 38 +- src/protostar.rs | 113 +- src/xdg.rs | 8 +- 12 files changed, 2345 insertions(+), 458 deletions(-) create mode 100644 mono_crash.0.0.json create mode 100644 src/application.rs diff --git a/Cargo.lock b/Cargo.lock index 0a50459..f03af8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -878,15 +878,6 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" -[[package]] -name = "glam" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f597d56c1bd55a811a1be189459e8fad2bbc272616375602443bdfb37fa774" -dependencies = [ - "mint", -] - [[package]] name = "glam" version = "0.24.0" @@ -1636,7 +1627,7 @@ dependencies = [ "directories", "dirs", "ez-pixmap", - "glam 0.24.0", + "glam", "image", "lazy_static", "linicon", @@ -2065,9 +2056,7 @@ dependencies = [ [[package]] name = "stardust-xr" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ce36e7f3285a6b1391a298c01bbbb92c9f3d77967a78d1beaac21c0043d23b" +version = "0.11.4" dependencies = [ "chrono", "cluFlock", @@ -2085,9 +2074,7 @@ dependencies = [ [[package]] name = "stardust-xr-fusion" -version = "0.39.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75214564f52e031f3873963b855cbd9a8253502ef18912480e8e84ad98a30c8" +version = "0.40.2" dependencies = [ "color-eyre", "color-rs", @@ -2103,17 +2090,16 @@ dependencies = [ "thiserror", "tokio", "tracing", - "xkbcommon", ] [[package]] name = "stardust-xr-molecules" -version = "0.24.0" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185291b7cf2368a9ef0deed9e5ebf2c49954e862d2747aeee96cc08a57796c1c" +checksum = "724a92e67765300651d165950b70ec4800d4ad6450f473882f6024d5a559ea9d" dependencies = [ "color-rs", - "glam 0.24.0", + "glam", "lazy_static", "map-range", "mint", @@ -2127,16 +2113,16 @@ dependencies = [ [[package]] name = "stardust-xr-schemas" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bebfbddbcba7db7ce8180bb373c9ad5a7d58ff60ee213ca07562820d7e8aab0" +version = "1.5.0" dependencies = [ "flatbuffers", "flexbuffers", - "glam 0.22.0", + "glam", + "manifest-dir-macros", "mint", "ouroboros", "serde", + "serde_repr", "thiserror", ] diff --git a/Cargo.toml b/Cargo.toml index 401041d..dfe4147 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,8 @@ nix = "0.26.1" regex = "1.7.1" resvg = "0.29.0" rustc-hash = "1.1.0" -stardust-xr-fusion = "0.39.2" -stardust-xr-molecules = "0.24.0" +stardust-xr-fusion = "0.40.2" +stardust-xr-molecules = "0.24.3" tokio = { version = "1.24.1", features = ["full"] } tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } tween = "2.0.0" @@ -29,4 +29,7 @@ ustr = "0.9.0" walkdir = "2.3.2" [dev-dependencies] -tempdir = "0.3.7" \ No newline at end of file +tempdir = "0.3.7" + +[patch.crates-io.stardust-xr-fusion] +path = "../../core/fusion" \ No newline at end of file diff --git a/assets/hexagon.blend b/assets/hexagon.blend index 17e162ab471f8b7c01f6677187370eb722f2ef1d..20cd188765d107276eb95413c4627acedd3a4754 100644 GIT binary patch literal 932488 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 MW#e$L{LjDt0tZwC&;S4c literal 926728 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

Rf7y7)-GALy=%em@ZT82K6+BEKX4O8If`2Vxw+ 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

`e+! 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

~XCH0HIyUjCK7l&tU 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 DlV+{h diff --git a/examples/hexagon_launcher.rs b/examples/hexagon_launcher.rs index 437aa7f..9b230c8 100644 --- a/examples/hexagon_launcher.rs +++ b/examples/hexagon_launcher.rs @@ -3,23 +3,26 @@ use glam::Quat; use manifest_dir_macros::directory_relative_path; use mint::Vector3; use protostar::{ - protostar::ProtoStar, - xdg::{get_desktop_files, parse_desktop_file, DesktopFile}, + application::Application, + xdg::{get_desktop_files, parse_desktop_file, DesktopFile, Icon, IconType}, }; use stardust_xr_fusion::{ client::{Client, FrameInfo, RootHandler}, core::values::Transform, - drawable::{MaterialParameter, Model, ResourceID}, + drawable::{Alignment, Bounds, MaterialParameter, Model, ResourceID, Text, TextFit, TextStyle}, fields::BoxField, node::NodeError, + node::NodeType, spatial::Spatial, }; use stardust_xr_molecules::{touch_plane::TouchPlane, GrabData, Grabbable}; use std::f32::consts::PI; use tween::TweenTime; +use tween::{QuartInOut, Tweener}; const APP_SIZE: f32 = 0.06; const PADDING: f32 = 0.005; +const ACTIVATION_DISTANCE: f32 = 0.5; #[derive(Clone)] struct Hex { @@ -110,7 +113,7 @@ impl AppHexGrid { break; }; apps.push( - App::new( + App::create_from_desktop_file( button.grabbable.content_parent(), hex.get_coords(), desktop_files.pop().unwrap(), @@ -132,16 +135,20 @@ impl RootHandler for AppHexGrid { let color = [0.0, 1.0, 0.0, 1.0]; self.button .model - .set_material_parameter(1, "color", MaterialParameter::Color(color)) + .model_part("Hex") + .unwrap() + .set_material_parameter("color", MaterialParameter::Color(color)) .unwrap(); for app in &mut self.apps { - app.protostar.toggle(); + app.toggle(); } } else if self.button.touch_plane.touch_stopped() { let color = [0.0, 0.0, 1.0, 1.0]; self.button .model - .set_material_parameter(1, "color", MaterialParameter::Color(color)) + .model_part("Hex") + .unwrap() + .set_material_parameter("color", MaterialParameter::Color(color)) .unwrap(); } for app in &mut self.apps { @@ -149,31 +156,6 @@ impl RootHandler for AppHexGrid { } } } -struct App { - _desktop_file: DesktopFile, - protostar: ProtoStar, -} - -impl App { - fn new( - parent: &Spatial, - position: impl Into>, - desktop_file: DesktopFile, - ) -> Option { - let position = position.into(); - let protostar = - ProtoStar::create_from_desktop_file(parent, position, desktop_file.clone()).ok()?; - Some(App { - _desktop_file: desktop_file, - protostar, - }) - } -} -impl RootHandler for App { - fn frame(&mut self, info: FrameInfo) { - self.protostar.frame(info); - } -} struct Button { touch_plane: TouchPlane, @@ -210,7 +192,9 @@ impl Button { ), &ResourceID::new_namespaced("protostar", "hexagon/hexagon"), )?; - model.set_material_parameter(1, "color", MaterialParameter::Color([0.0, 0.0, 1.0, 1.0]))?; + model + .model_part("Hex")? + .set_material_parameter("color", MaterialParameter::Color([0.0, 0.0, 1.0, 1.0]))?; Ok(Button { touch_plane, grabbable, @@ -230,3 +214,230 @@ impl RootHandler for Button { self.touch_plane.update(); } } + +fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { + return match &icon.icon_type { + IconType::Png => { + let t = Transform::from_rotation_scale( + Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI), + [APP_SIZE * 0.5; 3], + ); + + let model = Model::create( + parent, + t, + &ResourceID::new_namespaced("protostar", "hexagon/hexagon"), + )?; + model + .model_part("Hex")? + .set_material_parameter("color", MaterialParameter::Color([0.0, 1.0, 1.0, 1.0]))?; + model.model_part("Icon")?.set_material_parameter( + "diffuse", + MaterialParameter::Texture(ResourceID::Direct(icon.path.clone())), + )?; + Ok(model) + } + IconType::Gltf => Ok(Model::create( + parent, + Transform::from_scale([0.05; 3]), + &ResourceID::new_direct(icon.path.clone())?, + )?), + _ => panic!("Invalid Icon Type"), + }; +} + +pub struct App { + application: Application, + parent: Spatial, + position: Vector3, + grabbable: Grabbable, + _field: BoxField, + icon: Model, + label: Option, + grabbable_shrink: Option>, + grabbable_grow: Option>, + grabbable_move: Option>, + currently_shown: bool, +} +impl App { + pub fn create_from_desktop_file( + parent: &Spatial, + position: impl Into>, + desktop_file: DesktopFile, + ) -> Result { + let position = position.into(); + let field = BoxField::create(parent, Transform::default(), [APP_SIZE; 3])?; + let application = Application::create(&parent.client()?, desktop_file)?; + let icon = application.icon(128, false); + let grabbable = Grabbable::create( + parent, + Transform::from_position(position), + &field, + GrabData { + max_distance: 0.01, + frame_cancel_threshold: 50, + ..Default::default() + }, + )?; + grabbable.content_parent().set_spatial_parent(parent)?; + field.set_spatial_parent(grabbable.content_parent())?; + let icon = icon + .map(|i| model_from_icon(grabbable.content_parent(), &i)) + .unwrap_or_else(|| { + Ok(Model::create( + grabbable.content_parent(), + Transform::from_rotation_scale( + Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI), + [APP_SIZE * 0.5; 3], + ), + &ResourceID::new_namespaced("protostar", "hexagon/hexagon"), + )?) + })?; + + let label_style = TextStyle { + character_height: APP_SIZE * 2.0, + bounds: Some(Bounds { + bounds: [1.0; 2].into(), + fit: TextFit::Wrap, + bounds_align: Alignment::XCenter | Alignment::YCenter, + }), + text_align: Alignment::Center.into(), + ..Default::default() + }; + let label = application.name().and_then(|name| { + Text::create( + &icon, + Transform::from_position_rotation( + [0.0, 0.1, -(APP_SIZE * 4.0)], + Quat::from_rotation_x(PI * 0.5), + ), + name, + label_style, + ) + .ok() + }); + Ok(App { + parent: parent.alias(), + position, + grabbable, + _field: field, + label, + application, + icon, + grabbable_shrink: None, + grabbable_grow: None, + grabbable_move: None, + currently_shown: true, + }) + } + pub fn content_parent(&self) -> &Spatial { + self.grabbable.content_parent() + } + pub fn toggle(&mut self) { + self.grabbable.set_enabled(!self.currently_shown).unwrap(); + if self.currently_shown { + self.grabbable_move = Some(Tweener::quart_in_out(1.0, 0.0001, 0.25)); //TODO make the scale a parameter + } else { + self.icon.set_enabled(true).unwrap(); + self.label.as_ref().map(|l| l.set_enabled(true).unwrap()); + self.grabbable_move = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25)); + } + self.currently_shown = !self.currently_shown; + } +} +impl RootHandler for App { + fn frame(&mut self, info: FrameInfo) { + let _ = self.grabbable.update(&info); + + if let Some(grabbable_move) = &mut self.grabbable_move { + if !grabbable_move.is_finished() { + let scale = grabbable_move.move_by(info.delta); + self.grabbable + .content_parent() + .set_position( + Some(&self.parent), + [ + self.position.x * scale, + self.position.y * scale, + self.position.z * scale, + ], + ) + .unwrap(); + } else { + if grabbable_move.final_value() == 0.0001 { + self.icon.set_enabled(false).unwrap(); + self.label.as_ref().map(|l| l.set_enabled(false).unwrap()); + } + self.grabbable_move = None; + } + } + if let Some(grabbable_shrink) = &mut self.grabbable_shrink { + if !grabbable_shrink.is_finished() { + let scale = grabbable_shrink.move_by(info.delta); + self.grabbable + .content_parent() + .set_scale(Some(&self.parent), Vector3::from([scale; 3])) + .unwrap(); + } else { + self.grabbable + .content_parent() + .set_spatial_parent(&self.parent) + .unwrap(); + if self.currently_shown { + self.grabbable_grow = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25)); + self.grabbable.cancel_angular_velocity(); + self.grabbable.cancel_linear_velocity(); + } + self.grabbable_shrink = None; + self.grabbable + .content_parent() + .set_position(Some(&self.parent), self.position) + .unwrap(); + self.grabbable + .content_parent() + .set_rotation(Some(&self.parent), Quat::default()) + .unwrap(); + self.icon + .set_rotation( + None, + Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI), + ) + .unwrap(); + } + } else if let Some(grabbable_grow) = &mut self.grabbable_grow { + if !grabbable_grow.is_finished() { + let scale = grabbable_grow.move_by(info.delta); + self.grabbable + .content_parent() + .set_scale(Some(&self.parent), Vector3::from([scale; 3])) + .unwrap(); + } else { + self.grabbable + .content_parent() + .set_spatial_parent(&self.parent) + .unwrap(); + self.grabbable_grow = None; + } + } else if self.grabbable.valid() && self.grabbable.grab_action().actor_stopped() { + self.grabbable_shrink = Some(Tweener::quart_in_out(APP_SIZE * 0.5, 0.0001, 0.25)); + let Ok(distance_future) = self.grabbable + .content_parent() + .get_position_rotation_scale(&self.parent) + else {return}; + + let application = self.application.clone(); + let space = self.content_parent().alias(); + + //TODO: split the executable string for the args + tokio::task::spawn(async move { + let distance_vector = distance_future.await.ok().unwrap().0; + let distance = ((distance_vector.x.powi(2) + distance_vector.y.powi(2)).sqrt() + + distance_vector.z.powi(2)) + .sqrt(); + if dbg!(distance) > ACTIVATION_DISTANCE { + let _ = application.launch(&space); + } + }); + } + } +} diff --git a/mono_crash.0.0.json b/mono_crash.0.0.json new file mode 100644 index 0000000..1387e70 --- /dev/null +++ b/mono_crash.0.0.json @@ -0,0 +1,1665 @@ +{ + "protocol_version" : "0.0.6", + "configuration" : { + "version" : "(6.13.0) (explicit/88268f9e785)", + "tlc" : "__thread", + "sigsgev" : "altstack", + "notifications" : "epoll", + "architecture" : "amd64", + "disabled_features" : "com", + "smallconfig" : "disabled", + "bigarrays" : "disabled", + "softdebug" : "enabled", + "interpreter" : "enabled", + "llvm_support" : "disabled", + "suspend" : "preemptive" + }, + "memory" : { + "minor_gc_time" : "0", + "major_gc_time" : "9842", + "minor_gc_count" : "0", + "major_gc_count" : "1", + "major_gc_time_concurrent" : "0" + }, + "threads" : [ + { + "is_managed" : false, + "offset_free_hash" : "0x0", + "offset_rich_hash" : "0x0", + "crashed" : false, + "native_thread_id" : "0x7f48c6dff6c0", + "thread_info_addr" : "0x7f4828000b70", + "thread_name" : "Finalizer", + "ctx" : { + "IP" : "0x7f48ce074f0e", + "SP" : "0x7f48c6dfec80", + "BP" : "(nil)" + }, + "unmanaged_frames" : [ + { + "is_managed" : "false", + "native_address" : "0x7f48c74b98ad", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7603471", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7603797", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7603559", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7514514", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce02aab0", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce074f0e", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce0803e0", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c763abe1", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c76040f0", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7603fb2", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c767d428", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c767d0bb", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce07844b", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce0fbe40", + "native_offset" : "0x00000" + } + + ] + }, + { + "is_managed" : true, + "offset_free_hash" : "0x9bfc75db6", + "offset_rich_hash" : "0x9bfc75f80", + "crashed" : false, + "native_thread_id" : "0x7f4772dfc6c0", + "thread_info_addr" : "0x7f4728000b70", + "thread_name" : "Timer-Scheduler", + "ctx" : { + "IP" : "0x7f48ce074f0e", + "SP" : "0x7f4772dfaf20", + "BP" : "(nil)" + }, + "managed_frames" : [ + { + "is_managed" : "false", + "native_address" : "unregistered" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x00000", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x00000" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x60020a2", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x00044" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x600208e", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x00014" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x600208d", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x00000" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x6002088", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x00019" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x600208b", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x00000" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x600214a", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x0003c" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x6001f6d", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x00014" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x6001f15", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x00071" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x6001f13", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x00000" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x6001f12", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x0002b" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x6001f6f", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x00008" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x00000", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x00065" + } + + ], + "unmanaged_frames" : [ + { + "is_managed" : "false", + "native_address" : "0x7f48c74b98ad", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7603471", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7603797", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7603559", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7514514", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce02aab0", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce074f0e", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce077ab5", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c764e6eb", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c760d387", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c760d1fd", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c760d419", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c75ff08d", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c75aef0d", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x00000", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x00000" + } + + ] +}, +{ + "is_managed" : false, + "offset_free_hash" : "0x0", + "offset_rich_hash" : "0x0", + "crashed" : false, + "native_thread_id" : "0x7f47713fe6c0", + "thread_info_addr" : "0x7f4720000b70", + "thread_name" : "Thread Pool I/O", + "ctx" : { + "IP" : "0x7f48ce074f0e", + "SP" : "0x7f47713fdb50", + "BP" : "(nil)" + }, + "unmanaged_frames" : [ + { + "is_managed" : "false", + "native_address" : "0x7f48c74b98ad", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7603471", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7603797", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7603559", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7514514", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce02aab0", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce074f0e", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce077ab5", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c764e6eb", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c76578fa", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7565721", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c76040f0", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7603fb2", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c767d428", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c767d0bb", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce07844b", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce0fbe40", + "native_offset" : "0x00000" + } + + ] +}, +{ + "is_managed" : false, + "offset_free_hash" : "0x0", + "offset_rich_hash" : "0x0", + "crashed" : false, + "native_thread_id" : "0x7f4770ffc6c0", + "thread_info_addr" : "0x7f4708000b70", + "thread_name" : "Thread Pool Wor", + "ctx" : { + "IP" : "0x7f48ce074f0e", + "SP" : "0x7f4770ffbc50", + "BP" : "(nil)" + }, + "unmanaged_frames" : [ + { + "is_managed" : "false", + "native_address" : "0x7f48c74b98ad", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7603471", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7603797", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7603559", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7514514", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce02aab0", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce074f0e", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce0801cb", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7565e4a", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c76040f0", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7603fb2", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c767d428", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c767d0bb", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce07844b", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce0fbe40", + "native_offset" : "0x00000" + } + + ] +}, +{ + "is_managed" : false, + "offset_free_hash" : "0x0", + "offset_rich_hash" : "0x0", + "crashed" : false, + "native_thread_id" : "0x7f4770bfa6c0", + "thread_info_addr" : "0x7f4700000b70", + "thread_name" : "Thread Pool Wor", + "ctx" : { + "IP" : "0x7f48ce074f0e", + "SP" : "0x7f4770bf9c50", + "BP" : "(nil)" + }, + "unmanaged_frames" : [ + { + "is_managed" : "false", + "native_address" : "0x7f48c74b98ad", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7603471", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7603797", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7603559", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7514514", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce02aab0", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce074f0e", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce0801cb", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7565e4a", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c76040f0", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c7603fb2", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c767d428", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48c767d0bb", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce07844b", + "native_offset" : "0x00000" + } +, + { + "is_managed" : "false", + "native_address" : "0x7f48ce0fbe40", + "native_offset" : "0x00000" + } + + ] +}, +{ + "is_managed" : true, + "offset_free_hash" : "0x709fb4f11", + "offset_rich_hash" : "0x709fb5098", + "crashed" : false, + "native_thread_id" : "0x7f47737fe6c0", + "thread_info_addr" : "0x7f4750000b70", + "thread_name" : "LeapC Worker", + "ctx" : { + "IP" : "0x7f48ce074f0e", + "SP" : "0x7f47737fcf10", + "BP" : "(nil)" + }, + "managed_frames" : [ + { + "is_managed" : "false", + "native_address" : "unregistered" + } +, + { + "is_managed" : "true", + "guid" : "E5EF776C-70F2-43B0-927D-7EF9AD9EEE08", + "token" : "0x00000", + "native_offset" : "0x0", + "filename" : "LeapMotion.LeapCSharp.dll", + "sizeofimage" : "0x1e000", + "timestamp" : "0xf5879ec7", + "il_offset" : "0x0000c" + } +, + { + "is_managed" : "true", + "guid" : "E5EF776C-70F2-43B0-927D-7EF9AD9EEE08", + "token" : "0x6000020", + "native_offset" : "0x0", + "filename" : "LeapMotion.LeapCSharp.dll", + "sizeofimage" : "0x1e000", + "timestamp" : "0xf5879ec7", + "il_offset" : "0x0005e" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x6001f6d", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x00014" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x6001f15", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x00071" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x6001f13", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x00000" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x6001f12", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x0002b" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x6001f6f", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x00008" + } +, + { + "is_managed" : "true", + "guid" : "7FB66C41-B6E6-41FB-91B7-FD5E48B4C50D", + "token" : "0x00000", + "native_offset" : "0x0", + "filename" : "mscorlib.dll", + "sizeofimage" : "0x470000", + "timestamp" : "0xa9ddd34c", + "il_offset" : "0x00065" + } + + ], +"unmanaged_frames" : [ +{ + "is_managed" : "false", + "native_address" : "0x7f48c74b98ad", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603471", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603797", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603559", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7514514", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce02aab0", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce074f0e", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce077d85", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c6290ebf", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c62ca9d8", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c62cbcfe", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c62a6b98", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c62a77b1", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "true", + "guid" : "E5EF776C-70F2-43B0-927D-7EF9AD9EEE08", + "token" : "0x00000", + "native_offset" : "0x0", + "filename" : "LeapMotion.LeapCSharp.dll", + "sizeofimage" : "0x1e000", + "timestamp" : "0xf5879ec7", + "il_offset" : "0x00000" + } + +] +}, +{ +"is_managed" : false, +"offset_free_hash" : "0x0", +"offset_rich_hash" : "0x0", +"crashed" : true, +"native_thread_id" : "0x7f48d03ad740", +"thread_info_addr" : "0x15d8f00", +"thread_name" : "ultraleap-hand-", +"ctx" : { + "IP" : "0x7f48cfb0d407", + "SP" : "0x7ffd9dd9ba10", + "BP" : "0x15bfdc0" +}, +"unmanaged_frames" : [ +{ + "is_managed" : "false", + "native_address" : "0x7f48c74b98ad", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603471", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603797", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603d2b", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c75151c9", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c74bdd12", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c744302d", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce02aab0", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48cfb0d407", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48cfa86adb", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48cfa86b2b", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48cf062396", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48cf0676e5", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48cf0533f9", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce014850", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce01490a", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x201029", + "native_offset" : "0x00000" + } + +] +}, +{ +"is_managed" : false, +"offset_free_hash" : "0x0", +"offset_rich_hash" : "0x0", +"crashed" : false, +"native_thread_id" : "0x7f477041e6c0", +"thread_info_addr" : "0x7f46f8000b70", +"thread_name" : "Thread Pool Wor", +"ctx" : { + "IP" : "0x7f48ce074f0e", + "SP" : "0x7f477041dc50", + "BP" : "(nil)" +}, +"unmanaged_frames" : [ +{ + "is_managed" : "false", + "native_address" : "0x7f48c74b98ad", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603471", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603797", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603559", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7514514", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce02aab0", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce074f0e", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce0801cb", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7565e4a", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c76040f0", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603fb2", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c767d428", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c767d0bb", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce07844b", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce0fbe40", + "native_offset" : "0x00000" + } + +] +}, +{ +"is_managed" : false, +"offset_free_hash" : "0x0", +"offset_rich_hash" : "0x0", +"crashed" : false, +"native_thread_id" : "0x7f482019c6c0", +"thread_info_addr" : "0x7f475c000b70", +"thread_name" : "Loading.Preload", +"ctx" : { + "IP" : "0x7f48ce0f42ed", + "SP" : "0x7f482019bda8", + "BP" : "0x7f48d02a0448" +}, +"unmanaged_frames" : [ +{ + "is_managed" : "false", + "native_address" : "0x7f48c74b98ad", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603471", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603797", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603559", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7514514", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce02aab0", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce0f42ed", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48cfa4f1ce", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48cee327b9", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48cee326f6", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48cef00f29", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce07844b", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce0fbe40", + "native_offset" : "0x00000" + } + +] +}, +{ +"is_managed" : false, +"offset_free_hash" : "0x0", +"offset_rich_hash" : "0x0", +"crashed" : false, +"native_thread_id" : "0x7f47715ff6c0", +"thread_info_addr" : "0x7f472c000b70", +"thread_name" : "Thread Pool I/O", +"ctx" : { + "IP" : "0x7f48ce0eec0f", + "SP" : "0x7f47715fec30", + "BP" : "0x7f48c76cc80c" +}, +"unmanaged_frames" : [ +{ + "is_managed" : "false", + "native_address" : "0x7f48c74b98ad", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603471", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603797", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603559", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7514514", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce02aab0", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce0eec0f", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c76080ef", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7607b99", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c76040f0", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603fb2", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c767d428", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c767d0bb", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce07844b", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce0fbe40", + "native_offset" : "0x00000" + } + +] +}, +{ +"is_managed" : false, +"offset_free_hash" : "0x0", +"offset_rich_hash" : "0x0", +"crashed" : false, +"native_thread_id" : "0x7f47711fd6c0", +"thread_info_addr" : "0x7f4724000b70", +"thread_name" : "Thread Pool Wor", +"ctx" : { + "IP" : "0x7f48ce074f0e", + "SP" : "0x7f47711fcc50", + "BP" : "(nil)" +}, +"unmanaged_frames" : [ +{ + "is_managed" : "false", + "native_address" : "0x7f48c74b98ad", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603471", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603797", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603559", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7514514", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce02aab0", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce074f0e", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce0801cb", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7565e4a", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c76040f0", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603fb2", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c767d428", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c767d0bb", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce07844b", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce0fbe40", + "native_offset" : "0x00000" + } + +] +}, +{ +"is_managed" : false, +"offset_free_hash" : "0x0", +"offset_rich_hash" : "0x0", +"crashed" : false, +"native_thread_id" : "0x7f4770dfb6c0", +"thread_info_addr" : "0x7f470c000b70", +"thread_name" : "Thread Pool Wor", +"ctx" : { + "IP" : "0x7f48ce074f0e", + "SP" : "0x7f4770dfac50", + "BP" : "(nil)" +}, +"unmanaged_frames" : [ +{ + "is_managed" : "false", + "native_address" : "0x7f48c74b98ad", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603471", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603797", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603559", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7514514", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce02aab0", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce074f0e", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce0801cb", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7565e4a", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c76040f0", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603fb2", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c767d428", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c767d0bb", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce07844b", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce0fbe40", + "native_offset" : "0x00000" + } + +] +}, +{ +"is_managed" : false, +"offset_free_hash" : "0x0", +"offset_rich_hash" : "0x0", +"crashed" : false, +"native_thread_id" : "0x7f47709f96c0", +"thread_info_addr" : "0x7f4704000b70", +"thread_name" : "Thread Pool Wor", +"ctx" : { + "IP" : "0x7f48ce074f0e", + "SP" : "0x7f47709f8c50", + "BP" : "(nil)" +}, +"unmanaged_frames" : [ +{ + "is_managed" : "false", + "native_address" : "0x7f48c74b98ad", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603471", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603797", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603559", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7514514", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce02aab0", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce074f0e", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce0801cb", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7565e4a", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c76040f0", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c7603fb2", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c767d428", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48c767d0bb", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce07844b", + "native_offset" : "0x00000" + } +, +{ + "is_managed" : "false", + "native_address" : "0x7f48ce0fbe40", + "native_offset" : "0x00000" + } + +] +} +] +} \ No newline at end of file diff --git a/res/protostar/hexagon/hexagon.bin b/res/protostar/hexagon/hexagon.bin index e90047477bd60d3127ba12773e1445f54a0fae23..f56fec498900bdc2691e02795b23018812ceef0b 100644 GIT binary patch literal 2500 zcmeHJOGuPa6h6K(9UtSUH90;~D=kx}Cc&}(dsZ4XEruX)Q?$!Pi=viBGYiR8LD8;V z&_J}25?sXmF3J#7=mDAqMh~JEhNR}s&biGUj^Wj5*P{8k=lkxRbI$$Fx%Y>Eb#>!k z;7uh?K0o0Qx;z9o>nPR{AVx#}cy89#VC=V-UbFHX$R@WO8dV72 zv=8Y)$w|N7muf*9C!NUJNHa=KI&~jNGuk-m)UheeX-+ydPibD4U(0&s9GNRa8~R^_ z)kuF0+OWHQKxGGgXv28JfO;HRRyKUF7*?a+Ic393Q!kyi`%B7(msi57?^XZ}Hbr}o z=d-n4%DqsR#$aMlefF25!I`gd6$zE0!R4`DeZs0`kojH~M{E6U*OkzVBa zw|imrsQGOgqmyBE_TvX-@Jx3P^8DHS6Q^(HM;co`%{h1O{!|8UOofo=!_!ZlqIn-0 zJbdcCbMm_v4Gz5vAn_^?jHr9a>6SW1t+n<+i6@Rv36JG8F9(En$>=^D8f z3g>-d%uF9d>|fW;c}z}GyUb(lgC#bjp2%1hV0tKOk$vF4f$6P`75=Q*+@CeEbQUhh z@>$E+I@keQppHf%1fUiQ$QD2z?1r7-A?txssDTD3B3lGoVIS;*60#*w4vo+RUb0>& zhIZHsKC(X82FD#%vQrmb+0zH(Y=YzIFaqOTo4ja)bY)sRm%pT2`S TpbF~g*#j-~1ZiGR*a5!)Yp7AZ literal 2500 zcmeHJO-NKx82!{S%TXIgt;z8xt<*AgY7!jl-EZYYj0*_@H$|(G5K63_kuD@xwWxOO zf(D{ZnBXGjxv)a~fih4lF#17?FeZPJHg@jwz2O>NopvpHkNcf-@0{=6bH8`r@K#qh zzWg%LvTm*gEW9Q(0qLo<^yVrJ<$J#!mnL`ma?$K6iOu z@bK__H|HrtmVT=Lw3!UYeq(a}Y3P&T@L-)^+s~#P9xM&~a55Yogzblu;U^m60$wO{ zuEo^x(P+B$K6PC?Z>oGGnr_P6s~A^#xFH^s<8I8U<0H{tD|aaFu2cE(h7M~S>D@XG z50(agSmp3|j>9KQZ}7}2hfk{x@XR_6pH{BzIlE|Z)|&u>Gbf>Z+l>@ z6|}vXzvU+OtJAA{S8JmBm+zMRpOqn*y{j5^9(6C)l=hr|TL)dPZjWkB)Ld1s)O>RUtZKq<|L)nPgw3QCfUdlnt zp*$+5O7b9jD2wW7KY0Vwzn5 diff --git a/res/protostar/hexagon/hexagon.gltf b/res/protostar/hexagon/hexagon.gltf index 2199495..f0480e3 100644 --- a/res/protostar/hexagon/hexagon.gltf +++ b/res/protostar/hexagon/hexagon.gltf @@ -1,276 +1,286 @@ { - "asset" : { - "generator" : "Khronos glTF Blender I/O v3.4.50", - "version" : "2.0" - }, - "extensionsUsed" : [ - "KHR_materials_unlit" - ], - "scene" : 0, - "scenes" : [ - { - "name" : "Scene", - "nodes" : [ - 0 - ] - } - ], - "nodes" : [ - { - "mesh" : 0, - "name" : "Hex" - } - ], - "materials" : [ - { - "alphaCutoff" : 0.5, - "alphaMode" : "MASK", - "extensions" : { - "KHR_materials_unlit" : {} - }, - "name" : "Icon", - "pbrMetallicRoughness" : { - "baseColorTexture" : { - "index" : 0, - "texCoord" : 0 - }, - "metallicFactor" : 0, - "roughnessFactor" : 0.9 - } - }, - { - "alphaMode" : "BLEND", - "extensions" : { - "KHR_materials_unlit" : {} - }, - "name" : "Hex", - "pbrMetallicRoughness" : { - "baseColorFactor" : [ - 1, - 0, - 0, - 1 - ], - "baseColorTexture" : { - "index" : 1, - "texCoord" : 0 - }, - "metallicFactor" : 0, - "roughnessFactor" : 0.9 - } - } - ], - "meshes" : [ - { - "name" : "Circle", - "primitives" : [ - { - "attributes" : { - "COLOR_0" : 0, - "POSITION" : 1, - "TEXCOORD_0" : 2, - "NORMAL" : 3 - }, - "indices" : 4, - "material" : 0 - }, - { - "attributes" : { - "COLOR_0" : 5, - "POSITION" : 6, - "TEXCOORD_0" : 7, - "NORMAL" : 8 - }, - "indices" : 9, - "material" : 1 - } - ] - } - ], - "textures" : [ - { - "sampler" : 0, - "source" : 0 - }, - { - "sampler" : 1, - "source" : 1 - } - ], - "images" : [ - { - "mimeType" : "image/png", - "name" : "icon_test", - "uri" : "icon_test.png" - }, - { - "mimeType" : "image/png", - "name" : "hex_atlas", - "uri" : "hex_atlas.png" - } - ], - "accessors" : [ - { - "bufferView" : 0, - "componentType" : 5123, - "count" : 4, - "normalized" : true, - "type" : "VEC4" - }, - { - "bufferView" : 1, - "componentType" : 5126, - "count" : 4, - "max" : [ - 0.5853440165519714, - 0.05000000074505806, - 0.5853440165519714 - ], - "min" : [ - -0.5853440165519714, - 0.05000000074505806, - -0.5853440165519714 - ], - "type" : "VEC3" - }, - { - "bufferView" : 2, - "componentType" : 5126, - "count" : 4, - "type" : "VEC2" - }, - { - "bufferView" : 3, - "componentType" : 5126, - "count" : 4, - "type" : "VEC3" - }, - { - "bufferView" : 4, - "componentType" : 5123, - "count" : 6, - "type" : "SCALAR" - }, - { - "bufferView" : 5, - "componentType" : 5123, - "count" : 54, - "normalized" : true, - "type" : "VEC4" - }, - { - "bufferView" : 6, - "componentType" : 5126, - "count" : 54, - "max" : [ - 1.0441828966140747, - 0.05000000074505806, - 0.9042890667915344 - ], - "min" : [ - -1.0441828966140747, - 0, - -0.9042890667915344 - ], - "type" : "VEC3" - }, - { - "bufferView" : 7, - "componentType" : 5126, - "count" : 54, - "type" : "VEC2" - }, - { - "bufferView" : 8, - "componentType" : 5126, - "count" : 54, - "type" : "VEC3" - }, - { - "bufferView" : 9, - "componentType" : 5123, - "count" : 84, - "type" : "SCALAR" - } - ], - "bufferViews" : [ - { - "buffer" : 0, - "byteLength" : 32, - "byteOffset" : 0, - "target" : 34962 - }, - { - "buffer" : 0, - "byteLength" : 48, - "byteOffset" : 32, - "target" : 34962 - }, - { - "buffer" : 0, - "byteLength" : 32, - "byteOffset" : 80, - "target" : 34962 - }, - { - "buffer" : 0, - "byteLength" : 48, - "byteOffset" : 112, - "target" : 34962 - }, - { - "buffer" : 0, - "byteLength" : 12, - "byteOffset" : 160, - "target" : 34963 - }, - { - "buffer" : 0, - "byteLength" : 432, - "byteOffset" : 172, - "target" : 34962 - }, - { - "buffer" : 0, - "byteLength" : 648, - "byteOffset" : 604, - "target" : 34962 - }, - { - "buffer" : 0, - "byteLength" : 432, - "byteOffset" : 1252, - "target" : 34962 - }, - { - "buffer" : 0, - "byteLength" : 648, - "byteOffset" : 1684, - "target" : 34962 - }, - { - "buffer" : 0, - "byteLength" : 168, - "byteOffset" : 2332, - "target" : 34963 - } - ], - "samplers" : [ - { - "magFilter" : 9729, - "minFilter" : 9987, - "wrapS" : 33071, - "wrapT" : 33071 - }, - { - "magFilter" : 9729, - "minFilter" : 9987 - } - ], - "buffers" : [ - { - "byteLength" : 2500, - "uri" : "hexagon.bin" - } - ] + "asset":{ + "generator":"Khronos glTF Blender I/O v3.5.30", + "version":"2.0" + }, + "extensionsUsed":[ + "KHR_materials_unlit" + ], + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0, + 1 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"Icon" + }, + { + "mesh":1, + "name":"Hex" + } + ], + "materials":[ + { + "alphaCutoff":0.5, + "alphaMode":"MASK", + "extensions":{ + "KHR_materials_unlit":{} + }, + "name":"Icon", + "pbrMetallicRoughness":{ + "baseColorTexture":{ + "index":0, + "texCoord":0 + }, + "metallicFactor":0, + "roughnessFactor":0.9 + } + }, + { + "alphaMode":"BLEND", + "extensions":{ + "KHR_materials_unlit":{} + }, + "name":"Hex", + "pbrMetallicRoughness":{ + "baseColorFactor":[ + 1, + 0, + 0, + 1 + ], + "baseColorTexture":{ + "index":1, + "texCoord":0 + }, + "metallicFactor":0, + "roughnessFactor":0.9 + } + } + ], + "meshes":[ + { + "name":"Circle", + "primitives":[ + { + "attributes":{ + "COLOR_0":0, + "POSITION":1, + "TEXCOORD_0":2, + "NORMAL":3 + }, + "indices":4, + "material":0 + } + ] + }, + { + "name":"Circle.001", + "primitives":[ + { + "attributes":{ + "COLOR_0":5, + "POSITION":6, + "TEXCOORD_0":7, + "NORMAL":8 + }, + "indices":9, + "material":1 + } + ] + } + ], + "textures":[ + { + "sampler":0, + "source":0 + }, + { + "sampler":1, + "source":1 + } + ], + "images":[ + { + "mimeType":"image/png", + "name":"icon_test", + "uri":"icon_test.png" + }, + { + "mimeType":"image/png", + "name":"hex_atlas", + "uri":"hex_atlas.png" + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5123, + "count":4, + "normalized":true, + "type":"VEC4" + }, + { + "bufferView":1, + "componentType":5126, + "count":4, + "max":[ + 0.5853440165519714, + 0.05000000074505806, + 0.5853440165519714 + ], + "min":[ + -0.5853440165519714, + 0.05000000074505806, + -0.5853440165519714 + ], + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":4, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5126, + "count":4, + "type":"VEC3" + }, + { + "bufferView":4, + "componentType":5123, + "count":6, + "type":"SCALAR" + }, + { + "bufferView":5, + "componentType":5123, + "count":54, + "normalized":true, + "type":"VEC4" + }, + { + "bufferView":6, + "componentType":5126, + "count":54, + "max":[ + 1.0441828966140747, + 0.05000000074505806, + 0.9042890667915344 + ], + "min":[ + -1.0441828966140747, + 0, + -0.9042890667915344 + ], + "type":"VEC3" + }, + { + "bufferView":7, + "componentType":5126, + "count":54, + "type":"VEC2" + }, + { + "bufferView":8, + "componentType":5126, + "count":54, + "type":"VEC3" + }, + { + "bufferView":9, + "componentType":5123, + "count":84, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":32, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":48, + "byteOffset":32, + "target":34962 + }, + { + "buffer":0, + "byteLength":32, + "byteOffset":80, + "target":34962 + }, + { + "buffer":0, + "byteLength":48, + "byteOffset":112, + "target":34962 + }, + { + "buffer":0, + "byteLength":12, + "byteOffset":160, + "target":34963 + }, + { + "buffer":0, + "byteLength":432, + "byteOffset":172, + "target":34962 + }, + { + "buffer":0, + "byteLength":648, + "byteOffset":604, + "target":34962 + }, + { + "buffer":0, + "byteLength":432, + "byteOffset":1252, + "target":34962 + }, + { + "buffer":0, + "byteLength":648, + "byteOffset":1684, + "target":34962 + }, + { + "buffer":0, + "byteLength":168, + "byteOffset":2332, + "target":34963 + } + ], + "samplers":[ + { + "magFilter":9729, + "minFilter":9987, + "wrapS":33071, + "wrapT":33071 + }, + { + "magFilter":9729, + "minFilter":9987 + } + ], + "buffers":[ + { + "byteLength":2500, + "uri":"hexagon.bin" + } + ] } diff --git a/src/application.rs b/src/application.rs new file mode 100644 index 0000000..466bddc --- /dev/null +++ b/src/application.rs @@ -0,0 +1,100 @@ +use crate::xdg::{DesktopFile, Icon, IconType}; +use nix::unistd::setsid; +use regex::Regex; +use stardust_xr_fusion::{ + client::Client, + node::{NodeError, NodeType}, + spatial::Spatial, + startup_settings::StartupSettings, +}; +use std::{ + os::unix::process::CommandExt, + process::{Command, Stdio}, + sync::Arc, +}; + +#[derive(Debug, Clone)] +pub struct Application { + desktop_file: DesktopFile, + startup_settings: Arc, +} +impl Application { + pub fn create(client: &Arc, desktop_file: DesktopFile) -> Result { + if desktop_file.no_display { + return Err(NodeError::DoesNotExist); + } + + let startup_settings = Arc::new(StartupSettings::create(client)?); + Ok(Application { + desktop_file, + startup_settings, + }) + } + + pub fn name(&self) -> Option<&str> { + self.desktop_file.name.as_deref() + } + pub fn categories(&self) -> &[String] { + self.desktop_file.categories.as_slice() + } + + pub fn icon(&self, preferred_px_size: u16, prefer_3d: bool) -> Option { + let raw_icons = self.desktop_file.get_raw_icons(preferred_px_size); + let mut icon = raw_icons.iter().max_by_key(|i| i.size).cloned(); + if prefer_3d { + icon = raw_icons + .into_iter() + .find(|i| match i.icon_type { + IconType::Gltf => true, + _ => false, + }) + .or(icon); + } + + icon.and_then(|i| i.cached_process(preferred_px_size).ok()) + } + + pub fn launch(&self, launch_space: &Spatial) -> Result<(), NodeError> { + self.startup_settings.set_root(launch_space)?; + let future_startup_token = self.startup_settings.generate_startup_token()?; + let future_connection_env = self + .startup_settings + .node() + .client()? + .get_connection_environment()?; + + let executable = self + .desktop_file + .command + .clone() + .ok_or(NodeError::DoesNotExist)?; + tokio::task::spawn(async move { + let Ok(startup_token) = future_startup_token.await else {return}; + let Ok(connection_env) = future_connection_env.await else {return}; + + for (k, v) in connection_env.into_iter() { + std::env::set_var(k, v); + } + + std::env::set_var("STARDUST_STARTUP_TOKEN", startup_token); + let re = Regex::new(r"%[fFuUdDnNickvm]").unwrap(); + let exec = re.replace_all(&executable, ""); + unsafe { + Command::new("sh") + .arg("-c") + .arg(exec.to_string()) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .pre_exec(|| { + _ = setsid(); + Ok(()) + }) + .spawn() + .expect("Failed to start child process"); + } + }); + + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index bda7f63..c9c8dfe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,3 @@ +pub mod application; pub mod protostar; pub mod xdg; diff --git a/src/main.rs b/src/main.rs index a41c780..dd84573 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,5 @@ use clap::Parser; -use color_eyre::{ - eyre::{bail, Result}, - Report, -}; +use color_eyre::{eyre::Result, Report}; use manifest_dir_macros::directory_relative_path; use protostar::{protostar::ProtoStar, xdg::parse_desktop_file}; use stardust_xr_fusion::client::Client; @@ -11,21 +8,8 @@ use std::path::PathBuf; #[derive(Debug, Parser)] #[clap(author, version, about, long_about = None)] struct Args { - #[clap(short, long, default_value_t = 0.1)] - size: f32, - #[clap( - short, - long, - conflicts_with = "command", - required_unless_present = "command", - conflicts_with = "icon", - required_unless_present = "icon" - )] - desktop_file: Option, - #[clap(short, long, conflicts_with = "desktop_file", requires = "command")] - icon: Option, - #[clap(short, long, conflicts_with = "desktop_file", requires = "icon")] - command: Option, + // #[clap(short, long)] + desktop_file: PathBuf, } #[tokio::main(flavor = "current_thread")] @@ -35,17 +19,11 @@ async fn main() -> Result<()> { let (client, event_loop) = Client::connect_with_async_loop().await?; client.set_base_prefixes(&[directory_relative_path!("res")]); - let protostar = if let Some(desktop_file) = args.desktop_file { - ProtoStar::create_from_desktop_file( - client.get_root(), - [0.0, 0.0, 0.0], - parse_desktop_file(desktop_file).map_err(|e| Report::msg(e))?, - )? - } else if let Some(command) = args.command { - ProtoStar::new_raw(client.get_root(), [0.0, 0.0, 0.0], None, None, command)? - } else { - bail!("No command or desktop file, nothing to launch."); - }; + let protostar = ProtoStar::create_from_desktop_file( + client.get_root(), + [0.0, 0.0, 0.0], + parse_desktop_file(args.desktop_file).map_err(|e| Report::msg(e))?, + )?; let _root = client.wrap_root(protostar); diff --git a/src/protostar.rs b/src/protostar.rs index b86cbae..1fa8406 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -1,9 +1,10 @@ -use crate::xdg::{DesktopFile, Icon, IconType}; -use color_eyre::eyre::{eyre, Result}; +use crate::{ + application::Application, + xdg::{DesktopFile, Icon, IconType}, +}; +use color_eyre::eyre::Result; use glam::Quat; use mint::Vector3; -use nix::unistd::setsid; -use regex::Regex; use stardust_xr_fusion::{ client::{FrameInfo, RootHandler}, core::values::Transform, @@ -11,12 +12,9 @@ use stardust_xr_fusion::{ fields::BoxField, node::NodeType, spatial::Spatial, - startup_settings::StartupSettings, }; use stardust_xr_molecules::{GrabData, Grabbable}; use std::f32::consts::PI; -use std::os::unix::process::CommandExt; -use std::process::{Command, Stdio}; use tween::{QuartInOut, Tweener}; const MODEL_SCALE: f32 = 0.03; @@ -35,13 +33,10 @@ fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { t, &ResourceID::new_namespaced("protostar", "hexagon/hexagon"), )?; - model.set_material_parameter( - 1, - "color", - MaterialParameter::Color([0.0, 1.0, 1.0, 1.0]), - )?; - model.set_material_parameter( - 0, + model + .model_part("Hex")? + .set_material_parameter("color", MaterialParameter::Color([0.0, 1.0, 1.0, 1.0]))?; + model.model_part("Icon")?.set_material_parameter( "diffuse", MaterialParameter::Texture(ResourceID::Direct(icon.path.clone())), )?; @@ -57,16 +52,16 @@ fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { } pub struct ProtoStar { + application: Application, parent: Spatial, position: Vector3, grabbable: Grabbable, - field: BoxField, + _field: BoxField, icon: Model, label: Option, grabbable_shrink: Option>, grabbable_grow: Option>, grabbable_move: Option>, - execute_command: String, currently_shown: bool, } impl ProtoStar { @@ -74,45 +69,11 @@ impl ProtoStar { parent: &Spatial, position: impl Into>, desktop_file: DesktopFile, - ) -> Result { - // dbg!(&desktop_file); - let raw_icons = desktop_file.get_raw_icons(); - let mut icon = raw_icons - .clone() - .into_iter() - .find(|i| match i.icon_type { - IconType::Gltf => true, - _ => false, - }) - .or(raw_icons.into_iter().max_by_key(|i| i.size)); - - match icon { - Some(i) => { - icon = match i.cached_process(128) { - Ok(i) => Some(i), - _ => None, - } - } - None => {} - } - - Self::new_raw( - parent, - position, - desktop_file.name.as_deref(), - icon, - desktop_file.command.ok_or_else(|| eyre!("No command"))?, - ) - } - pub fn new_raw( - parent: &Spatial, - position: impl Into>, - name: Option<&str>, - icon: Option, - execute_command: String, ) -> Result { let position = position.into(); let field = BoxField::create(parent, Transform::default(), [MODEL_SCALE * 2.0; 3])?; + let application = Application::create(&parent.client()?, desktop_file)?; + let icon = application.icon(128, false); let grabbable = Grabbable::create( parent, Transform::from_position(position), @@ -147,7 +108,7 @@ impl ProtoStar { text_align: Alignment::Center.into(), ..Default::default() }; - let label = name.and_then(|name| { + let label = application.name().and_then(|name| { Text::create( &icon, Transform::from_position_rotation( @@ -163,14 +124,14 @@ impl ProtoStar { parent: parent.alias(), position, grabbable, - field, + _field: field, label, + application, icon, grabbable_shrink: None, grabbable_grow: None, - execute_command, - currently_shown: true, grabbable_move: None, + currently_shown: true, }) } pub fn content_parent(&self) -> &Spatial { @@ -261,20 +222,15 @@ impl RootHandler for ProtoStar { .unwrap(); self.grabbable_grow = None; } - } else if self.grabbable.grab_action().actor_stopped() { - let startup_settings = StartupSettings::create(&self.field.client().unwrap()).unwrap(); - startup_settings - .set_root(self.grabbable.content_parent()) - .unwrap(); + } else if self.grabbable.valid() && self.grabbable.grab_action().actor_stopped() { self.grabbable_shrink = Some(Tweener::quart_in_out(MODEL_SCALE, 0.0001, 0.25)); - let distance_future = self - .grabbable + let Ok(distance_future) = self.grabbable .content_parent() .get_position_rotation_scale(&self.parent) - .unwrap(); + else {return}; - let executable = self.execute_command.clone(); - let client = self.content_parent().client().unwrap(); + let application = self.application.clone(); + let space = self.content_parent().alias(); //TODO: split the executable string for the args tokio::task::spawn(async move { @@ -283,30 +239,7 @@ impl RootHandler for ProtoStar { + distance_vector.z.powi(2)) .sqrt(); if dbg!(distance) > ACTIVATION_DISTANCE { - let future = startup_settings.generate_startup_token().unwrap(); - - let env = client.get_connection_environment().unwrap().await.unwrap(); - for (k, v) in env.into_iter() { - std::env::set_var(k, v); - } - - std::env::set_var("STARDUST_STARTUP_TOKEN", future.await.unwrap()); - let re = Regex::new(r"%[fFuUdDnNickvm]").unwrap(); - let exec = re.replace_all(&executable, ""); - unsafe { - Command::new("sh") - .arg("-c") - .arg(exec.to_string()) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .pre_exec(|| { - _ = setsid(); - Ok(()) - }) - .spawn() - .expect("Failed to start child process"); - } + let _ = application.launch(&space); } }); } diff --git a/src/xdg.rs b/src/xdg.rs index 9390232..e024843 100644 --- a/src/xdg.rs +++ b/src/xdg.rs @@ -183,19 +183,19 @@ pub struct DesktopFile { pub no_display: bool, } impl DesktopFile { - pub fn get_raw_icons(&self) -> Vec { + pub fn get_raw_icons(&self, preferred_px_size: u16) -> 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) { + if let Some(icon) = Icon::from_path(test_icon_path, preferred_px_size) { return vec![icon]; } } 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) { + if let Some(icon) = Icon::from_path(cache_icon_path.unwrap(), preferred_px_size) { return vec![icon]; } } @@ -272,7 +272,7 @@ fn test_get_icon_path() { }; // Call the get_icon_path() function with a size argument and store the result - let icon_paths = desktop_file.get_raw_icons(); + let icon_paths = desktop_file.get_raw_icons(128); dbg!(&icon_paths); // Assert that the get_icon_path() function returns the expected result From 3fbeebd594183b37d4667acd093b33b3df541846 Mon Sep 17 00:00:00 2001 From: Nova Date: Thu, 11 May 2023 00:18:38 -0400 Subject: [PATCH 25/26] Use an hashmap instead of symlinks for cache --- Cargo.lock | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 3 ++ src/xdg.rs | 90 +++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 179 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f03af8c..a6758b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -174,6 +174,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.0" @@ -276,7 +282,8 @@ dependencies = [ "js-sys", "num-integer", "num-traits", - "time", + "serde", + "time 0.1.45", "wasm-bindgen", "winapi", ] @@ -944,6 +951,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "iana-time-zone" version = "0.1.56" @@ -1013,6 +1026,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", + "serde", ] [[package]] @@ -1047,6 +1061,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + [[package]] name = "jpeg-decoder" version = "0.3.0" @@ -1637,6 +1657,9 @@ dependencies = [ "regex", "resvg", "rustc-hash", + "serde", + "serde_json", + "serde_with", "stardust-xr-fusion", "stardust-xr-molecules", "tempdir", @@ -1932,6 +1955,12 @@ dependencies = [ "unicode-script", ] +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + [[package]] name = "same-file" version = "1.0.6" @@ -1979,6 +2008,17 @@ dependencies = [ "syn 2.0.15", ] +[[package]] +name = "serde_json" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_repr" version = "0.1.12" @@ -1990,6 +2030,34 @@ dependencies = [ "syn 2.0.15", ] +[[package]] +name = "serde_with" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85456ffac572dc8826334164f2fb6fb40a7c766aebe195a2a21ee69ee2885ecf" +dependencies = [ + "base64 0.13.1", + "chrono", + "hex", + "indexmap", + "serde", + "serde_json", + "serde_with_macros", + "time 0.3.20", +] + +[[package]] +name = "serde_with_macros" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cbcd6104f8a4ab6af7f6be2a0da6be86b9de3c401f6e86bb856ab2af739232f" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "sharded-slab" version = "0.1.4" @@ -2270,6 +2338,33 @@ dependencies = [ "winapi", ] +[[package]] +name = "time" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +dependencies = [ + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +dependencies = [ + "time-core", +] + [[package]] name = "tiny-skia" version = "0.8.4" @@ -2492,7 +2587,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b6bb4e62619d9f68aa2d8a823fea2bff302340a1f2d45c264d5b0be170832e" dependencies = [ - "base64", + "base64 0.21.0", "data-url", "flate2", "imagesize", diff --git a/Cargo.toml b/Cargo.toml index dfe4147..1b44b81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,9 @@ nix = "0.26.1" regex = "1.7.1" resvg = "0.29.0" rustc-hash = "1.1.0" +serde = "1.0.155" +serde_json = "1.0.94" +serde_with = "2.3.1" stardust-xr-fusion = "0.40.2" stardust-xr-molecules = "0.24.3" tokio = { version = "1.24.1", features = ["full"] } diff --git a/src/xdg.rs b/src/xdg.rs index e024843..df6c11a 100644 --- a/src/xdg.rs +++ b/src/xdg.rs @@ -1,18 +1,68 @@ -use cached::proc_macro::cached; use color_eyre::eyre::Result; +use lazy_static::lazy_static; use linicon; use regex::Regex; use resvg::render; use resvg::tiny_skia::{Pixmap, Transform}; use resvg::usvg::{FitTo, Tree}; +use serde::{Deserialize, Serialize}; +use serde_json; +use serde_with::serde_as; +use std::collections::HashMap; use std::ffi::OsString; use std::fs::create_dir_all; +use std::fs::File; use std::io::{BufRead, BufReader, ErrorKind}; -use std::os::unix::fs::symlink; +use std::io::{Read, Write}; 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(); @@ -193,10 +243,11 @@ impl DesktopFile { } } - 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(), preferred_px_size) { - 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]; + } } } @@ -247,10 +298,28 @@ impl Icon { } pub fn cached_process(self, size: u16) -> Result { - 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); + 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(); } match self.icon_type { IconType::Svg => Ok(Icon::from_path(get_png_from_svg(self.path, size)?, size).unwrap()), @@ -285,7 +354,6 @@ fn test_get_icon_path() { )); } -#[cached] pub fn get_image_cache_dir() -> PathBuf { let cache_dir; if let Ok(xdg_cache_home) = std::env::var("XDG_CACHE_HOME") { From 41e4ac1896b366b7907e8d799116791ecf5d5c1d Mon Sep 17 00:00:00 2001 From: nik012003 Date: Sun, 12 Mar 2023 02:26:16 +0100 Subject: [PATCH 26/26] Fixed crash while sorting desktop files --- examples/hexagon_launcher.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/hexagon_launcher.rs b/examples/hexagon_launcher.rs index 9b230c8..3a8336c 100644 --- a/examples/hexagon_launcher.rs +++ b/examples/hexagon_launcher.rs @@ -98,7 +98,7 @@ impl AppHexGrid { .filter(|d| !d.no_display) .collect(); - desktop_files.sort_by_key(|d| d.clone().name.unwrap()); + desktop_files.sort_by_key(|d| d.clone().name.unwrap_or_default()); let mut apps = Vec::new(); let mut radius = 1;