use crate::xdg::{DesktopFile, Icon, IconType}; use color_eyre::eyre::{eyre, Result}; use glam::Quat; use mint::Vector3; use nix::unistd::setsid; 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 std::os::unix::process::CommandExt; use std::process::{Command, Stdio}; use std::{f32::consts::PI, sync::Arc}; use tween::{QuartInOut, Tweener}; 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 model = Model::create( parent, 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, "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 ProtoStar { client: Arc, grabbable: Grabbable, field: BoxField, icon: Model, icon_shrink: Option>, icon_grow: Option>, execute_command: String, } impl ProtoStar { pub fn create_from_desktop_file(parent: &Spatial, 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, 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(_) => [0.05, 0.0665, 0.005], _ => [0.05; 3], } .into(), )?; let grabbable = Grabbable::new( parent, Transform::default(), &field, GrabData { max_distance: 0.01 }, )?; 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_xyzw(0.0, 0.707, 0.707, 0.0), [0.03, 0.03, 0.03], ), &ResourceID::new_namespaced("protostar", "hexagon/hexagon"), )?) })?; Ok(ProtoStar { client: parent.client()?, grabbable, field, icon, icon_shrink: None, icon_grow: 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(); 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(); } 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 .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 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()); 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"); } } }