new file: Makefile

new file:   TODO.md
	modified:   src/config.rs
	modified:   src/detect.rs
	modified:   src/diagnose.rs
	new file:   src/gui.rs
	modified:   src/main.rs
	modified:   src/service.rs
	modified:   src/setup.rs
	modified:   src/tray.rs
	new file:   src/util.rs
	new file:   umutray.desktop
This commit is contained in:
funman300
2026-04-17 23:12:47 -07:00
parent 4c918e673b
commit f2f584febf
12 changed files with 1471 additions and 113 deletions
+77 -22
View File
@@ -3,18 +3,28 @@ use std::path::PathBuf;
use std::process::Command;
const UNIT_NAME: &str = "umutray.service";
const DESKTOP_NAME: &str = "umutray.desktop";
fn home() -> Result<PathBuf> {
Ok(PathBuf::from(
std::env::var("HOME").context("$HOME is not set")?,
))
}
fn unit_path() -> Result<PathBuf> {
let home = std::env::var("HOME").context("$HOME is not set")?;
Ok(PathBuf::from(home)
.join(".config/systemd/user")
.join(UNIT_NAME))
Ok(home()?.join(".config/systemd/user").join(UNIT_NAME))
}
fn desktop_path() -> Result<PathBuf> {
Ok(home()?
.join(".local/share/applications")
.join(DESKTOP_NAME))
}
fn render_unit(exe: &std::path::Path) -> String {
format!(
"[Unit]\n\
Description=Battle.net tray manager\n\
Description=umutray Wine launcher manager\n\
After=graphical-session.target\n\
PartOf=graphical-session.target\n\
\n\
@@ -29,6 +39,21 @@ fn render_unit(exe: &std::path::Path) -> String {
)
}
fn render_desktop(exe: &std::path::Path) -> String {
format!(
"[Desktop Entry]\n\
Name=umutray\n\
Comment=Wine launcher manager for Windows game launchers\n\
Exec={exe} gui\n\
Icon=applications-games\n\
Type=Application\n\
Categories=Game;\n\
Keywords=wine;proton;gaming;launcher;\n\
StartupNotify=true\n",
exe = exe.display(),
)
}
fn systemctl(args: &[&str]) -> Result<()> {
let status = Command::new("systemctl")
.arg("--user")
@@ -41,20 +66,49 @@ fn systemctl(args: &[&str]) -> Result<()> {
Ok(())
}
/// Write the unit, reload systemd, and enable+start the service.
/// 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))
.with_context(|| format!("Failed to write desktop file {desktop:?}"))?;
println!("\x1b[1;32m✓\x1b[0m App menu entry written: {}", 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 the unit + desktop file, reload systemd, and enable+start the service.
pub fn install() -> Result<()> {
let exe = std::env::current_exe().context("Cannot determine path to own executable")?;
let path = unit_path()?;
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent).with_context(|| format!("Failed to create {parent:?}"))?;
// systemd unit
let unit = unit_path()?;
if let Some(p) = unit.parent() {
std::fs::create_dir_all(p).with_context(|| format!("Failed to create {p:?}"))?;
}
std::fs::write(&unit, render_unit(&exe))
.with_context(|| format!("Failed to write unit file {unit:?}"))?;
println!("Wrote unit: {}", unit.display());
let contents = render_unit(&exe);
std::fs::write(&path, &contents)
.with_context(|| format!("Failed to write unit file {path:?}"))?;
println!("Wrote unit: {}", path.display());
println!("ExecStart: {}", exe.display());
// .desktop file
install_desktop()?;
println!("Exec: {} gui", exe.display());
println!();
systemctl(&["daemon-reload"])?;
@@ -62,25 +116,26 @@ pub fn install() -> Result<()> {
println!();
println!("\x1b[1;32m✓\x1b[0m Service installed and started.");
println!(" umutray autostarts with your session and is in the app menu.");
println!(" Status: systemctl --user status {UNIT_NAME}");
println!(" Logs: journalctl --user -u {UNIT_NAME} -f");
Ok(())
}
/// Stop, disable, and remove the unit file.
/// Stop, disable, and remove the unit + desktop files.
pub fn uninstall() -> Result<()> {
let path = unit_path()?;
// Ignore failures: the unit may already be stopped or unknown to systemd.
let _ = systemctl(&["disable", "--now", UNIT_NAME]);
if path.exists() {
std::fs::remove_file(&path).with_context(|| format!("Failed to remove {path:?}"))?;
println!("Removed {}", path.display());
let unit = unit_path()?;
if unit.exists() {
std::fs::remove_file(&unit).with_context(|| format!("Failed to remove {unit:?}"))?;
println!("Removed {}", unit.display());
} else {
println!("No unit file at {}", path.display());
println!("No unit file at {}", unit.display());
}
uninstall_desktop()?;
let _ = systemctl(&["daemon-reload"]);
println!("\x1b[1;32m✓\x1b[0m Service removed.");
Ok(())