Files
protostar/src/application.rs
2023-05-11 00:17:13 -04:00

101 lines
2.4 KiB
Rust

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<StartupSettings>,
}
impl Application {
pub fn create(client: &Arc<Client>, desktop_file: DesktopFile) -> Result<Self, NodeError> {
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<Icon> {
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(())
}
}