From c8be913156a5663b282f78aa160344f7c9a25a33 Mon Sep 17 00:00:00 2001 From: Bennyboiii Date: Thu, 9 Mar 2023 21:25:05 -0500 Subject: [PATCH] updated packages --- Cargo.toml | 29 +++++++++ src/protostar.rs | 162 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 Cargo.toml create mode 100644 src/protostar.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9b2ccf8 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "protostar" +version = "0.4.0" +edition = "2021" + +[dependencies] +clap = { version = "4.1.3", features = ["derive"] } +color-eyre = "0.6.2" +directories = "4.0.1" +dirs = "4.0.0" +ez-pixmap = "0.2.2" +glam = { version = "0.22.0", features = ["mint"] } +image = "0.24.5" +lazy_static = "1.4.0" +manifest-dir-macros = "0.1.16" +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" +tokio = { version = "1.24.1", features = ["full"] } +tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } +tween = "2.0.0" +ustr = "0.9.0" +walkdir = "2.3.2" + +[dev-dependencies] +tempdir = "0.3.7" \ No newline at end of file diff --git a/src/protostar.rs b/src/protostar.rs new file mode 100644 index 0000000..9d3b92c --- /dev/null +++ b/src/protostar.rs @@ -0,0 +1,162 @@ +use crate::xdg::{DesktopFile, Icon, RawIconType}; +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 stardust_xr_molecules::{GrabData, Grabbable}; +use std::{f32::consts::PI, ffi::CStr, sync::Arc}; +use tween::{QuartInOut, Tweener}; +use ustr::ustr; + +fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { + let model = match icon { + Icon::Png(path) => { + let model = Model::create( + parent, + Transform::from_rotation(Quat::from_rotation_y(PI)), + &ResourceID::new_namespaced("protostar", "cartridge"), + )?; + model.set_material_parameter( + 0, + "diffuse", + MaterialParameter::Texture(ResourceID::Direct(path.clone())), + )?; + model + } + Icon::Gltf(path) => Model::create( + parent, + Transform::from_scale([0.05; 3]), + &ResourceID::new_direct(path)?, + )?, + }; + Ok(model) +} + +pub struct ProtoStar { + client: Arc, + grabbable: Grabbable, + field: BoxField, + icon: Model, + icon_shrink: Option>, + execute_command: String, +} +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(); + let last_icon = raw_icons.pop(); + let icon = raw_icons + .into_iter() + .find(|i| match i { + RawIconType::Png(_) => false, + RawIconType::Svg(_) => false, + RawIconType::Gltf(_) => true, + }) + .or(last_icon) + .map(|i| dbg!(i.process(64)).ok()) + .ok_or_else(|| eyre!("No compatible icons found"))?; + Self::new_raw( + parent, + icon, + desktop_file.command.ok_or_else(|| eyre!("No command"))?, + ) + } + pub fn new_raw(parent: &Spatial, icon: Option, execute_command: String) -> Result { + let field = BoxField::create( + parent, + Transform::default(), + match icon.as_ref() { + Some(Icon::Png(_)) => [0.05, 0.0665, 0.005], + _ => [0.05; 3], + }, + )?; + let grabbable = Grabbable::new( + parent, + Transform::default(), + &field, + GrabData { + max_distance: 0.025, + ..Default::default() + }, + )?; + 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_scale([0.05; 3]), + &ResourceID::new_namespaced("protostar", "default_icon"), + )?) + })?; + Ok(ProtoStar { + client: parent.client()?, + grabbable, + field, + icon, + icon_shrink: None, + execute_command, + }) + } + pub fn content_parent(&self) -> &Spatial { + self.grabbable.content_parent() + } +} +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.client.stop_loop(); + } + } 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(1.0, 0.0, 0.25)); + let future = startup_settings.generate_startup_token().unwrap(); + let executable = dbg!(self.execute_command.clone()); + 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(); + } + }); + } + } +}