250 lines
6.3 KiB
Rust
250 lines
6.3 KiB
Rust
#[allow(dead_code)]
|
|
mod backend;
|
|
mod config;
|
|
mod config_io;
|
|
mod graphics;
|
|
mod gui;
|
|
mod hid;
|
|
mod overlays;
|
|
mod shaders;
|
|
mod state;
|
|
|
|
#[cfg(feature = "wayvr")]
|
|
mod config_wayvr;
|
|
|
|
use std::{
|
|
path::PathBuf,
|
|
sync::{
|
|
atomic::{AtomicBool, Ordering},
|
|
Arc,
|
|
},
|
|
};
|
|
|
|
use clap::Parser;
|
|
use sysinfo::Pid;
|
|
|
|
/// The lightweight desktop overlay for OpenVR and OpenXR
|
|
#[derive(Default, Parser, Debug)]
|
|
#[command(version, about, long_about = None)]
|
|
struct Args {
|
|
#[cfg(feature = "openvr")]
|
|
/// Force OpenVR backend
|
|
#[arg(long)]
|
|
openvr: bool,
|
|
|
|
#[cfg(feature = "openxr")]
|
|
/// Force OpenXR backend
|
|
#[arg(long)]
|
|
openxr: bool,
|
|
|
|
/// Show the working set of overlay on startup
|
|
#[arg(long)]
|
|
show: bool,
|
|
|
|
/// Uninstall OpenVR manifest and exit
|
|
#[arg(long)]
|
|
uninstall: bool,
|
|
|
|
/// Replace running WlxOverlay-S instance
|
|
#[arg(long)]
|
|
replace: bool,
|
|
|
|
/// Allow multiple running instances of WlxOverlay-S (things may break!)
|
|
#[arg(long)]
|
|
multi: bool,
|
|
|
|
/// Path to write logs to
|
|
#[arg(short, long, value_name = "FILE_PATH")]
|
|
log_to: Option<String>,
|
|
|
|
#[cfg(feature = "uidev")]
|
|
/// Show a desktop window of a UI panel for development
|
|
#[arg(short, long, value_name = "UI_NAME")]
|
|
uidev: Option<String>,
|
|
}
|
|
|
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
let mut args = if std::env::args().skip(1).any(|a| !a.is_empty()) {
|
|
Args::parse()
|
|
} else {
|
|
Args::default()
|
|
};
|
|
|
|
if !args.multi && !ensure_single_instance(args.replace) {
|
|
println!("Looks like WlxOverlay-S is already running.");
|
|
println!("Use --replace and I will terminate it for you.");
|
|
return Ok(());
|
|
}
|
|
|
|
logging_init(&mut args)?;
|
|
|
|
log::info!(
|
|
"Welcome to {} version {}!",
|
|
env!("CARGO_PKG_NAME"),
|
|
env!("WLX_BUILD"),
|
|
);
|
|
log::info!("It is {}.", chrono::Local::now().format("%c"));
|
|
|
|
#[cfg(feature = "openvr")]
|
|
if args.uninstall {
|
|
crate::backend::openvr::openvr_uninstall();
|
|
return Ok(());
|
|
}
|
|
|
|
#[cfg(feature = "uidev")]
|
|
if let Some(panel_name) = args.uidev.as_ref() {
|
|
crate::backend::uidev::uidev_run(panel_name.as_str())?;
|
|
return Ok(());
|
|
}
|
|
|
|
let running = Arc::new(AtomicBool::new(true));
|
|
let _ = ctrlc::set_handler({
|
|
let running = running.clone();
|
|
move || {
|
|
running.store(false, Ordering::Relaxed);
|
|
}
|
|
});
|
|
|
|
auto_run(running, args);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn auto_run(running: Arc<AtomicBool>, args: Args) {
|
|
use backend::common::BackendError;
|
|
|
|
#[cfg(feature = "openxr")]
|
|
if !args_get_openvr(&args) {
|
|
use crate::backend::openxr::openxr_run;
|
|
match openxr_run(running.clone(), args.show) {
|
|
Ok(()) => return,
|
|
Err(BackendError::NotSupported) => (),
|
|
Err(e) => {
|
|
log::error!("{}", e.to_string());
|
|
return;
|
|
}
|
|
};
|
|
}
|
|
|
|
#[cfg(feature = "openvr")]
|
|
if !args_get_openxr(&args) {
|
|
use crate::backend::openvr::openvr_run;
|
|
match openvr_run(running.clone(), args.show) {
|
|
Ok(()) => return,
|
|
Err(BackendError::NotSupported) => (),
|
|
Err(e) => {
|
|
log::error!("{}", e.to_string());
|
|
return;
|
|
}
|
|
};
|
|
}
|
|
|
|
log::error!("No more backends to try");
|
|
|
|
#[cfg(not(any(feature = "openvr", feature = "openxr")))]
|
|
compile_error!("No VR support! Enable either openvr or openxr features!");
|
|
|
|
#[cfg(not(any(feature = "wayland", feature = "x11")))]
|
|
compile_error!("No desktop support! Enable either wayland or x11 features!");
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
fn args_get_openvr(_args: &Args) -> bool {
|
|
#[cfg(feature = "openvr")]
|
|
let ret = _args.openvr;
|
|
|
|
#[cfg(not(feature = "openvr"))]
|
|
let ret = false;
|
|
|
|
ret
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
fn args_get_openxr(_args: &Args) -> bool {
|
|
#[cfg(feature = "openxr")]
|
|
let ret = _args.openxr;
|
|
|
|
#[cfg(not(feature = "openxr"))]
|
|
let ret = false;
|
|
|
|
ret
|
|
}
|
|
|
|
fn logging_init(args: &mut Args) -> anyhow::Result<()> {
|
|
let log_file = args
|
|
.log_to
|
|
.take()
|
|
.or_else(|| std::env::var("WLX_LOGFILE").ok())
|
|
.or_else(|| Some("/tmp/wlx.log".to_string()));
|
|
|
|
if let Some(log_to) = log_file.filter(|s| !s.is_empty()) {
|
|
if let Err(e) = file_logging_init(&log_to) {
|
|
log::error!("Failed to initialize file logging: {}", e);
|
|
env_logger::init();
|
|
}
|
|
} else {
|
|
env_logger::init();
|
|
}
|
|
|
|
log_panics::init();
|
|
Ok(())
|
|
}
|
|
|
|
fn file_logging_init(log_to: &str) -> anyhow::Result<()> {
|
|
use std::io::Write;
|
|
let target = Box::new(std::fs::File::create(log_to)?);
|
|
|
|
env_logger::Builder::new()
|
|
.target(env_logger::Target::Pipe(target))
|
|
.filter(None, log::LevelFilter::Info)
|
|
.parse_default_env()
|
|
.format(|buf, record| {
|
|
eprintln!("[{}] {}", record.level(), record.args());
|
|
writeln!(
|
|
buf,
|
|
"[{} {} {}] {}",
|
|
chrono::Local::now().format("%H:%M:%S%.3f"),
|
|
record.level(),
|
|
record.module_path().unwrap_or_default(),
|
|
record.args()
|
|
)
|
|
})
|
|
.init();
|
|
|
|
println!("Logging to: {}", log_to);
|
|
Ok(())
|
|
}
|
|
|
|
fn ensure_single_instance(replace: bool) -> bool {
|
|
let mut path = std::env::var("XDG_RUNTIME_DIR")
|
|
.map(PathBuf::from)
|
|
.unwrap_or_else(|_| PathBuf::from("/tmp"));
|
|
path.push("wlx-overlay-s.pid");
|
|
|
|
if path.exists() {
|
|
// load contents
|
|
if let Ok(pid_str) = std::fs::read_to_string(&path) {
|
|
if let Ok(pid) = pid_str.trim().parse::<u32>() {
|
|
let mut system = sysinfo::System::new();
|
|
system.refresh_processes(
|
|
sysinfo::ProcessesToUpdate::Some(&[Pid::from_u32(pid)]),
|
|
false,
|
|
);
|
|
if let Some(proc) = system.process(sysinfo::Pid::from_u32(pid)) {
|
|
if replace {
|
|
proc.kill_with(sysinfo::Signal::Term);
|
|
proc.wait();
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let pid = std::process::id().to_string();
|
|
std::fs::write(path, pid).unwrap();
|
|
|
|
true
|
|
}
|