fix for new version of protostar
This commit is contained in:
@@ -34,7 +34,7 @@ const HEX_DIRECTION_VECTORS: [Hex; 6] = [
|
|||||||
Hex { q: 1, r: -1, s: 0 },
|
Hex { q: 1, r: -1, s: 0 },
|
||||||
Hex { q: 0, r: -1, s: 1 },
|
Hex { q: 0, r: -1, s: 1 },
|
||||||
Hex { q: -1, r: 0, s: 1 },
|
Hex { q: -1, r: 0, s: 1 },
|
||||||
Hex { q: -1, r: 1, s: 0 },
|
Hex{ q: -1, r: 1, s: 0 },
|
||||||
Hex { q: 0, r: 1, s: -1 },
|
Hex { q: 0, r: 1, s: -1 },
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ impl App {
|
|||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
let position = position.into();
|
let position = position.into();
|
||||||
let protostar =
|
let protostar =
|
||||||
ProtoStar::create_from_desktop_file(parent, position, desktop_file.clone()).ok()?;
|
ProtoStar::create_from_desktop_file(parent, desktop_file.clone()).ok()?;
|
||||||
Some(App {
|
Some(App {
|
||||||
_desktop_file: desktop_file,
|
_desktop_file: desktop_file,
|
||||||
protostar,
|
protostar,
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ struct Star {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Star {
|
impl Star {
|
||||||
fn new(parent: &Spatial, name: Option<&str>,path: &str) -> Option<Self> {
|
fn new(parent: &Spatial, _name: Option<&str>,path: &str) -> Option<Self> {
|
||||||
let cli = ProtoStar::new_raw(parent, Vec3::default(), name, None, path.to_string()).unwrap();
|
let cli = ProtoStar::new_raw(parent, None, path.to_string()).unwrap();
|
||||||
Some(Star {
|
Some(Star {
|
||||||
cli,
|
cli,
|
||||||
})
|
})
|
||||||
@@ -60,9 +60,9 @@ struct Sirius {
|
|||||||
impl Sirius {
|
impl Sirius {
|
||||||
fn new(client: &Client) -> Result<Self, NodeError> {
|
fn new(client: &Client) -> Result<Self, NodeError> {
|
||||||
let client_list: Vec<(Option<&str>, &str)> = Vec::from([
|
let client_list: Vec<(Option<&str>, &str)> = Vec::from([
|
||||||
(Some("Magnetar"), "$HOME/repos/stardust/telescope/repos/magnetar/target/release/magnetar"),
|
(Some("Magnetar"), "/home/bc/repos/stardust/telescope/repos/magnetar/target/release/magnetar"),
|
||||||
(Some("Atmosphere"), "$HOME/repos/stardust/telescope/repos/atmosphere/target/release/atmosphere"),
|
(Some("Atmosphere"), "/home/bc/repos/stardust/telescope/repos/atmosphere/target/release/atmosphere"),
|
||||||
(Some("Manifold"), "$HOME/repos/stardust/telescope/repos/manifold/target/release/manifold"),
|
(Some("Manifold"), "/home/bc/repos/stardust/telescope/repos/manifold/target/release/manifold"),
|
||||||
]);
|
]);
|
||||||
let root = Spatial::create(client.get_root(), Transform::default(), false).unwrap();
|
let root = Spatial::create(client.get_root(), Transform::default(), false).unwrap();
|
||||||
|
|
||||||
|
|||||||
245
src/xdg.rs
245
src/xdg.rs
@@ -1,52 +1,40 @@
|
|||||||
use cached::proc_macro::cached;
|
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use linicon;
|
|
||||||
use regex::Regex;
|
|
||||||
use resvg::render;
|
use resvg::render;
|
||||||
use resvg::tiny_skia::{Pixmap, Transform};
|
use resvg::tiny_skia::{Pixmap, Transform};
|
||||||
use resvg::usvg::{FitTo, Tree};
|
use resvg::usvg::{FitTo, Tree};
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::fs::create_dir_all;
|
use std::fs::create_dir_all;
|
||||||
use std::io::{BufRead, BufReader, ErrorKind};
|
use std::io::{BufRead, BufReader, ErrorKind};
|
||||||
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::{env, fs};
|
use std::{env, fs};
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
fn get_data_dirs() -> Vec<PathBuf> {
|
|
||||||
let xdg_data_dirs_str = std::env::var("XDG_DATA_DIRS").unwrap_or_default();
|
|
||||||
|
|
||||||
let xdg_data_dirs = xdg_data_dirs_str
|
const ICON_SIZES: &[&str] = &["128x128", "scalable", "256x256", "64x64", "32x32"];
|
||||||
.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<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> {
|
pub fn get_desktop_files() -> Vec<PathBuf> {
|
||||||
|
// 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();
|
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)
|
||||||
@@ -87,10 +75,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() {
|
||||||
@@ -104,14 +88,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 {
|
||||||
@@ -131,12 +107,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,7 +118,6 @@ pub fn parse_desktop_file(path: PathBuf) -> Result<DesktopFile, String> {
|
|||||||
command,
|
command,
|
||||||
categories,
|
categories,
|
||||||
icon,
|
icon,
|
||||||
no_display,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,81 +149,76 @@ 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)
|
||||||
return vec![icon];
|
.map(|i| vec![i])
|
||||||
}
|
.unwrap_or_default();
|
||||||
}
|
}
|
||||||
|
|
||||||
let cache_icon_path = get_image_cache_dir().join(icon_name).canonicalize();
|
let Ok(icon_name) = OsString::from_str(icon_name) else { return Vec::new(); };
|
||||||
if cache_icon_path.is_ok() {
|
|
||||||
if let Some(icon) = Icon::from_path(cache_icon_path.unwrap(), 128) {
|
|
||||||
return vec![icon];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut icons_iter = linicon::lookup_icon(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
|
||||||
.use_fallback_themes(false)
|
let icon_theme = env::var_os("XDG_ICON_THEME").unwrap_or("hicolor".into());
|
||||||
.peekable();
|
|
||||||
|
|
||||||
if icons_iter.peek().is_none() {
|
// Get the XDG_DATA_HOME and XDG_DATA_DIRS environment variables, and split the XDG_DATA_DIRS variable into a list of directories
|
||||||
//dbg!("No icons found in current theme");
|
let Some(xdg_data_dirs) = env::var_os("XDG_DATA_DIRS") else { return Vec::new(); };
|
||||||
icons_iter = linicon::lookup_icon(icon_name).peekable();
|
let Ok(binding) = xdg_data_dirs.into_string() else { return Vec::new(); };
|
||||||
}
|
let xdg_data_dirs = binding.split(":").map(Path::new);
|
||||||
|
|
||||||
let sized_png: Vec<Icon> = icons_iter
|
// Concatenate the XDG_DATA_HOME and XDG_DATA_DIRS directories with the default path for icon themes
|
||||||
.filter_map(|i| i.ok())
|
xdg_data_dirs // XDG_DATA_DIRS directories
|
||||||
.filter(|i| i.icon_type != linicon::IconType::XMP) //TODO: support XMP
|
.flat_map(|dir| {
|
||||||
.map(|i| Icon::from_path(i.path, i.max_size - 2).unwrap())
|
let icons_path = dir.join("icons").join(&icon_theme);
|
||||||
.collect();
|
ICON_SIZES
|
||||||
sized_png
|
.iter()
|
||||||
|
.map(|path| icons_path.join(path).join("apps"))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.filter_map(|dir| {
|
||||||
|
let dir = fs::read_dir(dir).ok()?;
|
||||||
|
Some(
|
||||||
|
dir.filter_map(|e| e.ok())
|
||||||
|
.map(|file| file.path())
|
||||||
|
.filter(|file| file.file_stem() == Some(&icon_name)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.filter_map(RawIconType::from_path)
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[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 process(self, size: u32) -> Result<Icon, std::io::Error> {
|
||||||
let new_path =
|
match self {
|
||||||
get_image_cache_dir().join(self.path.with_extension("").file_name().unwrap());
|
RawIconType::Png(path) => Ok(Icon::Png(path)),
|
||||||
if !new_path.exists() {
|
RawIconType::Svg(path) => {
|
||||||
_ = symlink(self.path.clone(), new_path);
|
let png_path = path.with_extension("png");
|
||||||
}
|
render_svg_to_png(path, &png_path, size)?;
|
||||||
match self.icon_type {
|
Ok(Icon::Png(png_path))
|
||||||
IconType::Svg => Ok(Icon::from_path(get_png_from_svg(self.path, size)?, size).unwrap()),
|
}
|
||||||
_ => Ok(self),
|
RawIconType::Gltf(path) => Ok(Icon::Gltf(path)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -268,7 +232,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
|
||||||
@@ -276,49 +239,37 @@ 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]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub fn get_image_cache_dir() -> PathBuf {
|
pub enum Icon {
|
||||||
let cache_dir;
|
Png(PathBuf),
|
||||||
if let Ok(xdg_cache_home) = std::env::var("XDG_CACHE_HOME") {
|
Gltf(PathBuf),
|
||||||
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<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(),
|
||||||
);
|
);
|
||||||
@@ -342,7 +293,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
|
||||||
|
|||||||
Reference in New Issue
Block a user