use std::io;
use std::path::PathBuf;
const ICON_SVG: &[u8] = include_bytes!("../assets/com.mousehop.Mousehop.svg");
const DESKTOP_ENTRY: &str = include_str!("../com.mousehop.Mousehop.desktop");
const APP_ID: &str = "com.mousehop.Mousehop";
const BINARY_NAME: &str = "mousehop";
fn xdg_data_home() -> io::Result<PathBuf> {
if let Some(dir) = std::env::var_os("XDG_DATA_HOME").filter(|d| !d.is_empty()) {
return Ok(PathBuf::from(dir));
}
let home = std::env::var_os("HOME").ok_or_else(|| {
io::Error::new(io::ErrorKind::NotFound, "neither XDG_DATA_HOME nor HOME is set")
})?;
Ok(PathBuf::from(home).join(".local/share"))
}
fn xdg_state_home() -> io::Result<PathBuf> {
if let Some(dir) = std::env::var_os("XDG_STATE_HOME").filter(|d| !d.is_empty()) {
return Ok(PathBuf::from(dir));
}
let home = std::env::var_os("HOME").ok_or_else(|| {
io::Error::new(io::ErrorKind::NotFound, "neither XDG_STATE_HOME nor HOME is set")
})?;
Ok(PathBuf::from(home).join(".local/state"))
}
fn install() -> io::Result<()> {
let data = xdg_data_home()?;
let icon_dir = data.join("icons/hicolor/scalable/apps");
std::fs::create_dir_all(&icon_dir)?;
let icon_path = icon_dir.join(format!("{APP_ID}.svg"));
std::fs::write(&icon_path, ICON_SVG)?;
let app_dir = data.join("applications");
std::fs::create_dir_all(&app_dir)?;
let exe = std::env::current_exe()?.display().to_string();
let entry = DESKTOP_ENTRY
.replace(
&format!("Exec={BINARY_NAME}\n"),
&format!("Exec={exe}\n"),
);
let entry_path = app_dir.join(format!("{APP_ID}.desktop"));
std::fs::write(&entry_path, entry)?;
Ok(())
}
fn packaged_entry_exists() -> bool {
let dirs = std::env::var("XDG_DATA_DIRS")
.ok()
.filter(|s| !s.is_empty())
.unwrap_or_else(|| "/usr/local/share:/usr/share".to_string());
std::env::split_paths(&dirs).any(|d| d.join(format!("applications/{APP_ID}.desktop")).is_file())
}
pub fn ensure_first_launch() {
let _ = try_ensure_first_launch();
}
fn try_ensure_first_launch() -> io::Result<()> {
if std::env::var_os("FLATPAK_ID").is_some() {
return Ok(());
}
let state = xdg_state_home()?;
let marker_dir = state.join(env!("CARGO_PKG_NAME"));
let marker = marker_dir.join("desktop-install-done");
if marker.exists() {
return Ok(());
}
let user_entry = xdg_data_home()?.join(format!("applications/{APP_ID}.desktop"));
if !user_entry.exists() && !packaged_entry_exists() {
install()?;
}
std::fs::create_dir_all(&marker_dir)?;
std::fs::write(
&marker,
"Mousehop ran its one-time first-launch desktop integration.\n",
)?;
Ok(())
}