use anyhow::{Context, Result, anyhow};
use openvr::{ApplicationType, init};
use std::fs;
use std::path::PathBuf;
use tracing::info;
const APP_KEY: &str = "io.atomicflag.lighthouse-manager";
pub fn enable() -> Result<()> {
let context = unsafe { init(ApplicationType::Utility) }?;
let mut app = context.application()?;
let manifest_path = manifest_path()?;
write_manifest_if_missing(&manifest_path)?;
let already_installed = app
.is_application_installed(APP_KEY)
.map_err(|e| anyhow!("IsApplicationInstalled failed: {e:?}"))?;
if already_installed {
info!("App already installed with SteamVR.");
} else {
app.add_application_manifest(&manifest_path, false)
.map_err(|e| anyhow!("AddApplicationManifest failed: {e:?}"))?;
}
info!("Autostart enabled — lighthouse-manager-ovr will launch with SteamVR.");
Ok(())
}
pub fn run(action: &str) -> Result<()> {
match action {
"on" => enable(),
"off" => disable(),
other => Err(anyhow!("Invalid action '{other}'. Expected 'on' or 'off'.")),
}
}
pub fn disable() -> Result<()> {
let context = unsafe { init(ApplicationType::Utility) }?;
let mut app = context.application()?;
let manifest_path = manifest_path()?;
app.remove_application_manifest(&manifest_path)
.map_err(|e| anyhow!("RemoveApplicationManifest failed: {e:?}"))?;
info!("Autostart disabled — lighthouse-manager-ovr will no longer launch with SteamVR.");
Ok(())
}
fn manifest_path() -> Result<PathBuf> {
let exe = std::env::current_exe().context("Could not determine current executable path")?;
let parent = exe.parent().ok_or_else(|| {
anyhow::anyhow!("Could not determine parent directory of current executable")
})?;
Ok(parent.join("manifest.vrmanifest"))
}
fn write_manifest_if_missing(path: &PathBuf) -> Result<()> {
if path.exists() {
return Ok(()); }
let ovr_exe_path = ovr_binary_path()?;
let exe_name = ovr_exe_path
.file_name()
.and_then(|n| n.to_str())
.ok_or_else(|| anyhow!("Could not determine -ovr executable file name"))?;
let manifest = format!(
r#"{{
"source": "builtin",
"applications": [
{{
"app_key": "{APP_KEY}",
"launch_type": "binary",
"binary_path_windows": "{exe_name}",
"binary_path_linux": "{exe_name}",
"binary_path_osx": "{exe_name}",
"is_dashboard_overlay": true,
"strings": {{
"en_us": {{
"name": "Lighthouse Manager",
"description": "Tool that lets you discover, power on/off, and identify SteamVR Lighthouse base stations wirelessly via Bluetooth Low Energy."
}}
}}
}}
]
}}
"#
);
fs::write(path, manifest).context("Could not write manifest")?;
info!("Wrote manifest to: {}", path.display());
Ok(())
}
fn ovr_binary_path() -> Result<PathBuf> {
let cli_exe = std::env::current_exe().context("Could not determine current executable path")?;
let parent = cli_exe.parent().ok_or_else(|| {
anyhow::anyhow!("Could not determine parent directory of current executable")
})?;
let cli_stem = cli_exe
.file_stem()
.and_then(|n| n.to_str())
.ok_or_else(|| anyhow!("CLI exe has no file stem"))?;
let ovr_name = format!(
"{cli_stem}-ovr{}",
cli_exe
.extension()
.map(|e| format!(".{}", e.to_str().unwrap_or("")))
.unwrap_or_default()
);
Ok(parent.join(ovr_name))
}