Merge branch 'main' of https://github.com/Bennyboiii/protostar into Bennyboiii-main

This commit is contained in:
Nicola Guerrera
2023-05-14 14:50:18 +02:00
5 changed files with 257 additions and 438 deletions

255
Cargo.lock generated
View File

@@ -23,12 +23,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"
[[package]] [[package]]
name = "ahash" name = "ahash"
version = "0.7.6" version = "0.7.6"
@@ -40,15 +34,6 @@ dependencies = [
"version_check", "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]] [[package]]
name = "aliasable" name = "aliasable"
version = "0.1.3" version = "0.1.3"
@@ -87,23 +72,6 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
[[package]]
name = "async-trait"
version = "0.1.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b84f9ebcc6c1f5b8cb160f6990096a5c127f423fcb6e1ccc46c370cbdfb75dfc"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "async_once"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ce4f10ea3abcd6617873bae9f91d1c5332b4a778bd9ce34d0cd517474c1de82"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@@ -173,44 +141,6 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
[[package]]
name = "cached"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5877db5d1af7fae60d06b5db9430b68056a69b3582a0be8e3691e87654aeb6"
dependencies = [
"async-trait",
"async_once",
"cached_proc_macro",
"cached_proc_macro_types",
"futures",
"hashbrown 0.13.2",
"instant",
"lazy_static",
"once_cell",
"thiserror",
"tokio",
]
[[package]]
name = "cached_proc_macro"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e10ca87c81aaa3a949dbbe2b5e6c2c45dbc94ba4897e45ea31ff9ec5087be3dc"
dependencies = [
"cached_proc_macro_types",
"darling",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "cached_proc_macro_types"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.79" version = "1.0.79"
@@ -529,15 +459,6 @@ dependencies = [
"winapi", "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]] [[package]]
name = "either" name = "either"
version = "1.8.1" version = "1.8.1"
@@ -612,15 +533,6 @@ dependencies = [
"color-maps", "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]] [[package]]
name = "flagset" name = "flagset"
version = "0.4.3" version = "0.4.3"
@@ -706,83 +618,24 @@ dependencies = [
"ttf-parser", "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]] [[package]]
name = "fuchsia-cprng" name = "fuchsia-cprng"
version = "0.1.1" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "futures"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]] [[package]]
name = "futures-core" name = "futures-core"
version = "0.3.27" version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd"
[[package]]
name = "futures-io"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91"
[[package]] [[package]]
name = "futures-sink" name = "futures-sink"
version = "0.3.27" version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2"
[[package]]
name = "futures-task"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879"
[[package]]
name = "futures-util"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab"
dependencies = [
"futures-core",
"futures-sink",
"futures-task",
"pin-project-lite",
"pin-utils",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.8" version = "0.2.8"
@@ -855,27 +708,12 @@ dependencies = [
"crunchy", "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]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.12.3" version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.4.1" version = "0.4.1"
@@ -971,7 +809,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"hashbrown 0.12.3", "hashbrown",
"serde", "serde",
] ]
@@ -1066,29 +904,6 @@ version = "0.2.140"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
[[package]]
name = "linicon"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ee8c5653188a809616c97296180a0547a61dba205bcdcbdd261dbd022a25fd9"
dependencies = [
"file-locker",
"freedesktop_entry_parser",
"linicon-theme",
"memmap2",
"thiserror",
]
[[package]]
name = "linicon-theme"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4f8240c33bb08c5d8b8cdea87b683b05e61037aa76ff26bef40672cc6ecbb80"
dependencies = [
"freedesktop_entry_parser",
"rust-ini",
]
[[package]] [[package]]
name = "link-cplusplus" name = "link-cplusplus"
version = "1.0.8" version = "1.0.8"
@@ -1159,15 +974,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "memoffset"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "memoffset" name = "memoffset"
version = "0.7.1" version = "0.7.1"
@@ -1186,12 +992,6 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.6.2" version = "0.6.2"
@@ -1240,20 +1040,6 @@ dependencies = [
"getrandom", "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]] [[package]]
name = "nix" name = "nix"
version = "0.26.2" version = "0.26.2"
@@ -1268,16 +1054,6 @@ dependencies = [
"static_assertions", "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]] [[package]]
name = "nu-ansi-term" name = "nu-ansi-term"
version = "0.46.0" version = "0.46.0"
@@ -1364,16 +1140,6 @@ version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "ordered-multimap"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c672c7ad9ec066e428c00eb917124a06f08db19e2584de982cc34b1f4c12485"
dependencies = [
"dlv-list",
"hashbrown 0.9.1",
]
[[package]] [[package]]
name = "os_str_bytes" name = "os_str_bytes"
version = "6.4.1" version = "6.4.1"
@@ -1566,7 +1332,6 @@ dependencies = [
name = "protostar" name = "protostar"
version = "0.4.0" version = "0.4.0"
dependencies = [ dependencies = [
"cached",
"clap", "clap",
"color-eyre", "color-eyre",
"directories", "directories",
@@ -1575,11 +1340,9 @@ dependencies = [
"glam 0.22.0", "glam 0.22.0",
"image", "image",
"lazy_static", "lazy_static",
"linicon",
"manifest-dir-macros", "manifest-dir-macros",
"mint", "mint",
"nix 0.26.2", "nix",
"regex",
"resvg", "resvg",
"rustc-hash", "rustc-hash",
"serde", "serde",
@@ -1725,8 +1488,6 @@ version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
dependencies = [ dependencies = [
"aho-corasick",
"memchr",
"regex-syntax", "regex-syntax",
] ]
@@ -1804,16 +1565,6 @@ dependencies = [
"xmlparser", "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]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.21" version = "0.1.21"
@@ -2485,7 +2236,7 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "371436099f2980de56dc385b615696d3eabbdac9649a72b85f9d75f68474fa9c" checksum = "371436099f2980de56dc385b615696d3eabbdac9649a72b85f9d75f68474fa9c"
dependencies = [ dependencies = [
"ahash 0.7.6", "ahash",
"byteorder", "byteorder",
"lazy_static", "lazy_static",
"parking_lot 0.11.2", "parking_lot 0.11.2",

View File

@@ -10,3 +10,6 @@ TODO:
3. ~~Use XDG_CACHE_DIR to rasterize svgs (done)~~ 3. ~~Use XDG_CACHE_DIR to rasterize svgs (done)~~
4. Make sure it's using the current icon theme 4. Make sure it's using the current icon theme
5. Design a better app launcher ui 5. Design a better app launcher ui
TO RUN DOCK
cargo run --example sirius

165
examples/sirius.rs Normal file
View File

@@ -0,0 +1,165 @@
#![allow(dead_code)]
use color_eyre::eyre::Result;
use glam::Vec3;
use manifest_dir_macros::directory_relative_path;
use protostar::protostar::ProtoStar;
use stardust_xr_fusion::{
client::{Client, FrameInfo, RootHandler},
core::values::Transform,
drawable::{MaterialParameter, Model, ResourceID},
node::NodeError, input::{InputData, InputDataType}, spatial::Spatial, fields::BoxField,
};
use stardust_xr_molecules::{touch_plane::TouchPlane, Grabbable, GrabData};
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
color_eyre::install()?;
let (client, event_loop) = Client::connect_with_async_loop().await?;
client.set_base_prefixes(&[directory_relative_path!("res")]);
let _wrapped_root = client.wrap_root(Sirius::new(&client)?)?;
tokio::select! {
_ = tokio::signal::ctrl_c() => (),
e = event_loop => e??,
}
Ok(())
}
struct Star {
cli: ProtoStar
}
impl Star {
fn new(parent: &Spatial, _name: Option<&str>,path: String) -> Option<Self> {
let cli = ProtoStar::new_raw(parent, None, path).unwrap();
Some(Star {
cli,
})
}
}
impl RootHandler for Star {
fn frame(&mut self, info: FrameInfo) {
self.cli.frame(info);
}
}
const LOCATION_OF_TELESCOPE: &str = "/home/bc/repos/stardust/";
const EXECUTABLES: [&str; 3] = [
"magnetar",
"atmosphere",
"manifold",
];
struct Sirius {
touch_plane: TouchPlane,
model: Model,
root: Spatial,
clients: Vec<Star>,
visibility: bool,
grabbable: Grabbable,
}
impl Sirius {
fn new(client: &Client) -> Result<Self, NodeError> {
let mut client_list: Vec<(Option<&str>, String)> = Vec::new();
for executable in EXECUTABLES {
client_list.push((Some(executable), format!("{}telescope/repos/{}/target/release/{}", LOCATION_OF_TELESCOPE, executable, executable)));
}
let root = Spatial::create(client.get_root(), Transform::default(), false).unwrap();
let field = BoxField::create(&root, Transform::default(), Vec3::from([0.1;3])).unwrap();
let grabbable = Grabbable::new(&root, Transform::default(), &field, GrabData::default())?;
let touch_plane = TouchPlane::new(grabbable.content_parent(), Transform::default(), [0.1; 2], 0.03)?;
let mut clients = Vec::new();
for clientkv in client_list {
clients.push(Star::new(grabbable.content_parent(), clientkv.0, clientkv.1).unwrap());
}
let model = Model::create(
grabbable.content_parent(),
Transform::default(),
&ResourceID::new_namespaced("protostar", "button"),
)?;
field.set_spatial_parent(grabbable.content_parent())?;
let visibility = false;
Ok(Sirius { touch_plane, model , root, clients, visibility, grabbable})
}
// fn left_hand(input_data: &InputData, _: &()) -> bool {
// match &input_data.input {
// InputDataType::Hand(h) => !h.right,
// _ => false,
// }
// }
}
impl RootHandler for Sirius {
fn frame(&mut self, info: FrameInfo) {
for app in &mut self.clients {
app.frame(info);
}
self.grabbable.update(&info);
self.touch_plane.update();
if self.touch_plane.touch_started() {
println!("Touch started");
self.visibility = !self.visibility;
match self.visibility {
true => for star in self.clients.iter().enumerate() {
let mut starpos = (star.0 as f32 +1.0)/10.0;
match starpos % 0.2 == 0.0 {
true => starpos = -starpos/2.0,
false => starpos = (starpos - 0.1)/2.0,
}
println!("{}", starpos);
star.1.cli.content_parent().set_position(Some(&self.grabbable.content_parent()), Vec3::from([starpos,0.1,0.0])).ok();
},
false => for star in &self.clients {
star.cli.content_parent().set_position(Some(&self.grabbable.content_parent()), Vec3::from([0.0,0.0,0.0])).ok();
},
}
let color = [0.0, 1.0, 0.0, 1.0];
self.model
.set_material_parameter(0, "color", MaterialParameter::Color(color))
.unwrap();
self.model
.set_material_parameter(
0,
"emission_factor",
MaterialParameter::Color(color.map(|c| c * 0.75)),
)
.unwrap();
}
if self.touch_plane.touch_stopped() {
println!("Touch ended");
let color = [1.0, 0.0, 0.0, 1.0];
self.model
.set_material_parameter(0, "color", MaterialParameter::Color(color))
.unwrap();
self.model
.set_material_parameter(
0,
"emission_factor",
MaterialParameter::Color(color.map(|c| c * 0.5)),
)
.unwrap();
}
}
}
fn position(data: &InputData) -> Vec3 {
match &data.input {
InputDataType::Hand(h) => h.palm.position.into(),
InputDataType::Pointer(w) => w.deepest_point.into(),
InputDataType::Tip(t) => t.origin.into(),
}
}

BIN
res/protostar/button.glb Normal file

Binary file not shown.

View File

@@ -1,5 +1,4 @@
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use lazy_static::lazy_static;
use linicon; use linicon;
use regex::Regex; use regex::Regex;
use resvg::render; use resvg::render;
@@ -13,90 +12,33 @@ use std::ffi::OsString;
use std::fs::create_dir_all; use std::fs::create_dir_all;
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader, ErrorKind}; use std::io::{BufRead, BufReader, ErrorKind};
use std::io::{Read, Write}; use std::os::unix::fs::symlink;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::FromStr; use std::str::FromStr;
use std::sync::Mutex; use std::sync::Mutex;
use std::{env, fs}; use std::{env, fs};
use walkdir::WalkDir; use walkdir::WalkDir;
#[serde_as]
#[derive(Deserialize, Serialize)]
struct ImageCache {
path: PathBuf,
#[serde_as(as = "Vec<(_, _)>")]
pub map: HashMap<String, PathBuf>,
}
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<ImageCache> = Mutex::new(ImageCache::new(
get_image_cache_dir().join("imagechache.map")
));
}
fn get_data_dirs() -> Vec<PathBuf> { fn get_data_dirs() -> Vec<PathBuf> {
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 // Append the applications directory to each data directory
let app_dirs = xdg_data_dirs
.split(":") .split(":")
.filter_map(|dir| PathBuf::from_str(dir).ok()); .map(|dir| Path::new(dir).join("applications"));
let data_home = dirs::home_dir() // Get the user's local applications directory
.unwrap_or(PathBuf::from_str("/usr/share/").expect( let local_app_dir = dirs::home_dir()
"No XDG_DATA_DIR set, no HOME directory found and no /usr/share direcotry found", .unwrap()
))
.join(".local") .join(".local")
.join("share"); .join("share")
.join("applications");
xdg_data_dirs
.chain([data_home].into_iter())
.filter(|dir| dir.exists() && dir.is_dir())
.collect()
}
fn get_app_dirs() -> Vec<PathBuf> {
get_data_dirs()
.into_iter()
.map(|dir| dir.join("applications"))
.filter(|dir| dir.exists() && dir.is_dir())
.collect()
}
pub fn get_desktop_files() -> Vec<PathBuf> {
let desktop_extension = OsString::from_str("desktop").unwrap(); let desktop_extension = OsString::from_str("desktop").unwrap();
// Get the list of directories to search // Get the list of directories to search
let app_dirs = get_app_dirs();
app_dirs app_dirs
.into_iter() .chain(Some(local_app_dir))
.filter(|dir| dir.exists() && dir.is_dir())
.flat_map(|dir| { .flat_map(|dir| {
// Follow symlinks and recursively search directories // Follow symlinks and recursively search directories
WalkDir::new(dir) WalkDir::new(dir)
@@ -137,10 +79,6 @@ pub fn parse_desktop_file(path: PathBuf) -> Result<DesktopFile, String> {
let mut command = None; let mut command = None;
let mut categories = Vec::new(); let mut categories = Vec::new();
let mut icon = None; 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 // Loop through each line of the file
for line in reader.lines() { for line in reader.lines() {
@@ -154,14 +92,6 @@ pub fn parse_desktop_file(path: PathBuf) -> Result<DesktopFile, String> {
continue; 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 // Split the line into a key-value pair by looking for the first "=" character
let parts = line.split_once('='); let parts = line.split_once('=');
let (key, value) = match parts { let (key, value) = match parts {
@@ -181,12 +111,6 @@ pub fn parse_desktop_file(path: PathBuf) -> Result<DesktopFile, String> {
.collect() .collect()
} }
"Icon" => icon = Some(value.to_string()), "Icon" => icon = Some(value.to_string()),
"NoDisplay" => {
no_display = match value {
"true" => true,
_ => false,
}
}
_ => (), // Ignore unknown keys _ => (), // Ignore unknown keys
} }
} }
@@ -198,7 +122,6 @@ pub fn parse_desktop_file(path: PathBuf) -> Result<DesktopFile, String> {
command, command,
categories, categories,
icon, icon,
no_display,
}) })
} }
@@ -230,96 +153,77 @@ pub struct DesktopFile {
pub command: Option<String>, pub command: Option<String>,
pub categories: Vec<String>, pub categories: Vec<String>,
pub icon: Option<String>, pub icon: Option<String>,
pub no_display: bool,
} }
impl DesktopFile { impl DesktopFile {
pub fn get_raw_icons(&self) -> Vec<Icon> { pub fn get_raw_icons(&self) -> Vec<RawIconType> {
// Get the name of the icon from the DesktopFile struct // Get the name of the icon from the DesktopFile struct
let Some(icon_name) = self.icon.as_ref() else { return Vec::new(); }; let Some(icon_name) = self.icon.as_ref() else { return Vec::new(); };
let test_icon_path = self.path.join(Path::new(icon_name)); let test_icon_path = self.path.join(Path::new(icon_name));
if test_icon_path.exists() { if test_icon_path.exists() {
if let Some(icon) = Icon::from_path(test_icon_path, 128) { return RawIconType::from_path(test_icon_path)
.map(|i| vec![i])
.unwrap_or_default();
}
let cache_icon_path = get_image_cache_dir().join(icon_name).canonicalize();
if cache_icon_path.is_ok() {
if let Some(icon) = Icon::from_path(cache_icon_path.unwrap(), 128) {
return vec![icon]; return vec![icon];
} }
} }
if let Some(cache_icon_path) = IMAGE_CACHE.lock().unwrap().map.get(icon_name) { // 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
if cache_icon_path.exists() { let icon_theme = env::var_os("XDG_ICON_THEME").unwrap_or("hicolor".into());
if let Some(icon) = Icon::from_path(cache_icon_path.to_owned(), 128) {
return vec![icon];
}
}
}
let mut icons_iter = linicon::lookup_icon(icon_name) // Get the XDG_DATA_HOME and XDG_DATA_DIRS environment variables, and split the XDG_DATA_DIRS variable into a list of directories
.use_fallback_themes(false) let Some(xdg_data_dirs) = env::var_os("XDG_DATA_DIRS") else { return Vec::new(); };
.peekable(); let Ok(binding) = xdg_data_dirs.into_string() else { return Vec::new(); };
let xdg_data_dirs = binding.split(":").map(Path::new);
if icons_iter.peek().is_none() { // Concatenate the XDG_DATA_HOME and XDG_DATA_DIRS directories with the default path for icon themes
//dbg!("No icons found in current theme"); xdg_data_dirs // XDG_DATA_DIRS directories
icons_iter = linicon::lookup_icon(icon_name).peekable(); .flat_map(|dir| {
} let icons_path = dir.join("icons").join(&icon_theme);
ICON_SIZES
let sized_png: Vec<Icon> = icons_iter .iter()
.filter_map(|i| i.ok()) .map(|path| icons_path.join(path).join("apps"))
.filter(|i| i.icon_type != linicon::IconType::XMP) //TODO: support XMP .collect::<Vec<_>>()
.map(|i| Icon::from_path(i.path, i.max_size - 2).unwrap()) })
.collect(); .filter_map(|dir| {
sized_png let dir = fs::read_dir(dir).ok()?;
Some(
dir.filter_map(|e| e.ok())
.map(|file| file.path())
.filter(|file| file.file_stem() == Some(&icon_name)),
)
})
.flatten()
.filter_map(RawIconType::from_path)
.collect()
} }
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq)]
pub struct Icon { pub enum RawIconType {
pub icon_type: IconType, Png(PathBuf),
pub path: PathBuf, Svg(PathBuf),
pub size: u16, Gltf(PathBuf),
} }
impl RawIconType {
#[derive(Debug, PartialEq, Eq, Clone)] pub fn from_path(path: PathBuf) -> Option<RawIconType> {
pub enum IconType { match path.extension().and_then(|ext| ext.to_str()) {
Png, Some("png") => Some(RawIconType::Png(path)),
Svg, Some("svg") => Some(RawIconType::Svg(path)),
Gltf, Some("glb") | Some("gltf") => Some(RawIconType::Gltf(path)),
} _ => None,
impl Icon { }
pub fn from_path(path: PathBuf, size: u16) -> Option<Icon> {
let icon_type = match path.extension().and_then(|ext| ext.to_str()) {
Some("png") => IconType::Png,
Some("svg") => IconType::Svg,
Some("glb") | Some("gltf") => IconType::Gltf,
_ => return None,
};
return Some(Icon {
icon_type,
path,
size,
});
} }
pub fn cached_process(self, size: u16) -> Result<Icon, std::io::Error> { pub fn cached_process(self, size: u16) -> Result<Icon, std::io::Error> {
if !IMAGE_CACHE.lock().unwrap().map.contains_key( let new_path =
&self get_image_cache_dir().join(self.path.with_extension("").file_name().unwrap());
.path if !new_path.exists() {
.with_extension("") _ = symlink(self.path.clone(), new_path);
.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 { 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()),
@@ -337,7 +241,6 @@ fn test_get_icon_path() {
command: None, command: None,
categories: vec![], categories: vec![],
icon: Some("krita".into()), icon: Some("krita".into()),
no_display: false,
}; };
// Call the get_icon_path() function with a size argument and store the result // Call the get_icon_path() function with a size argument and store the result
@@ -345,15 +248,12 @@ fn test_get_icon_path() {
dbg!(&icon_paths); dbg!(&icon_paths);
// Assert that the get_icon_path() function returns the expected result // Assert that the get_icon_path() function returns the expected result
assert!(icon_paths.contains( assert!(icon_paths.contains(&RawIconType::Png(PathBuf::from(
&Icon::from_path( "/usr/share/icons/hicolor/16x16/apps/krita.png"
PathBuf::from("/usr/share/icons/hicolor/32x32/apps/krita.png"), ))));
32
)
.unwrap()
));
} }
#[cached]
pub fn get_image_cache_dir() -> PathBuf { pub fn get_image_cache_dir() -> PathBuf {
let cache_dir; let cache_dir;
if let Ok(xdg_cache_home) = std::env::var("XDG_CACHE_HOME") { if let Ok(xdg_cache_home) = std::env::var("XDG_CACHE_HOME") {
@@ -367,26 +267,26 @@ pub fn get_image_cache_dir() -> PathBuf {
return image_cache_dir; return image_cache_dir;
} }
pub fn get_png_from_svg(svg_path: impl AsRef<Path>, size: u16) -> Result<PathBuf, std::io::Error> { pub fn render_svg_to_png(
cache_dir: impl AsRef<Path>,
svg_path: impl AsRef<Path>,
size: u32,
) -> Result<PathBuf, std::io::Error> {
let svg_path = fs::canonicalize(svg_path)?; let svg_path = fs::canonicalize(svg_path)?;
let svg_data = fs::read(svg_path.as_path())?; let tree = Tree::from_data(
let tree = Tree::from_data(svg_data.as_slice(), &resvg::usvg::Options::default()) fs::read(svg_path.as_path())?.as_slice(),
.map_err(|_| ErrorKind::InvalidData)?; &resvg::usvg::Options::default(),
)
let png_path = get_image_cache_dir().join(format!( .map_err(|_| ErrorKind::InvalidData)?;
"{}-{}.png", create_dir_all(cache_dir.as_ref())?;
svg_path.file_name().unwrap().to_str().unwrap(), let png_path = cache_dir
svg_data.len() .as_ref()
)); .join(svg_path.file_name().unwrap())
.with_extension("png");
if png_path.exists() { let mut pixmap = Pixmap::new(size, size).unwrap();
return Ok(png_path);
}
let mut pixmap = Pixmap::new(size.into(), size.into()).unwrap();
render( render(
&tree, &tree,
FitTo::Width(size.into()), FitTo::Width(size),
Transform::identity(), Transform::identity(),
pixmap.as_mut(), pixmap.as_mut(),
); );
@@ -410,7 +310,7 @@ fn test_render_svg_to_png() {
fs::write(&svg_path, test_svg_data).unwrap(); fs::write(&svg_path, test_svg_data).unwrap();
// Call the function with the test input and output paths and a size of 200 // Call the function with the test input and output paths and a size of 200
let png_path = get_png_from_svg(&svg_path, 200).unwrap(); let png_path = render_svg_to_png(".", &svg_path, 200).unwrap();
dbg!(&png_path); dbg!(&png_path);
// Check that the output file exists // Check that the output file exists