use anyhow::{Context, Result}; use owo_colors::OwoColorize; use std::path::PathBuf; const DESKTOP_NAME: &str = "umutray.desktop"; fn home() -> Result { dirs::home_dir().context("Cannot determine home directory") } fn autostart_path() -> Result { Ok(home()?.join(".config/autostart").join(DESKTOP_NAME)) } fn desktop_path() -> Result { Ok(home()?.join(".local/share/applications").join(DESKTOP_NAME)) } fn render_desktop(exe: &std::path::Path, autostart: bool) -> String { let mut s = format!( "[Desktop Entry]\n\ Name=umutray\n\ Comment=Wine launcher manager for Windows game launchers\n\ Exec={exe}\n\ Icon=applications-games\n\ Type=Application\n\ Categories=Game;\n\ Keywords=wine;proton;gaming;launcher;\n\ StartupNotify=false\n", exe = exe.display(), ); if autostart { s.push_str("X-GNOME-Autostart-enabled=true\n"); s.push_str("Hidden=false\n"); } else { // App-menu entry launches the GUI s = s.replace( &format!("Exec={}", exe.display()), &format!("Exec={} gui", exe.display()), ); s.push_str("StartupNotify=true\n"); } s } /// Install only the .desktop file so umutray appears in the app menu. /// Called automatically on first `umutray gui` run. pub fn install_desktop() -> Result<()> { let exe = std::env::current_exe().context("Cannot determine path to own executable")?; let desktop = desktop_path()?; if let Some(p) = desktop.parent() { std::fs::create_dir_all(p).with_context(|| format!("Failed to create {p:?}"))?; } std::fs::write(&desktop, render_desktop(&exe, false)) .with_context(|| format!("Failed to write desktop file {desktop:?}"))?; println!("{} App menu entry written: {}", "✓".green().bold(), desktop.display()); Ok(()) } /// Remove the .desktop file. pub fn uninstall_desktop() -> Result<()> { let desktop = desktop_path()?; if desktop.exists() { std::fs::remove_file(&desktop) .with_context(|| format!("Failed to remove {desktop:?}"))?; println!("Removed {}", desktop.display()); } else { println!("No desktop file at {}", desktop.display()); } Ok(()) } /// Write an XDG autostart entry and the app-menu .desktop file. pub fn install() -> Result<()> { let exe = std::env::current_exe().context("Cannot determine path to own executable")?; // XDG autostart let autostart = autostart_path()?; if let Some(p) = autostart.parent() { std::fs::create_dir_all(p).with_context(|| format!("Failed to create {p:?}"))?; } std::fs::write(&autostart, render_desktop(&exe, true)) .with_context(|| format!("Failed to write autostart file {autostart:?}"))?; println!("Wrote autostart: {}", autostart.display()); // App-menu entry install_desktop()?; println!(); println!("{} Autostart installed.", "✓".green().bold()); println!(" umutray will start with your next graphical session."); println!(" To start now: {}", exe.display()); Ok(()) } /// Remove the XDG autostart entry and the app-menu .desktop file. pub fn uninstall() -> Result<()> { let autostart = autostart_path()?; if autostart.exists() { std::fs::remove_file(&autostart) .with_context(|| format!("Failed to remove {autostart:?}"))?; println!("Removed {}", autostart.display()); } else { println!("No autostart file at {}", autostart.display()); } uninstall_desktop()?; println!("{} Autostart removed.", "✓".green().bold()); Ok(()) } /// Show whether the XDG autostart entry is present. pub fn status() -> Result<()> { let autostart = autostart_path()?; if autostart.exists() { println!("{} Autostart enabled: {}", "✓".green().bold(), autostart.display()); } else { println!("{} Autostart not installed ({})", "✗".red().bold(), autostart.display()); } Ok(()) }