feat!: refactor around pathbuf

This commit is contained in:
Gabriele Musco 2024-07-21 09:41:07 +02:00
commit b01eaf69f6
41 changed files with 461 additions and 412 deletions

7
Cargo.lock generated
View file

@ -289,6 +289,7 @@ dependencies = [
"tracker", "tracker",
"uuid", "uuid",
"vte4", "vte4",
"xdg",
] ]
[[package]] [[package]]
@ -2528,3 +2529,9 @@ dependencies = [
"cfg-if", "cfg-if",
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "xdg"
version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546"

View file

@ -40,3 +40,4 @@ serde_json = "1.0.106"
tracker = "0.2.1" tracker = "0.2.1"
uuid = { version = "1.8.0", features = ["v4", "fast-rng"] } uuid = { version = "1.8.0", features = ["v4", "fast-rng"] }
vte4 = { version = "0.7.1", features = ["v0_72"] } vte4 = { version = "0.7.1", features = ["v0_72"] }
xdg = "2.5.2"

View file

@ -1,12 +1,12 @@
use crate::cmd_runner::CmdRunner; use std::path::PathBuf;
use std::path::Path;
pub fn get_adb_install_runner(apk_path: &String) -> CmdRunner { use crate::cmd_runner::CmdRunner;
let path = Path::new(apk_path);
pub fn get_adb_install_runner(path: &PathBuf) -> CmdRunner {
path.try_exists().expect("APK file provided does not exist"); path.try_exists().expect("APK file provided does not exist");
CmdRunner::new( CmdRunner::new(
None, None,
"adb".into(), "adb".into(),
vec!["install".into(), path.to_str().unwrap().to_string()], vec!["install".into(), path.to_string_lossy().to_string()],
) )
} }

View file

@ -1,10 +1,10 @@
use crate::ui::job_worker::job::WorkerJob; use crate::ui::job_worker::job::WorkerJob;
use std::collections::HashMap; use std::{collections::HashMap, path::PathBuf};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Cmake { pub struct Cmake {
pub build_dir: String, pub build_dir: PathBuf,
pub source_dir: String, pub source_dir: PathBuf,
pub vars: Option<HashMap<String, String>>, pub vars: Option<HashMap<String, String>>,
pub env: Option<HashMap<String, String>>, pub env: Option<HashMap<String, String>>,
} }
@ -13,7 +13,7 @@ impl Cmake {
pub fn get_prepare_job(&self) -> WorkerJob { pub fn get_prepare_job(&self) -> WorkerJob {
let mut args = vec![ let mut args = vec![
"-B".into(), "-B".into(),
self.build_dir.clone(), self.build_dir.to_string_lossy().to_string(),
"-G".into(), "-G".into(),
"Ninja".into(), "Ninja".into(),
]; ];
@ -29,7 +29,7 @@ impl Cmake {
} }
} }
} }
args.push(self.source_dir.clone()); args.push(self.source_dir.to_string_lossy().to_string());
WorkerJob::new_cmd(self.env.clone(), "cmake".into(), Some(args)) WorkerJob::new_cmd(self.env.clone(), "cmake".into(), Some(args))
} }
@ -37,7 +37,10 @@ impl Cmake {
WorkerJob::new_cmd( WorkerJob::new_cmd(
self.env.clone(), self.env.clone(),
"cmake".into(), "cmake".into(),
Some(vec!["--build".into(), self.build_dir.clone()]), Some(vec![
"--build".into(),
self.build_dir.to_string_lossy().to_string(),
]),
) )
} }
@ -45,7 +48,10 @@ impl Cmake {
WorkerJob::new_cmd( WorkerJob::new_cmd(
self.env.clone(), self.env.clone(),
"cmake".into(), "cmake".into(),
Some(vec!["--install".into(), self.build_dir.clone()]), Some(vec![
"--install".into(),
self.build_dir.to_string_lossy().to_string(),
]),
) )
} }
} }

View file

@ -1,17 +1,17 @@
use crate::ui::job_worker::job::{FuncWorkerOut, WorkerJob}; use crate::ui::job_worker::job::{FuncWorkerOut, WorkerJob};
use git2::Repository; use git2::Repository;
use std::path::Path; use std::path::PathBuf;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Git { pub struct Git {
pub repo: String, pub repo: String,
pub dir: String, pub dir: PathBuf,
pub branch: String, pub branch: String,
} }
impl Git { impl Git {
fn cmd(&self, args: Vec<String>) -> WorkerJob { fn cmd(&self, args: Vec<String>) -> WorkerJob {
let mut nargs = vec!["-C".into(), self.dir.clone()]; let mut nargs = vec!["-C".into(), self.dir.to_string_lossy().to_string()];
nargs.extend(args); nargs.extend(args);
WorkerJob::new_cmd(None, "git".into(), Some(nargs)) WorkerJob::new_cmd(None, "git".into(), Some(nargs))
} }
@ -81,7 +81,7 @@ impl Git {
Some(vec![ Some(vec![
"clone".into(), "clone".into(),
self.get_repo(), self.get_repo(),
self.dir.clone(), self.dir.to_string_lossy().to_string(),
"--recurse-submodules".into(), "--recurse-submodules".into(),
]), ]),
) )
@ -101,8 +101,7 @@ impl Git {
} }
pub fn clone_exists(&self) -> bool { pub fn clone_exists(&self) -> bool {
let path_s = format!("{}/.git", self.dir.clone()); self.dir.join(".git").is_dir()
Path::new(&path_s).is_dir()
} }
pub fn get_pre_build_jobs(&self, pull_on_build: bool) -> Vec<WorkerJob> { pub fn get_pre_build_jobs(&self, pull_on_build: bool) -> Vec<WorkerJob> {

View file

@ -4,10 +4,7 @@ use crate::{
profile::Profile, profile::Profile,
ui::job_worker::job::WorkerJob, ui::job_worker::job::WorkerJob,
}; };
use std::{ use std::collections::{HashMap, VecDeque};
collections::{HashMap, VecDeque},
path::Path,
};
pub fn get_build_basalt_jobs(profile: &Profile, clean_build: bool) -> VecDeque<WorkerJob> { pub fn get_build_basalt_jobs(profile: &Profile, clean_build: bool) -> VecDeque<WorkerJob> {
let mut jobs = VecDeque::<WorkerJob>::new(); let mut jobs = VecDeque::<WorkerJob>::new();
@ -32,16 +29,19 @@ pub fn get_build_basalt_jobs(profile: &Profile, clean_build: bool) -> VecDeque<W
jobs.extend(git.get_pre_build_jobs(profile.pull_on_build)); jobs.extend(git.get_pre_build_jobs(profile.pull_on_build));
let build_dir = format!("{}/build", profile.features.basalt.path.as_ref().unwrap()); let build_dir = profile.features.basalt.path.as_ref().unwrap().join("build");
let mut cmake_vars: HashMap<String, String> = HashMap::new(); let mut cmake_vars: HashMap<String, String> = HashMap::new();
cmake_vars.insert("CMAKE_EXPORT_COMPILE_COMMANDS".into(), "ON".into()); cmake_vars.insert("CMAKE_EXPORT_COMPILE_COMMANDS".into(), "ON".into());
cmake_vars.insert("CMAKE_BUILD_TYPE".into(), "RelWithDebInfo".into()); cmake_vars.insert("CMAKE_BUILD_TYPE".into(), "RelWithDebInfo".into());
cmake_vars.insert("CMAKE_INSTALL_PREFIX".into(), profile.prefix.clone()); cmake_vars.insert(
"CMAKE_INSTALL_PREFIX".into(),
profile.prefix.to_string_lossy().to_string(),
);
cmake_vars.insert("BUILD_TESTS".into(), "OFF".into()); cmake_vars.insert("BUILD_TESTS".into(), "OFF".into());
cmake_vars.insert("BASALT_INSTANTIATIONS_DOUBLE".into(), "OFF".into()); cmake_vars.insert("BASALT_INSTANTIATIONS_DOUBLE".into(), "OFF".into());
cmake_vars.insert( cmake_vars.insert(
"CMAKE_INSTALL_LIBDIR".into(), "CMAKE_INSTALL_LIBDIR".into(),
format!("{}/lib", profile.prefix), profile.prefix.join("lib").to_string_lossy().to_string(),
); );
let mut cmake_env: HashMap<String, String> = HashMap::new(); let mut cmake_env: HashMap<String, String> = HashMap::new();
@ -56,7 +56,7 @@ pub fn get_build_basalt_jobs(profile: &Profile, clean_build: bool) -> VecDeque<W
build_dir: build_dir.clone(), build_dir: build_dir.clone(),
}; };
if !Path::new(&build_dir).is_dir() || clean_build { if !build_dir.is_dir() || clean_build {
rm_rf(&build_dir); rm_rf(&build_dir);
jobs.push_back(cmake.get_prepare_job()); jobs.push_back(cmake.get_prepare_job());
} }
@ -68,10 +68,11 @@ pub fn get_build_basalt_jobs(profile: &Profile, clean_build: bool) -> VecDeque<W
"mkdir".into(), "mkdir".into(),
Some(vec![ Some(vec![
"-p".into(), "-p".into(),
format!( profile
"{}/share/basalt/thirdparty/basalt-headers/thirdparty", .prefix
profile.prefix .join("share/basalt/thirdparty/basalt-headers/thirdparty")
), .to_string_lossy()
.to_string(),
]), ]),
)); ));
jobs.push_back(WorkerJob::new_cmd( jobs.push_back(WorkerJob::new_cmd(
@ -79,11 +80,20 @@ pub fn get_build_basalt_jobs(profile: &Profile, clean_build: bool) -> VecDeque<W
"cp".into(), "cp".into(),
Some(vec![ Some(vec![
"-Ra".into(), "-Ra".into(),
format!( profile
"{}/thirdparty/basalt-headers/thirdparty/eigen", .features
profile.features.basalt.path.as_ref().unwrap().clone() .basalt
), .path
format!("{}/share/basalt/thirdparty", profile.prefix), .as_ref()
.unwrap()
.join("thirdparty/basalt-headers/thirdparty/eigen")
.to_string_lossy()
.to_string(),
profile
.prefix
.join("share/basalt/thirdparty")
.to_string_lossy()
.to_string(),
]), ]),
)); ));

View file

@ -32,20 +32,26 @@ pub fn get_build_libsurvive_jobs(profile: &Profile, clean_build: bool) -> VecDeq
jobs.extend(git.get_pre_build_jobs(profile.pull_on_build)); jobs.extend(git.get_pre_build_jobs(profile.pull_on_build));
let build_dir = format!( let build_dir = profile
"{}/build", .features
profile.features.libsurvive.path.as_ref().unwrap() .libsurvive
); .path
.as_ref()
.unwrap()
.join("build");
let mut cmake_vars: HashMap<String, String> = HashMap::new(); let mut cmake_vars: HashMap<String, String> = HashMap::new();
cmake_vars.insert("CMAKE_EXPORT_COMPILE_COMMANDS".into(), "ON".into()); cmake_vars.insert("CMAKE_EXPORT_COMPILE_COMMANDS".into(), "ON".into());
cmake_vars.insert("CMAKE_BUILD_TYPE".into(), "RelWithDebInfo".into()); cmake_vars.insert("CMAKE_BUILD_TYPE".into(), "RelWithDebInfo".into());
cmake_vars.insert("ENABLE_api_example".into(), "OFF".into()); cmake_vars.insert("ENABLE_api_example".into(), "OFF".into());
cmake_vars.insert("USE_HIDAPI".into(), "ON".into()); cmake_vars.insert("USE_HIDAPI".into(), "ON".into());
cmake_vars.insert("CMAKE_SKIP_INSTALL_RPATH".into(), "YES".into()); cmake_vars.insert("CMAKE_SKIP_INSTALL_RPATH".into(), "YES".into());
cmake_vars.insert("CMAKE_INSTALL_PREFIX".into(), profile.prefix.clone()); cmake_vars.insert(
"CMAKE_INSTALL_PREFIX".into(),
profile.prefix.to_string_lossy().to_string(),
);
cmake_vars.insert( cmake_vars.insert(
"CMAKE_INSTALL_LIBDIR".into(), "CMAKE_INSTALL_LIBDIR".into(),
format!("{}/lib", profile.prefix), profile.prefix.join("lib").to_string_lossy().to_string(),
); );
let cmake = Cmake { let cmake = Cmake {

View file

@ -5,10 +5,13 @@ use crate::{
pub fn get_build_mercury_job(profile: &Profile) -> WorkerJob { pub fn get_build_mercury_job(profile: &Profile) -> WorkerJob {
WorkerJob::new_cmd( WorkerJob::new_cmd(
None, None,
format!( pkg_data_dir()
"{sysdata}/scripts/build_mercury.sh", .join("scripts/build_mercury.sh")
sysdata = pkg_data_dir() .to_string_lossy()
), .to_string(),
Some(vec![profile.prefix.clone(), get_cache_dir()]), Some(vec![
profile.prefix.to_string_lossy().to_string(),
get_cache_dir().to_string_lossy().to_string(),
]),
) )
} }

View file

@ -28,25 +28,41 @@ pub fn get_build_monado_jobs(profile: &Profile, clean_build: bool) -> VecDeque<W
jobs.extend(git.get_pre_build_jobs(profile.pull_on_build)); jobs.extend(git.get_pre_build_jobs(profile.pull_on_build));
let build_dir = format!("{}/build", profile.xrservice_path); let build_dir = profile.xrservice_path.join("build");
let mut env: HashMap<String, String> = HashMap::new(); let mut env: HashMap<String, String> = HashMap::new();
env.insert( env.insert(
"PKG_CONFIG_PATH".into(), "PKG_CONFIG_PATH".into(),
format!("{}/lib/pkgconfig", profile.prefix), profile
.prefix
.join("lib/pkgconfig")
.to_string_lossy()
.to_string(),
); );
let mut cmake_vars: HashMap<String, String> = HashMap::new(); let mut cmake_vars: HashMap<String, String> = HashMap::new();
cmake_vars.insert("CMAKE_EXPORT_COMPILE_COMMANDS".into(), "ON".into()); cmake_vars.insert("CMAKE_EXPORT_COMPILE_COMMANDS".into(), "ON".into());
cmake_vars.insert("CMAKE_BUILD_TYPE".into(), "RelWithDebInfo".into()); cmake_vars.insert("CMAKE_BUILD_TYPE".into(), "RelWithDebInfo".into());
cmake_vars.insert("XRT_HAVE_SYSTEM_CJSON".into(), "NO".into()); cmake_vars.insert("XRT_HAVE_SYSTEM_CJSON".into(), "NO".into());
cmake_vars.insert("CMAKE_LIBDIR".into(), format!("{}/lib", profile.prefix)); cmake_vars.insert(
cmake_vars.insert("CMAKE_INSTALL_PREFIX".into(), profile.prefix.clone()); "CMAKE_LIBDIR".into(),
profile.prefix.join("lib").to_string_lossy().to_string(),
);
cmake_vars.insert(
"CMAKE_INSTALL_PREFIX".into(),
profile.prefix.to_string_lossy().to_string(),
);
cmake_vars.insert( cmake_vars.insert(
"CMAKE_C_FLAGS".into(), "CMAKE_C_FLAGS".into(),
format!("-Wl,-rpath='{}/lib'", profile.prefix,), format!(
"-Wl,-rpath='{}/lib'",
profile.prefix.to_string_lossy().to_string(),
),
); );
cmake_vars.insert( cmake_vars.insert(
"CMAKE_CXX_FLAGS".into(), "CMAKE_CXX_FLAGS".into(),
format!("-Wl,-rpath='{}/lib'", profile.prefix,), format!(
"-Wl,-rpath='{}/lib'",
profile.prefix.to_string_lossy().to_string(),
),
); );
profile.xrservice_cmake_flags.iter().for_each(|(k, v)| { profile.xrservice_cmake_flags.iter().for_each(|(k, v)| {
if k == "CMAKE_C_FLAGS" || k == "CMAKE_CXX_FLAGS" { if k == "CMAKE_C_FLAGS" || k == "CMAKE_CXX_FLAGS" {

View file

@ -28,7 +28,7 @@ pub fn get_build_opencomposite_jobs(profile: &Profile, clean_build: bool) -> Vec
jobs.extend(git.get_pre_build_jobs(profile.pull_on_build)); jobs.extend(git.get_pre_build_jobs(profile.pull_on_build));
let build_dir = format!("{}/build", profile.opencomposite_path); let build_dir = profile.opencomposite_path.join("build");
let mut cmake_vars: HashMap<String, String> = HashMap::new(); let mut cmake_vars: HashMap<String, String> = HashMap::new();
cmake_vars.insert("CMAKE_EXPORT_COMPILE_COMMANDS".into(), "ON".into()); cmake_vars.insert("CMAKE_EXPORT_COMPILE_COMMANDS".into(), "ON".into());
cmake_vars.insert("CMAKE_BUILD_TYPE".into(), "RelWithDebInfo".into()); cmake_vars.insert("CMAKE_BUILD_TYPE".into(), "RelWithDebInfo".into());

View file

@ -32,14 +32,23 @@ pub fn get_build_openhmd_jobs(profile: &Profile, clean_build: bool) -> VecDeque<
jobs.extend(git.get_pre_build_jobs(profile.pull_on_build)); jobs.extend(git.get_pre_build_jobs(profile.pull_on_build));
let build_dir = format!("{}/build", profile.features.openhmd.path.as_ref().unwrap()); let build_dir = profile
.features
.openhmd
.path
.as_ref()
.unwrap()
.join("build");
let mut cmake_vars: HashMap<String, String> = HashMap::new(); let mut cmake_vars: HashMap<String, String> = HashMap::new();
cmake_vars.insert("CMAKE_EXPORT_COMPILE_COMMANDS".into(), "ON".into()); cmake_vars.insert("CMAKE_EXPORT_COMPILE_COMMANDS".into(), "ON".into());
cmake_vars.insert("CMAKE_BUILD_TYPE".into(), "RelWithDebInfo".into()); cmake_vars.insert("CMAKE_BUILD_TYPE".into(), "RelWithDebInfo".into());
cmake_vars.insert("CMAKE_INSTALL_PREFIX".into(), profile.prefix.clone()); cmake_vars.insert(
"CMAKE_INSTALL_PREFIX".into(),
profile.prefix.to_string_lossy().to_string(),
);
cmake_vars.insert( cmake_vars.insert(
"CMAKE_INSTALL_LIBDIR".into(), "CMAKE_INSTALL_LIBDIR".into(),
format!("{}/lib", profile.prefix), profile.prefix.join("lib").to_string_lossy().to_string(),
); );
cmake_vars.insert("OPENHMD_DRIVER_OCULUS_RIFT_S".into(), "OFF".into()); cmake_vars.insert("OPENHMD_DRIVER_OCULUS_RIFT_S".into(), "OFF".into());

View file

@ -28,13 +28,16 @@ pub fn get_build_wivrn_jobs(profile: &Profile, clean_build: bool) -> VecDeque<Wo
jobs.extend(git.get_pre_build_jobs(profile.pull_on_build)); jobs.extend(git.get_pre_build_jobs(profile.pull_on_build));
let build_dir = format!("{}/build", profile.xrservice_path); let build_dir = profile.xrservice_path.join("build");
let mut cmake_vars: HashMap<String, String> = HashMap::new(); let mut cmake_vars: HashMap<String, String> = HashMap::new();
cmake_vars.insert("CMAKE_EXPORT_COMPILE_COMMANDS".into(), "ON".into()); cmake_vars.insert("CMAKE_EXPORT_COMPILE_COMMANDS".into(), "ON".into());
cmake_vars.insert("CMAKE_BUILD_TYPE".into(), "RelWithDebInfo".into()); cmake_vars.insert("CMAKE_BUILD_TYPE".into(), "RelWithDebInfo".into());
cmake_vars.insert("XRT_HAVE_SYSTEM_CJSON".into(), "NO".into()); cmake_vars.insert("XRT_HAVE_SYSTEM_CJSON".into(), "NO".into());
cmake_vars.insert("WIVRN_BUILD_CLIENT".into(), "OFF".into()); cmake_vars.insert("WIVRN_BUILD_CLIENT".into(), "OFF".into());
cmake_vars.insert("CMAKE_INSTALL_PREFIX".into(), profile.prefix.clone()); cmake_vars.insert(
"CMAKE_INSTALL_PREFIX".into(),
profile.prefix.to_string_lossy().to_string(),
);
profile.xrservice_cmake_flags.iter().for_each(|(k, v)| { profile.xrservice_cmake_flags.iter().for_each(|(k, v)| {
cmake_vars.insert(k.clone(), v.clone()); cmake_vars.insert(k.clone(), v.clone());

View file

@ -14,6 +14,7 @@ use crate::{
use std::{ use std::{
collections::HashMap, collections::HashMap,
io::{BufRead, BufReader, Write}, io::{BufRead, BufReader, Write},
path::PathBuf,
process::{Child, Command, Stdio}, process::{Child, Command, Stdio},
sync::{ sync::{
mpsc::{sync_channel, Receiver, SyncSender}, mpsc::{sync_channel, Receiver, SyncSender},
@ -99,9 +100,11 @@ impl CmdRunner {
Self::new( Self::new(
Some(env), Some(env),
match profile.xrservice_type { match profile.xrservice_type {
XRServiceType::Monado => format!("{pfx}/bin/monado-service", pfx = profile.prefix), XRServiceType::Monado => profile.prefix.join("bin/monado-service"),
XRServiceType::Wivrn => format!("{pfx}/bin/wivrn-server", pfx = profile.prefix), XRServiceType::Wivrn => profile.prefix.join("bin/wivrn-server"),
}, }
.to_string_lossy()
.to_string(),
vec![], vec![],
) )
} }
@ -171,13 +174,13 @@ impl CmdRunner {
} }
} }
fn save_log(path_s: String, log: &[String]) -> Result<(), std::io::Error> { fn save_log(path: &PathBuf, log: &[String]) -> Result<(), std::io::Error> {
let mut writer = get_writer(&path_s).map_err(std::io::Error::other)?; let mut writer = get_writer(path).map_err(std::io::Error::other)?;
let log_s = log.concat(); let log_s = log.concat();
writer.write_all(log_s.as_ref()) writer.write_all(log_s.as_ref())
} }
pub fn save_output(&mut self, path: String) -> Result<(), std::io::Error> { pub fn save_output(&mut self, path: &PathBuf) -> Result<(), std::io::Error> {
CmdRunner::save_log(path, &self.output) CmdRunner::save_log(path, &self.output)
} }
} }
@ -251,14 +254,14 @@ mod tests {
} }
runner runner
.save_output("./target/testout/testlog".into()) .save_output(&"./target/testout/testlog".into())
.expect("Failed to save output file"); .expect("Failed to save output file");
} }
#[test] #[test]
fn can_create_from_profile() { fn can_create_from_profile() {
CmdRunner::xrservice_runner_from_profile(&Profile::load_profile( CmdRunner::xrservice_runner_from_profile(&Profile::load_profile(
&"./test/files/profile.json".to_string(), &"./test/files/profile.json".into(),
)); ));
} }
} }

View file

@ -1,3 +1,5 @@
use serde::{de::Error, Deserialize, Serialize};
use crate::{ use crate::{
constants::CMD_NAME, constants::CMD_NAME,
device_prober::get_xr_usb_devices, device_prober::get_xr_usb_devices,
@ -9,8 +11,7 @@ use crate::{
survive::survive_profile, wivrn::wivrn_profile, wmr::wmr_profile, survive::survive_profile, wivrn::wivrn_profile, wmr::wmr_profile,
}, },
}; };
use serde::{ser::Error, Deserialize, Serialize}; use std::{fs::File, io::BufReader, path::PathBuf};
use std::{fs::File, io::BufReader};
fn default_win_size() -> [i32; 2] { fn default_win_size() -> [i32; 2] {
[360, 400] [360, 400]
@ -56,16 +57,12 @@ impl Config {
} }
} }
pub fn config_file_path() -> String { pub fn config_file_path() -> PathBuf {
format!( get_config_dir().join(format!("{CMD_NAME}.json"))
"{config}/{name}.json",
config = get_config_dir(),
name = CMD_NAME
)
} }
fn from_path(path_s: String) -> Self { fn from_path(path: &PathBuf) -> Self {
match File::open(path_s) { match File::open(path) {
Ok(file) => { Ok(file) => {
let reader = BufReader::new(file); let reader = BufReader::new(file);
match serde_json::from_reader(reader) { match serde_json::from_reader(reader) {
@ -77,8 +74,8 @@ impl Config {
} }
} }
fn save_to_path(&self, path_s: &String) -> Result<(), serde_json::Error> { fn save_to_path(&self, path: &PathBuf) -> Result<(), serde_json::Error> {
let writer = get_writer(path_s).map_err(serde_json::Error::custom)?; let writer = get_writer(path).map_err(serde_json::Error::custom)?;
serde_json::to_writer_pretty(writer, self) serde_json::to_writer_pretty(writer, self)
} }
@ -88,7 +85,7 @@ impl Config {
} }
pub fn get_config() -> Self { pub fn get_config() -> Self {
Self::from_path(Self::config_file_path()) Self::from_path(&Self::config_file_path())
} }
pub fn set_profiles(&mut self, profiles: &[Profile]) { pub fn set_profiles(&mut self, profiles: &[Profile]) {
@ -117,7 +114,7 @@ mod tests {
#[test] #[test]
fn will_load_default_if_config_does_not_exist() { fn will_load_default_if_config_does_not_exist() {
assert_eq!( assert_eq!(
Config::from_path("/non/existing/file.json".into()).debug_view_enabled, Config::from_path(&"/non/existing/file.json".into()).debug_view_enabled,
false false
) )
} }

View file

@ -1,5 +1,7 @@
use crate::paths::get_exec_prefix; use crate::paths::get_exec_prefix;
use std::path::PathBuf;
pub const APP_NAME: &str = "@PRETTY_NAME@"; pub const APP_NAME: &str = "@PRETTY_NAME@";
pub const APP_ID: &str = "@APP_ID@"; pub const APP_ID: &str = "@APP_ID@";
pub const RESOURCES_BASE_PATH: &str = "@RESOURCES_BASE_PATH@"; pub const RESOURCES_BASE_PATH: &str = "@RESOURCES_BASE_PATH@";
@ -17,10 +19,10 @@ pub fn get_developers() -> Vec<String> {
vec!["Gabriele Musco <gabmus@disroot.org>".to_string()] vec!["Gabriele Musco <gabmus@disroot.org>".to_string()]
} }
pub fn pkg_data_dir() -> String { pub fn pkg_data_dir() -> PathBuf {
format!("{}/share/{}", get_exec_prefix(), CMD_NAME) get_exec_prefix().join("share").join(CMD_NAME)
} }
pub fn resources() -> String { pub fn resources() -> String {
format!("{}/resources.gresource", pkg_data_dir()) format!("{}/resources.gresource", pkg_data_dir().to_string_lossy().to_string())
} }

View file

@ -3,7 +3,7 @@ use reqwest::{
header::{HeaderMap, USER_AGENT}, header::{HeaderMap, USER_AGENT},
Method, Method,
}; };
use std::{io::prelude::*, thread::JoinHandle}; use std::{io::prelude::*, path::PathBuf, thread::JoinHandle};
use std::{thread, time::Duration}; use std::{thread, time::Duration};
const TIMEOUT: Duration = Duration::from_secs(60); const TIMEOUT: Duration = Duration::from_secs(60);
@ -23,7 +23,7 @@ fn client() -> reqwest::blocking::Client {
.expect("Failed to build reqwest::Client") .expect("Failed to build reqwest::Client")
} }
pub fn download_file(url: String, path: String) -> JoinHandle<Result<(), reqwest::Error>> { pub fn download_file(url: String, path: PathBuf) -> JoinHandle<Result<(), reqwest::Error>> {
thread::spawn(move || { thread::spawn(move || {
let client = client(); let client = client();
match client.request(Method::GET, url).send() { match client.request(Method::GET, url).send() {

View file

@ -1,7 +1,8 @@
use crate::{ use crate::{
file_utils::{copy_file, deserialize_file, get_writer, set_file_readonly}, file_utils::{copy_file, deserialize_file, get_writer, set_file_readonly},
paths::{get_backup_dir, get_xdg_config_dir, get_xdg_data_dir, SYSTEM_PREFIX}, paths::{get_backup_dir, SYSTEM_PREFIX},
profile::{Profile, XRServiceType}, profile::{Profile, XRServiceType},
xdg::XDG,
}; };
use serde::{ser::Error, Deserialize, Serialize}; use serde::{ser::Error, Deserialize, Serialize};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -10,9 +11,9 @@ use std::path::{Path, PathBuf};
pub struct ActiveRuntimeInnerRuntime { pub struct ActiveRuntimeInnerRuntime {
#[serde(rename = "VALVE_runtime_is_steamvr")] #[serde(rename = "VALVE_runtime_is_steamvr")]
pub valve_runtime_is_steamvr: Option<bool>, pub valve_runtime_is_steamvr: Option<bool>,
pub library_path: String,
#[serde(rename = "MND_libmonado_path")] #[serde(rename = "MND_libmonado_path")]
pub libmonado_path: Option<PathBuf>, pub libmonado_path: Option<PathBuf>,
pub library_path: PathBuf,
pub name: Option<String>, pub name: Option<String>,
} }
@ -22,26 +23,20 @@ pub struct ActiveRuntime {
pub runtime: ActiveRuntimeInnerRuntime, pub runtime: ActiveRuntimeInnerRuntime,
} }
pub fn get_openxr_conf_dir() -> String { pub fn get_openxr_conf_dir() -> PathBuf {
format!("{config}/openxr", config = get_xdg_config_dir()) XDG.get_config_home().join("openxr")
} }
fn get_active_runtime_json_path() -> String { fn get_active_runtime_json_path() -> PathBuf {
format!( get_openxr_conf_dir().join("1/active_runtime.json")
"{config}/1/active_runtime.json",
config = get_openxr_conf_dir()
)
} }
pub fn is_steam(active_runtime: &ActiveRuntime) -> bool { pub fn is_steam(active_runtime: &ActiveRuntime) -> bool {
matches!(active_runtime.runtime.valve_runtime_is_steamvr, Some(true)) matches!(active_runtime.runtime.valve_runtime_is_steamvr, Some(true))
} }
fn get_backup_steam_active_runtime_path() -> String { fn get_backup_steam_active_runtime_path() -> PathBuf {
format!( get_backup_dir().join("active_runtime.json.steam.bak")
"{backup}/active_runtime.json.steam.bak",
backup = get_backup_dir()
)
} }
fn get_backed_up_steam_active_runtime() -> Option<ActiveRuntime> { fn get_backed_up_steam_active_runtime() -> Option<ActiveRuntime> {
@ -59,8 +54,8 @@ fn backup_steam_active_runtime() {
} }
} }
fn get_active_runtime_from_path(path_s: &String) -> Option<ActiveRuntime> { fn get_active_runtime_from_path(path: &PathBuf) -> Option<ActiveRuntime> {
deserialize_file(path_s) deserialize_file(path)
} }
pub fn get_current_active_runtime() -> Option<ActiveRuntime> { pub fn get_current_active_runtime() -> Option<ActiveRuntime> {
@ -69,9 +64,9 @@ pub fn get_current_active_runtime() -> Option<ActiveRuntime> {
fn dump_active_runtime_to_path( fn dump_active_runtime_to_path(
active_runtime: &ActiveRuntime, active_runtime: &ActiveRuntime,
path_s: &String, path: &PathBuf,
) -> Result<(), serde_json::Error> { ) -> Result<(), serde_json::Error> {
let writer = get_writer(path_s).map_err(serde_json::Error::custom)?; let writer = get_writer(path).map_err(serde_json::Error::custom)?;
serde_json::to_writer_pretty(writer, active_runtime) serde_json::to_writer_pretty(writer, active_runtime)
} }
@ -89,11 +84,10 @@ fn build_steam_active_runtime() -> ActiveRuntime {
file_format_version: "1.0.0".into(), file_format_version: "1.0.0".into(),
runtime: ActiveRuntimeInnerRuntime { runtime: ActiveRuntimeInnerRuntime {
valve_runtime_is_steamvr: Some(true), valve_runtime_is_steamvr: Some(true),
library_path: format!(
"{data}/Steam/steamapps/common/SteamVR/bin/linux64/vrclient.so",
data = get_xdg_data_dir()
),
libmonado_path: None, libmonado_path: None,
library_path: XDG
.get_data_home()
.join("Steam/steamapps/common/SteamVR/bin/linux64/vrclient.so"),
name: Some("SteamVR".into()), name: Some("SteamVR".into()),
}, },
} }
@ -144,26 +138,25 @@ pub fn build_profile_active_runtime(profile: &Profile) -> ActiveRuntime {
name: None, name: None,
valve_runtime_is_steamvr: None, valve_runtime_is_steamvr: None,
libmonado_path: monado_so, libmonado_path: monado_so,
library_path: oxr_so.to_string_lossy().into_owned(), library_path: oxr_so,
}, },
} }
} }
// for system installs // for system installs
fn relativize_active_runtime_lib_path(ar: &ActiveRuntime, dest: &str) -> ActiveRuntime { fn relativize_active_runtime_lib_path(ar: &ActiveRuntime, path: &PathBuf) -> ActiveRuntime {
let mut res = ar.clone(); let mut res = ar.clone();
let path = Path::new(dest);
let mut rel_chain = path let mut rel_chain = path
.components() .components()
.map(|_| String::from("..")) .map(|_| String::from(".."))
.collect::<Vec<String>>(); .collect::<Vec<String>>();
rel_chain.pop(); rel_chain.pop();
rel_chain.pop(); rel_chain.pop();
res.runtime.library_path = format!( res.runtime.library_path = PathBuf::from(format!(
"{rels}{fullpath}", "{rels}{fullpath}",
rels = rel_chain.join("/"), rels = rel_chain.join("/"),
fullpath = ar.runtime.library_path fullpath = ar.runtime.library_path.to_string_lossy()
); ));
res res
} }
@ -174,7 +167,7 @@ pub fn set_current_active_runtime_to_profile(profile: &Profile) -> anyhow::Resul
let pfx = profile.clone().prefix; let pfx = profile.clone().prefix;
let mut ar = build_profile_active_runtime(profile); let mut ar = build_profile_active_runtime(profile);
// hack: relativize libopenxr_monado.so path for system installs // hack: relativize libopenxr_monado.so path for system installs
if pfx == SYSTEM_PREFIX { if pfx == PathBuf::from(SYSTEM_PREFIX) {
ar = relativize_active_runtime_lib_path(&ar, &dest); ar = relativize_active_runtime_lib_path(&ar, &dest);
} }
dump_current_active_runtime(&ar)?; dump_current_active_runtime(&ar)?;
@ -184,6 +177,8 @@ pub fn set_current_active_runtime_to_profile(profile: &Profile) -> anyhow::Resul
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::path::PathBuf;
use super::{ use super::{
dump_active_runtime_to_path, get_active_runtime_from_path, dump_active_runtime_to_path, get_active_runtime_from_path,
relativize_active_runtime_lib_path, ActiveRuntime, ActiveRuntimeInnerRuntime, relativize_active_runtime_lib_path, ActiveRuntime, ActiveRuntimeInnerRuntime,
@ -197,7 +192,9 @@ mod tests {
assert!(ar.runtime.valve_runtime_is_steamvr.unwrap()); assert!(ar.runtime.valve_runtime_is_steamvr.unwrap());
assert_eq!( assert_eq!(
ar.runtime.library_path, ar.runtime.library_path,
"/home/user/.local/share/Steam/steamapps/common/SteamVR/bin/linux64/vrclient.so" PathBuf::from(
"/home/user/.local/share/Steam/steamapps/common/SteamVR/bin/linux64/vrclient.so"
)
); );
assert_eq!(ar.runtime.name.unwrap(), "SteamVR"); assert_eq!(ar.runtime.name.unwrap(), "SteamVR");
} }
@ -232,10 +229,14 @@ mod tests {
}; };
let relativized = relativize_active_runtime_lib_path( let relativized = relativize_active_runtime_lib_path(
&ar, &ar,
"/home/user/.config/openxr/1/active_runtime.json", &PathBuf::from("/home/user/.config/openxr/1/active_runtime.json"),
); );
assert_eq!( assert_eq!(
relativized.runtime.library_path, relativized
.runtime
.library_path
.to_string_lossy()
.to_string(),
"../../../../../usr/lib64/libopenxr_monado.so" "../../../../../usr/lib64/libopenxr_monado.so"
); );
} }

View file

@ -1,6 +1,8 @@
use std::path::PathBuf;
use crate::{ use crate::{
file_utils::{deserialize_file, get_writer}, file_utils::{deserialize_file, get_writer},
paths::get_xdg_config_dir, xdg::XDG,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -28,15 +30,12 @@ impl Default for MonadoAutorunConfig {
} }
} }
fn get_monado_autorun_config_path() -> String { fn get_monado_autorun_config_path() -> PathBuf {
format!( XDG.get_config_home().join("monado/autorun_v0.json")
"{config}/monado/autorun_v0.json",
config = get_xdg_config_dir()
)
} }
fn get_monado_autorun_config_from_path(path_s: &String) -> Option<MonadoAutorunConfig> { fn get_monado_autorun_config_from_path(path: &PathBuf) -> Option<MonadoAutorunConfig> {
deserialize_file(path_s) deserialize_file(path)
} }
pub fn get_monado_autorun_config() -> MonadoAutorunConfig { pub fn get_monado_autorun_config() -> MonadoAutorunConfig {
@ -44,8 +43,8 @@ pub fn get_monado_autorun_config() -> MonadoAutorunConfig {
.unwrap_or(MonadoAutorunConfig::default()) .unwrap_or(MonadoAutorunConfig::default())
} }
fn dump_monado_autorun_config_to_path(config: &MonadoAutorunConfig, path_s: &String) { fn dump_monado_autorun_config_to_path(config: &MonadoAutorunConfig, path: &PathBuf) {
let writer = get_writer(path_s).expect("Unable to save Monado Autorun config"); let writer = get_writer(path).expect("Unable to save Monado Autorun config");
serde_json::to_writer_pretty(writer, config).expect("Unable to save Monado Autorun config"); serde_json::to_writer_pretty(writer, config).expect("Unable to save Monado Autorun config");
} }

View file

@ -1,43 +1,41 @@
use std::path::PathBuf;
use crate::{ use crate::{
file_utils::{copy_file, deserialize_file, get_writer, set_file_readonly}, file_utils::{copy_file, deserialize_file, get_writer, set_file_readonly},
paths::{get_backup_dir, get_xdg_config_dir, get_xdg_data_dir}, paths::get_backup_dir,
profile::Profile, profile::Profile,
xdg::XDG,
}; };
use serde::{ser::Error, Deserialize, Serialize}; use serde::{ser::Error, Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct OpenVrPaths { pub struct OpenVrPaths {
config: Vec<String>, config: Vec<PathBuf>,
external_drivers: Option<Vec<String>>, // never seen it populated external_drivers: Option<Vec<String>>, // never seen it populated
jsonid: String, jsonid: String,
log: Vec<String>, log: Vec<PathBuf>,
runtime: Vec<String>, runtime: Vec<PathBuf>,
version: u32, version: u32,
} }
pub fn get_openvr_conf_dir() -> String { pub fn get_openvr_conf_dir() -> PathBuf {
format!("{config}/openvr", config = get_xdg_config_dir()) XDG.get_config_home().join("openvr")
} }
fn get_openvrpaths_vrpath_path() -> String { fn get_openvrpaths_vrpath_path() -> PathBuf {
format!( get_openvr_conf_dir().join("openvrpaths.vrpath")
"{config}/openvrpaths.vrpath",
config = get_openvr_conf_dir()
)
} }
pub fn is_steam(ovr_paths: &OpenVrPaths) -> bool { pub fn is_steam(ovr_paths: &OpenVrPaths) -> bool {
ovr_paths.runtime.iter().any(|rt| { ovr_paths.runtime.iter().any(|rt| {
rt.to_lowercase() rt.to_string_lossy()
.to_lowercase()
.ends_with("/steam/steamapps/common/steamvr") .ends_with("/steam/steamapps/common/steamvr")
}) })
} }
fn get_backup_steam_openvrpaths_path() -> String { fn get_backup_steam_openvrpaths_path() -> PathBuf {
format!( get_backup_dir().join("openvrpaths.vrpath.steam.bak")
"{backup}/openvrpaths.vrpath.steam.bak",
backup = get_backup_dir()
)
} }
fn get_backed_up_steam_openvrpaths() -> Option<OpenVrPaths> { fn get_backed_up_steam_openvrpaths() -> Option<OpenVrPaths> {
@ -55,8 +53,8 @@ fn backup_steam_openvrpaths() {
} }
} }
fn get_openvrpaths_from_path(path_s: &String) -> Option<OpenVrPaths> { fn get_openvrpaths_from_path(path: &PathBuf) -> Option<OpenVrPaths> {
deserialize_file(path_s) deserialize_file(path)
} }
pub fn get_current_openvrpaths() -> Option<OpenVrPaths> { pub fn get_current_openvrpaths() -> Option<OpenVrPaths> {
@ -65,9 +63,9 @@ pub fn get_current_openvrpaths() -> Option<OpenVrPaths> {
fn dump_openvrpaths_to_path( fn dump_openvrpaths_to_path(
ovr_paths: &OpenVrPaths, ovr_paths: &OpenVrPaths,
path_s: &String, path: &PathBuf,
) -> Result<(), serde_json::Error> { ) -> Result<(), serde_json::Error> {
let writer = get_writer(path_s).map_err(serde_json::Error::custom)?; let writer = get_writer(path).map_err(serde_json::Error::custom)?;
serde_json::to_writer_pretty(writer, ovr_paths) serde_json::to_writer_pretty(writer, ovr_paths)
} }
@ -79,16 +77,13 @@ fn build_steam_openvrpaths() -> OpenVrPaths {
if let Some(backup) = get_backed_up_steam_openvrpaths() { if let Some(backup) = get_backed_up_steam_openvrpaths() {
return backup; return backup;
} }
let datadir = get_xdg_data_dir(); let datadir = XDG.get_data_home();
OpenVrPaths { OpenVrPaths {
config: vec![format!("{data}/Steam/config", data = datadir)], config: vec![datadir.join("Steam/config")],
external_drivers: None, external_drivers: None,
jsonid: "vrpathreg".into(), jsonid: "vrpathreg".into(),
log: vec![format!("{data}/Steam/logs", data = datadir)], log: vec![datadir.join("Steam/logs")],
runtime: vec![format!( runtime: vec![datadir.join("Steam/steamapps/common/SteamVR")],
"{data}/Steam/steamapps/common/SteamVR",
data = datadir
)],
version: 1, version: 1,
} }
} }
@ -100,16 +95,13 @@ pub fn set_current_openvrpaths_to_steam() -> anyhow::Result<()> {
} }
pub fn build_profile_openvrpaths(profile: &Profile) -> OpenVrPaths { pub fn build_profile_openvrpaths(profile: &Profile) -> OpenVrPaths {
let datadir = get_xdg_data_dir(); let datadir = XDG.get_data_home();
OpenVrPaths { OpenVrPaths {
config: vec![format!("{data}/Steam/config", data = datadir)], config: vec![datadir.join("Steam/config")],
external_drivers: None, external_drivers: None,
jsonid: "vrpathreg".into(), jsonid: "vrpathreg".into(),
log: vec![format!("{data}/Steam/logs", data = datadir)], log: vec![datadir.join("Steam/logs")],
runtime: vec![format!( runtime: vec![profile.opencomposite_path.join("build")],
"{opencomp_dir}/build",
opencomp_dir = profile.opencomposite_path
)],
version: 1, version: 1,
} }
} }
@ -125,6 +117,8 @@ pub fn set_current_openvrpaths_to_profile(profile: &Profile) -> anyhow::Result<(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::path::PathBuf;
use super::{dump_openvrpaths_to_path, get_openvrpaths_from_path, OpenVrPaths}; use super::{dump_openvrpaths_to_path, get_openvrpaths_from_path, OpenVrPaths};
#[test] #[test]
@ -133,7 +127,7 @@ mod tests {
assert_eq!(ovrp.config.len(), 1); assert_eq!(ovrp.config.len(), 1);
assert_eq!( assert_eq!(
ovrp.config.get(0).unwrap(), ovrp.config.get(0).unwrap(),
"/home/user/.local/share/Steam/config" &PathBuf::from("/home/user/.local/share/Steam/config")
); );
assert_eq!(ovrp.external_drivers, None); assert_eq!(ovrp.external_drivers, None);
assert_eq!(ovrp.jsonid, "vrpathreg"); assert_eq!(ovrp.jsonid, "vrpathreg");

View file

@ -1,9 +1,9 @@
use crate::{ use crate::{
file_utils::{deserialize_file, get_writer}, file_utils::{deserialize_file, get_writer},
paths::get_xdg_config_dir, xdg::XDG,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{fmt::Display, slice::Iter}; use std::{fmt::Display, path::PathBuf, slice::Iter};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
@ -123,20 +123,20 @@ impl Default for WivrnConfig {
} }
} }
fn get_wivrn_config_path() -> String { fn get_wivrn_config_path() -> PathBuf {
format!("{config}/wivrn/config.json", config = get_xdg_config_dir()) XDG.get_config_home().join("wivrn/config.json")
} }
fn get_wivrn_config_from_path(path_s: &String) -> Option<WivrnConfig> { fn get_wivrn_config_from_path(path: &PathBuf) -> Option<WivrnConfig> {
deserialize_file(path_s) deserialize_file(path)
} }
pub fn get_wivrn_config() -> WivrnConfig { pub fn get_wivrn_config() -> WivrnConfig {
get_wivrn_config_from_path(&get_wivrn_config_path()).unwrap_or(WivrnConfig::default()) get_wivrn_config_from_path(&get_wivrn_config_path()).unwrap_or(WivrnConfig::default())
} }
fn dump_wivrn_config_to_path(config: &WivrnConfig, path_s: &String) { fn dump_wivrn_config_to_path(config: &WivrnConfig, path: &PathBuf) {
let writer = get_writer(path_s).expect("Unable to save WiVRn config"); let writer = get_writer(path).expect("Unable to save WiVRn config");
serde_json::to_writer_pretty(writer, config).expect("Unable to save WiVRn config"); serde_json::to_writer_pretty(writer, config).expect("Unable to save WiVRn config");
} }

View file

@ -6,11 +6,10 @@ use nix::{
use std::{ use std::{
fs::{self, copy, create_dir_all, remove_dir_all, File, OpenOptions}, fs::{self, copy, create_dir_all, remove_dir_all, File, OpenOptions},
io::{BufReader, BufWriter}, io::{BufReader, BufWriter},
path::Path, path::{Path, PathBuf},
}; };
pub fn get_writer(path_s: &str) -> anyhow::Result<BufWriter<std::fs::File>> { pub fn get_writer(path: &PathBuf) -> anyhow::Result<BufWriter<std::fs::File>> {
let path = Path::new(path_s);
if let Some(parent) = path.parent() { if let Some(parent) = path.parent() {
if !parent.is_dir() { if !parent.is_dir() {
create_dir_all(parent)?; create_dir_all(parent)?;
@ -24,26 +23,25 @@ pub fn get_writer(path_s: &str) -> anyhow::Result<BufWriter<std::fs::File>> {
Ok(BufWriter::new(file)) Ok(BufWriter::new(file))
} }
pub fn get_reader(path_s: &str) -> Option<BufReader<File>> { pub fn get_reader(path: &PathBuf) -> Option<BufReader<File>> {
let path = Path::new(&path_s);
if !(path.is_file() || path.is_symlink()) { if !(path.is_file() || path.is_symlink()) {
return None; return None;
} }
match File::open(path) { match File::open(path) {
Err(e) => { Err(e) => {
println!("Error opening {}: {}", path_s, e); println!("Error opening {}: {}", path.to_string_lossy(), e);
None None
} }
Ok(fd) => Some(BufReader::new(fd)), Ok(fd) => Some(BufReader::new(fd)),
} }
} }
pub fn deserialize_file<T: serde::de::DeserializeOwned>(path_s: &String) -> Option<T> { pub fn deserialize_file<T: serde::de::DeserializeOwned>(path: &PathBuf) -> Option<T> {
match get_reader(path_s) { match get_reader(path) {
None => None, None => None,
Some(reader) => match serde_json::from_reader(reader) { Some(reader) => match serde_json::from_reader(reader) {
Err(e) => { Err(e) => {
println!("Failed to deserialize {}: {}", path_s, e); println!("Failed to deserialize {}: {}", path.to_string_lossy(), e);
None None
} }
Ok(res) => Some(res), Ok(res) => Some(res),
@ -51,8 +49,7 @@ pub fn deserialize_file<T: serde::de::DeserializeOwned>(path_s: &String) -> Opti
} }
} }
pub fn set_file_readonly(path_s: &str, readonly: bool) -> Result<(), std::io::Error> { pub fn set_file_readonly(path: &PathBuf, readonly: bool) -> Result<(), std::io::Error> {
let path = Path::new(&path_s);
if !path.is_file() { if !path.is_file() {
println!("WARN: trying to set readonly on a file that does not exist"); println!("WARN: trying to set readonly on a file that does not exist");
return Ok(()); return Ok(());
@ -64,38 +61,45 @@ pub fn set_file_readonly(path_s: &str, readonly: bool) -> Result<(), std::io::Er
fs::set_permissions(path, perms) fs::set_permissions(path, perms)
} }
pub fn setcap_cap_sys_nice_eip(file: String) { pub fn setcap_cap_sys_nice_eip(file: &PathBuf) {
let mut runner = CmdRunner::new( let mut runner = CmdRunner::new(
None, None,
"pkexec".into(), "pkexec".into(),
vec!["setcap".into(), "CAP_SYS_NICE=eip".into(), file], vec![
"setcap".into(),
"CAP_SYS_NICE=eip".into(),
file.to_string_lossy().to_string(),
],
); );
runner.start(); runner.start();
runner.join(); runner.join();
} }
pub fn rm_rf(path_s: &String) { pub fn rm_rf(path: &PathBuf) {
if remove_dir_all(path_s).is_err() { if remove_dir_all(path).is_err() {
println!("Failed to remove path {}", path_s); println!("Failed to remove path {}", path.to_string_lossy());
} }
} }
pub fn copy_file(source_s: &str, dest_s: &str) { pub fn copy_file(source: &PathBuf, dest: &PathBuf) {
let source = Path::new(source_s);
let dest = Path::new(dest_s);
if let Some(parent) = dest.parent() { if let Some(parent) = dest.parent() {
if !parent.is_dir() { if !parent.is_dir() {
create_dir_all(parent) create_dir_all(parent)
.unwrap_or_else(|_| panic!("Failed to create dir {}", parent.to_str().unwrap())); .unwrap_or_else(|_| panic!("Failed to create dir {}", parent.to_str().unwrap()));
} }
} }
set_file_readonly(dest_s, false) set_file_readonly(dest, false)
.unwrap_or_else(|_| panic!("Failed to set file {} as rw", dest_s)); .unwrap_or_else(|_| panic!("Failed to set file {} as rw", dest.to_string_lossy()));
copy(source, dest).unwrap_or_else(|_| panic!("Failed to copy {} to {}", source_s, dest_s)); copy(source, dest).unwrap_or_else(|_| {
panic!(
"Failed to copy {} to {}",
source.to_string_lossy(),
dest.to_string_lossy()
)
});
} }
pub fn mount_has_nosuid(path_s: &str) -> Result<bool, Errno> { pub fn mount_has_nosuid(path: &PathBuf) -> Result<bool, Errno> {
let path = Path::new(path_s);
match statvfs(path) { match statvfs(path) {
Ok(fstats) => Ok(fstats.flags().contains(FsFlags::ST_NOSUID)), Ok(fstats) => Ok(fstats.flags().contains(FsFlags::ST_NOSUID)),
Err(e) => Err(e), Err(e) => Err(e),
@ -104,10 +108,12 @@ pub fn mount_has_nosuid(path_s: &str) -> Result<bool, Errno> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::path::PathBuf;
use crate::file_utils::mount_has_nosuid; use crate::file_utils::mount_has_nosuid;
#[test] #[test]
fn can_get_nosuid() { fn can_get_nosuid() {
mount_has_nosuid("/tmp").expect("Error running statvfs"); mount_has_nosuid(&PathBuf::from("/tmp")).expect("Error running statvfs");
} }
} }

View file

@ -1,16 +1,16 @@
use crate::file_utils::get_reader; use crate::file_utils::get_reader;
use std::{error::Error, fmt::Display, io::Read, str::FromStr}; use std::{error::Error, fmt::Display, io::Read, path::PathBuf, str::FromStr};
// const POW_PROF_PATH: &str = "/sys/class/drm/card0/device/pp_power_profile_mode"; // const POW_PROF_PATH: &str = "/sys/class/drm/card0/device/pp_power_profile_mode";
fn power_profile_mode_file(card_dir: &str) -> String { fn power_profile_mode_file(card_dir: &PathBuf) -> PathBuf {
format!("{}/device/pp_power_profile_mode", card_dir) card_dir.join("device/pp_power_profile_mode")
} }
pub fn get_set_amd_vr_pow_prof_cmd(card_dir: &str) -> String { pub fn get_set_amd_vr_pow_prof_cmd(card_dir: &PathBuf) -> String {
format!( format!(
"sudo sh -c \"echo '4' > {}\"", "sudo sh -c \"echo '4' > {}\"",
power_profile_mode_file(card_dir) power_profile_mode_file(card_dir).to_string_lossy()
) )
} }
@ -93,10 +93,10 @@ const AMD_VENDOR_ID: &str = "0x1002";
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum GpuSysDrm { pub enum GpuSysDrm {
Amd(String), Amd(PathBuf),
Intel(String), Intel(PathBuf),
Nvidia(String), Nvidia(PathBuf),
Other(String), Other(PathBuf),
} }
fn list_gpus() -> Vec<GpuSysDrm> { fn list_gpus() -> Vec<GpuSysDrm> {
@ -104,8 +104,8 @@ fn list_gpus() -> Vec<GpuSysDrm> {
for i in 0..5 { for i in 0..5 {
// arbitrary range, find a better way // arbitrary range, find a better way
let card_dir = format!("/sys/class/drm/card{}", i); let card_dir = PathBuf::from(format!("/sys/class/drm/card{}", i));
let vendor_file = format!("{}/device/vendor", card_dir); let vendor_file = card_dir.join("device/vendor");
if let Some(mut reader) = get_reader(&vendor_file) { if let Some(mut reader) = get_reader(&vendor_file) {
let mut buf = String::new(); let mut buf = String::new();
if reader.read_to_string(&mut buf).is_ok() { if reader.read_to_string(&mut buf).is_ok() {
@ -131,7 +131,7 @@ pub fn get_first_amd_gpu() -> Option<GpuSysDrm> {
pub fn get_amd_gpu_power_profile() -> Option<GpuPowerProfile> { pub fn get_amd_gpu_power_profile() -> Option<GpuPowerProfile> {
let amd_gpu = get_first_amd_gpu(); let amd_gpu = get_first_amd_gpu();
if let Some(GpuSysDrm::Amd(card_dir)) = amd_gpu { if let Some(GpuSysDrm::Amd(card_dir)) = amd_gpu {
if let Some(mut reader) = get_reader(&power_profile_mode_file(card_dir.as_str())) { if let Some(mut reader) = get_reader(&power_profile_mode_file(&card_dir)) {
let mut txt = String::new(); let mut txt = String::new();
reader.read_to_string(&mut txt).ok()?; reader.read_to_string(&mut txt).ok()?;
for line in txt.split('\n') { for line in txt.split('\n') {

View file

@ -1,5 +1,8 @@
use crate::file_utils::get_reader; use crate::file_utils::get_reader;
use std::io::{BufRead, Read}; use std::{
io::{BufRead, Read},
path::PathBuf,
};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum LinuxDistro { pub enum LinuxDistro {
@ -18,10 +21,10 @@ impl LinuxDistro {
} }
fn get_from_etc_os_release() -> Option<Self> { fn get_from_etc_os_release() -> Option<Self> {
Self::get_from_etc_os_release_file("/etc/os-release") Self::get_from_etc_os_release_file(&"/etc/os-release".into())
} }
fn get_from_etc_os_release_file(fp: &str) -> Option<Self> { fn get_from_etc_os_release_file(fp: &PathBuf) -> Option<Self> {
if let Some(mut reader) = get_reader(fp) { if let Some(mut reader) = get_reader(fp) {
let mut buf = String::new(); let mut buf = String::new();
loop { loop {
@ -47,7 +50,7 @@ impl LinuxDistro {
} }
fn get_from_etc_issue() -> Option<Self> { fn get_from_etc_issue() -> Option<Self> {
if let Some(mut reader) = get_reader("/etc/issue") { if let Some(mut reader) = get_reader(&"/etc/issue".into()) {
let mut buf = String::new(); let mut buf = String::new();
if reader.read_to_string(&mut buf).is_ok() { if reader.read_to_string(&mut buf).is_ok() {
buf = buf.trim().to_lowercase(); buf = buf.trim().to_lowercase();
@ -111,7 +114,7 @@ mod tests {
#[test] #[test]
fn can_detect_arch_linux_from_etc_os_release() { fn can_detect_arch_linux_from_etc_os_release() {
assert_eq!( assert_eq!(
LinuxDistro::get_from_etc_os_release_file("./test/files/archlinux-os-release"), LinuxDistro::get_from_etc_os_release_file(&"./test/files/archlinux-os-release".into()),
Some(LinuxDistro::Arch) Some(LinuxDistro::Arch)
) )
} }

View file

@ -42,6 +42,7 @@ pub mod runner_pipeline;
pub mod steam_linux_runtime_injector; pub mod steam_linux_runtime_injector;
pub mod steamvr_utils; pub mod steamvr_utils;
pub mod ui; pub mod ui;
pub mod xdg;
pub mod xr_devices; pub mod xr_devices;
fn restore_steam_xr_files() { fn restore_steam_xr_files() {

View file

@ -1,32 +1,36 @@
use crate::{constants::CMD_NAME, profile::XRServiceType}; use crate::{constants::CMD_NAME, profile::XRServiceType, xdg::XDG};
use std::{env, fs::create_dir_all, path::Path}; use std::{
env,
fs::create_dir_all,
path::{Path, PathBuf},
};
pub fn data_opencomposite_path() -> String { pub fn data_opencomposite_path() -> PathBuf {
format!("{data}/opencomposite", data = get_data_dir()) get_data_dir().join("opencomposite")
} }
pub fn data_monado_path() -> String { pub fn data_monado_path() -> PathBuf {
format!("{data}/monado", data = get_data_dir()) get_data_dir().join("monado")
} }
pub fn data_wivrn_path() -> String { pub fn data_wivrn_path() -> PathBuf {
format!("{data}/wivrn", data = get_data_dir()) get_data_dir().join("wivrn")
} }
pub fn data_libsurvive_path() -> String { pub fn data_libsurvive_path() -> PathBuf {
format!("{data}/libsurvive", data = get_data_dir()) get_data_dir().join("libsurvive")
} }
pub fn data_openhmd_path() -> String { pub fn data_openhmd_path() -> PathBuf {
format!("{data}/openhmd", data = get_data_dir()) get_data_dir().join("openhmd")
} }
pub fn data_basalt_path() -> String { pub fn data_basalt_path() -> PathBuf {
format!("{data}/basalt", data = get_data_dir()) get_data_dir().join("basalt")
} }
pub fn wivrn_apk_download_path() -> String { pub fn wivrn_apk_download_path() -> PathBuf {
format!("{cache}/wivrn.apk", cache = get_cache_dir()) get_cache_dir().join("wivrn.apk")
} }
pub const SYSTEM_PREFIX: &str = "/usr"; pub const SYSTEM_PREFIX: &str = "/usr";
@ -34,58 +38,24 @@ pub const SYSTEM_PREFIX: &str = "/usr";
/** System prefix inside a bubblewrap environment (flatpak or pressure vessel) */ /** System prefix inside a bubblewrap environment (flatpak or pressure vessel) */
pub const BWRAP_SYSTEM_PREFIX: &str = "/run/host/usr"; pub const BWRAP_SYSTEM_PREFIX: &str = "/run/host/usr";
pub fn get_home_dir() -> String { pub fn get_home_dir() -> PathBuf {
env::var("HOME").expect("HOME env var not defined") env::var("HOME").expect("HOME env var not defined").into()
} }
pub fn get_xdg_config_dir() -> String { pub fn get_config_dir() -> PathBuf {
match env::var("XDG_CONFIG_HOME") { XDG.get_config_home().join(CMD_NAME)
Ok(conf_home) => conf_home,
Err(_) => format!("{home}/.config", home = get_home_dir()),
}
} }
pub fn get_xdg_data_dir() -> String { pub fn get_data_dir() -> PathBuf {
match env::var("XDG_DATA_HOME") { XDG.get_data_home().join(CMD_NAME)
Ok(data_home) => data_home,
Err(_) => format!("{home}/.local/share", home = get_home_dir()),
}
} }
pub fn get_xdg_cache_dir() -> String { pub fn get_cache_dir() -> PathBuf {
match env::var("XDG_CACHE_HOME") { XDG.get_cache_home().join(CMD_NAME)
Ok(cache_home) => cache_home,
Err(_) => format!("{home}/.cache", home = get_home_dir()),
}
} }
pub fn get_xdg_runtime_dir() -> String { pub fn get_backup_dir() -> PathBuf {
env::var("XDG_RUNTIME_DIR").expect("XDG_RUNTIME_DIR is not set") let p = get_data_dir().join("backups");
}
pub fn get_config_dir() -> String {
format!(
"{config}/{name}",
config = get_xdg_config_dir(),
name = CMD_NAME
)
}
pub fn get_data_dir() -> String {
format!("{data}/{name}", data = get_xdg_data_dir(), name = CMD_NAME)
}
pub fn get_cache_dir() -> String {
format!(
"{cache}/{name}",
cache = get_xdg_cache_dir(),
name = CMD_NAME
)
}
pub fn get_backup_dir() -> String {
let p_s = format!("{data}/backups", data = get_data_dir());
let p = Path::new(&p_s);
if !p.is_dir() { if !p.is_dir() {
if p.is_file() { if p.is_file() {
panic!( panic!(
@ -93,12 +63,12 @@ pub fn get_backup_dir() -> String {
p.to_str().unwrap() p.to_str().unwrap()
); );
} }
create_dir_all(p).expect("Failed to create backups dir"); create_dir_all(&p).expect("Failed to create backups dir");
} }
p.to_str().unwrap().to_string() p
} }
pub fn get_exec_prefix() -> String { pub fn get_exec_prefix() -> PathBuf {
let p = Path::new("/proc/self/exe"); let p = Path::new("/proc/self/exe");
if !p.is_symlink() { if !p.is_symlink() {
panic!("/proc/self/exe is not a symlink!"); panic!("/proc/self/exe is not a symlink!");
@ -110,25 +80,19 @@ pub fn get_exec_prefix() -> String {
.unwrap() .unwrap()
.parent() .parent()
.unwrap() .unwrap()
.to_str() .into()
.unwrap()
.to_string()
} }
pub fn get_ipc_file_path(xrservice_type: &XRServiceType) -> String { pub fn get_ipc_file_path(xrservice_type: &XRServiceType) -> PathBuf {
format!( XDG.get_runtime_directory()
"{runtime}/{xrservice}_comp_ipc", .expect("XDG runtime directory is not available")
runtime = get_xdg_runtime_dir(), .join(match xrservice_type {
xrservice = match xrservice_type { XRServiceType::Monado => "monado_comp_ipc",
XRServiceType::Monado => "monado", XRServiceType::Wivrn => "wivrn_comp_ipc",
XRServiceType::Wivrn => "wivrn", })
}
)
} }
pub fn get_steamvr_bin_dir_path() -> String { pub fn get_steamvr_bin_dir_path() -> PathBuf {
format!( XDG.get_data_home()
"{data}/Steam/steamapps/common/SteamVR/bin/linux64", .join("Steam/steamapps/common/SteamVR/bin/linux64")
data = get_xdg_data_dir()
)
} }

View file

@ -2,8 +2,16 @@ use crate::{
file_utils::get_writer, file_utils::get_writer,
paths::{get_data_dir, get_ipc_file_path, BWRAP_SYSTEM_PREFIX, SYSTEM_PREFIX}, paths::{get_data_dir, get_ipc_file_path, BWRAP_SYSTEM_PREFIX, SYSTEM_PREFIX},
}; };
use nix::NixPath;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fmt::Display, fs::File, io::BufReader, path::Path, slice::Iter}; use std::{
collections::HashMap,
fmt::Display,
fs::File,
io::BufReader,
path::{Path, PathBuf},
slice::Iter,
};
use uuid::Uuid; use uuid::Uuid;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@ -85,7 +93,7 @@ impl Display for ProfileFeatureType {
pub struct ProfileFeature { pub struct ProfileFeature {
pub feature_type: ProfileFeatureType, pub feature_type: ProfileFeatureType,
pub enabled: bool, pub enabled: bool,
pub path: Option<String>, pub path: Option<PathBuf>,
pub repo: Option<String>, pub repo: Option<String>,
pub branch: Option<String>, pub branch: Option<String>,
} }
@ -207,18 +215,18 @@ pub struct Profile {
pub uuid: String, pub uuid: String,
pub name: String, pub name: String,
pub xrservice_type: XRServiceType, pub xrservice_type: XRServiceType,
pub xrservice_path: String, pub xrservice_path: PathBuf,
pub xrservice_repo: Option<String>, pub xrservice_repo: Option<String>,
pub xrservice_branch: Option<String>, pub xrservice_branch: Option<String>,
#[serde(default = "HashMap::<String, String>::default")] #[serde(default = "HashMap::<String, String>::default")]
pub xrservice_cmake_flags: HashMap<String, String>, pub xrservice_cmake_flags: HashMap<String, String>,
pub opencomposite_path: String, pub opencomposite_path: PathBuf,
pub opencomposite_repo: Option<String>, pub opencomposite_repo: Option<String>,
pub opencomposite_branch: Option<String>, pub opencomposite_branch: Option<String>,
pub features: ProfileFeatures, pub features: ProfileFeatures,
pub environment: HashMap<String, String>, pub environment: HashMap<String, String>,
/** Install prefix */ /** Install prefix */
pub prefix: String, pub prefix: PathBuf,
pub can_be_built: bool, pub can_be_built: bool,
pub editable: bool, pub editable: bool,
pub pull_on_build: bool, pub pull_on_build: bool,
@ -239,10 +247,10 @@ impl Display for Profile {
impl Default for Profile { impl Default for Profile {
fn default() -> Self { fn default() -> Self {
let uuid = Uuid::new_v4().to_string(); let uuid = Uuid::new_v4().to_string();
let profile_dir = format!("{}/{}", get_data_dir(), uuid); let profile_dir = get_data_dir().join(&uuid);
Self { Self {
name: "Default profile name".into(), name: "Default profile name".into(),
xrservice_path: format!("{}/xrservice", profile_dir), xrservice_path: profile_dir.join("xrservice"),
xrservice_type: XRServiceType::Monado, xrservice_type: XRServiceType::Monado,
xrservice_repo: None, xrservice_repo: None,
xrservice_branch: None, xrservice_branch: None,
@ -250,21 +258,21 @@ impl Default for Profile {
features: ProfileFeatures { features: ProfileFeatures {
libsurvive: ProfileFeature { libsurvive: ProfileFeature {
enabled: false, enabled: false,
path: Some(format!("{}/libsurvive", profile_dir)), path: Some(profile_dir.join("libsurvive")),
repo: None, repo: None,
branch: None, branch: None,
feature_type: ProfileFeatureType::Libsurvive, feature_type: ProfileFeatureType::Libsurvive,
}, },
basalt: ProfileFeature { basalt: ProfileFeature {
enabled: false, enabled: false,
path: Some(format!("{}/basalt", profile_dir)), path: Some(profile_dir.join("basalt")),
repo: None, repo: None,
branch: None, branch: None,
feature_type: ProfileFeatureType::Basalt, feature_type: ProfileFeatureType::Basalt,
}, },
openhmd: ProfileFeature { openhmd: ProfileFeature {
enabled: false, enabled: false,
path: Some(format!("{}/openhmd", profile_dir)), path: Some(profile_dir.join("openhmd")),
repo: None, repo: None,
branch: None, branch: None,
feature_type: ProfileFeatureType::OpenHmd, feature_type: ProfileFeatureType::OpenHmd,
@ -272,10 +280,10 @@ impl Default for Profile {
mercury_enabled: false, mercury_enabled: false,
}, },
environment: HashMap::new(), environment: HashMap::new(),
prefix: format!("{}/prefixes/{}", get_data_dir(), uuid), prefix: get_data_dir().join("prefixes").join(&uuid),
can_be_built: true, can_be_built: true,
pull_on_build: true, pull_on_build: true,
opencomposite_path: format!("{}/opencomposite", profile_dir), opencomposite_path: profile_dir.join("opencomposite"),
opencomposite_repo: None, opencomposite_repo: None,
opencomposite_branch: None, opencomposite_branch: None,
editable: true, editable: true,
@ -291,7 +299,7 @@ impl Profile {
pub fn xr_runtime_json_env_var(&self) -> String { pub fn xr_runtime_json_env_var(&self) -> String {
format!( format!(
"XR_RUNTIME_JSON=\"{prefix}/share/openxr/1/openxr_{runtime}.json\"", "XR_RUNTIME_JSON=\"{prefix}/share/openxr/1/openxr_{runtime}.json\"",
prefix = match self.prefix.as_str() { prefix = match self.prefix.to_string_lossy().to_string().as_str() {
SYSTEM_PREFIX => BWRAP_SYSTEM_PREFIX, SYSTEM_PREFIX => BWRAP_SYSTEM_PREFIX,
other => other, other => other,
}, },
@ -312,7 +320,7 @@ impl Profile {
self.xr_runtime_json_env_var(), self.xr_runtime_json_env_var(),
format!( format!(
"PRESSURE_VESSEL_FILESYSTEMS_RW=\"{path}\"", "PRESSURE_VESSEL_FILESYSTEMS_RW=\"{path}\"",
path = get_ipc_file_path(&self.xrservice_type), path = get_ipc_file_path(&self.xrservice_type).to_string_lossy(),
), ),
] ]
} }
@ -332,22 +340,22 @@ impl Profile {
vars.join(" ") vars.join(" ")
} }
pub fn get_survive_cli_path(&self) -> Option<String> { pub fn get_survive_cli_path(&self) -> Option<PathBuf> {
let path_s = format!("{pfx}/bin/survive-cli", pfx = self.prefix); let path = self.prefix.join("bin/survive-cli");
if Path::new(&path_s).is_file() { if path.is_file() {
return Some(path_s); return Some(path);
} }
None None
} }
pub fn load_profile(path: &String) -> Self { pub fn load_profile(path: &PathBuf) -> Self {
let file = File::open(path).expect("Unable to open profile"); let file = File::open(path).expect("Unable to open profile");
let reader = BufReader::new(file); let reader = BufReader::new(file);
serde_json::from_reader(reader).expect("Faiuled to deserialize profile") serde_json::from_reader(reader).expect("Faiuled to deserialize profile")
} }
pub fn dump_profile(&self, path_s: &String) { pub fn dump_profile(&self, path: &PathBuf) {
let writer = get_writer(path_s).expect("Could not write profile"); let writer = get_writer(path).expect("Could not write profile");
serde_json::to_writer_pretty(writer, self).expect("Could not write profile") serde_json::to_writer_pretty(writer, self).expect("Could not write profile")
} }
@ -398,53 +406,46 @@ impl Profile {
&& !self.xrservice_path.is_empty() && !self.xrservice_path.is_empty()
&& !self.prefix.is_empty() && !self.prefix.is_empty()
&& (!self.features.libsurvive.enabled && (!self.features.libsurvive.enabled
|| !self || self
.features .features
.libsurvive .libsurvive
.path .path
.as_ref() .as_ref()
.unwrap_or(&"".to_string()) .is_some_and(|p| !p.is_empty()))
.is_empty())
&& (!self.features.basalt.enabled && (!self.features.basalt.enabled
|| !self || self
.features .features
.basalt .basalt
.path .path
.as_ref() .as_ref()
.unwrap_or(&"".to_string()) .is_some_and(|p| !p.is_empty()))
.is_empty())
&& (!self.features.openhmd.enabled && (!self.features.openhmd.enabled
|| !self || self
.features .features
.openhmd .openhmd
.path .path
.as_ref() .as_ref()
.unwrap_or(&"".to_string()) .is_some_and(|p| !p.is_empty()))
.is_empty())
} }
pub fn xrservice_binary(&self) -> String { pub fn xrservice_binary(&self) -> PathBuf {
format!( self.prefix.join("bin").join(match self.xrservice_type {
"{}/bin/{}", XRServiceType::Monado => "monado-service",
self.prefix, XRServiceType::Wivrn => "wivrn-server",
match self.xrservice_type { })
XRServiceType::Monado => "monado-service",
XRServiceType::Wivrn => "wivrn-server",
}
)
} }
pub fn can_start(&self) -> bool { pub fn can_start(&self) -> bool {
Path::new(&self.xrservice_binary()).is_file() Path::new(&self.xrservice_binary()).is_file()
} }
pub fn libmonado_so(&self) -> Option<String> { pub fn libmonado_so(&self) -> Option<PathBuf> {
let mut res = format!("{}/lib/libmonado.so", self.prefix); let res = self.prefix.join("lib/libmonado.so");
if Path::new(&res).is_file() { if res.is_file() {
return Some(res); return Some(res);
} }
res = format!("{}/lib64/libmonado.so", self.prefix); let res = self.prefix.join("lib64/libmonado.so");
if Path::new(&res).is_file() { if res.is_file() {
return Some(res); return Some(res);
} }
@ -458,7 +459,7 @@ impl Profile {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::collections::HashMap; use std::{collections::HashMap, path::PathBuf};
use crate::profile::{ProfileFeature, ProfileFeatureType, ProfileFeatures, XRServiceType}; use crate::profile::{ProfileFeature, ProfileFeatureType, ProfileFeatures, XRServiceType};
@ -466,14 +467,17 @@ mod tests {
#[test] #[test]
fn profile_can_be_loaded() { fn profile_can_be_loaded() {
let profile = Profile::load_profile(&"./test/files/profile.json".to_string()); let profile = Profile::load_profile(&"./test/files/profile.json".into());
assert_eq!(profile.name, "Demo profile"); assert_eq!(profile.name, "Demo profile");
assert_eq!(profile.xrservice_path, "/home/user/monado"); assert_eq!(profile.xrservice_path, PathBuf::from("/home/user/monado"));
assert_eq!(profile.opencomposite_path, "/home/user/opencomposite");
assert_eq!(profile.prefix, "/home/user/envisionprefix");
assert_eq!( assert_eq!(
profile.features.libsurvive.path.as_deref(), profile.opencomposite_path,
Some("/home/user/libsurvive") PathBuf::from("/home/user/opencomposite")
);
assert_eq!(profile.prefix, PathBuf::from("/home/user/envisionprefix"));
assert_eq!(
profile.features.libsurvive.path,
Some(PathBuf::from("/home/user/libsurvive"))
); );
assert_eq!(profile.features.basalt.path, None); assert_eq!(profile.features.basalt.path, None);
assert_eq!(profile.features.libsurvive.enabled, true); assert_eq!(profile.features.libsurvive.enabled, true);
@ -496,14 +500,14 @@ mod tests {
let p = Profile { let p = Profile {
uuid: "demo".into(), uuid: "demo".into(),
name: "Demo profile".into(), name: "Demo profile".into(),
xrservice_path: String::from("/home/user/monado"), xrservice_path: PathBuf::from("/home/user/monado"),
xrservice_type: XRServiceType::Monado, xrservice_type: XRServiceType::Monado,
opencomposite_path: String::from("/home/user/opencomposite"), opencomposite_path: PathBuf::from("/home/user/opencomposite"),
features: ProfileFeatures { features: ProfileFeatures {
libsurvive: ProfileFeature { libsurvive: ProfileFeature {
feature_type: ProfileFeatureType::Libsurvive, feature_type: ProfileFeatureType::Libsurvive,
enabled: true, enabled: true,
path: Some(String::from("/home/user/libsurvive")), path: Some(PathBuf::from("/home/user/libsurvive")),
repo: None, repo: None,
branch: None, branch: None,
}, },
@ -512,17 +516,17 @@ mod tests {
mercury_enabled: false, mercury_enabled: false,
}, },
environment: env, environment: env,
prefix: String::from("/home/user/envisionprefix"), prefix: PathBuf::from("/home/user/envisionprefix"),
editable: true, editable: true,
..Default::default() ..Default::default()
}; };
let fpath = String::from("./target/testout/testprofile.json"); let fpath = PathBuf::from("./target/testout/testprofile.json");
p.dump_profile(&fpath); p.dump_profile(&fpath);
let loaded = Profile::load_profile(&fpath); let loaded = Profile::load_profile(&fpath);
assert_eq!(loaded.name, "Demo profile"); assert_eq!(loaded.name, "Demo profile");
assert_eq!( assert_eq!(
loaded.features.libsurvive.path, loaded.features.libsurvive.path,
Some(String::from("/home/user/libsurvive")) Some(PathBuf::from("/home/user/libsurvive"))
); );
assert_eq!( assert_eq!(
loaded loaded
@ -534,6 +538,6 @@ mod tests {
} }
} }
pub fn prepare_ld_library_path(prefix: &str) -> String { pub fn prepare_ld_library_path(prefix: &PathBuf) -> String {
format!("{pfx}/lib:{pfx}/lib64", pfx = prefix) format!("{pfx}/lib:{pfx}/lib64", pfx = prefix.to_string_lossy())
} }

View file

@ -7,7 +7,7 @@ use std::collections::HashMap;
pub fn lighthouse_profile() -> Profile { pub fn lighthouse_profile() -> Profile {
let data_dir = get_data_dir(); let data_dir = get_data_dir();
let prefix = format!("{data}/prefixes/lighthouse_default", data = data_dir); let prefix = data_dir.join("prefixes/lighthouse_default");
let mut environment: HashMap<String, String> = HashMap::new(); let mut environment: HashMap<String, String> = HashMap::new();
environment.insert("XRT_JSON_LOG".into(), "1".into()); environment.insert("XRT_JSON_LOG".into(), "1".into());
environment.insert("XRT_COMPOSITOR_SCALE_PERCENTAGE".into(), "140".into()); environment.insert("XRT_COMPOSITOR_SCALE_PERCENTAGE".into(), "140".into());

View file

@ -10,7 +10,7 @@ use std::collections::HashMap;
pub fn openhmd_profile() -> Profile { pub fn openhmd_profile() -> Profile {
let data_dir = get_data_dir(); let data_dir = get_data_dir();
let prefix = format!("{data}/prefixes/openhmd_default", data = data_dir); let prefix = data_dir.join("prefixes/openhmd_default");
let mut environment: HashMap<String, String> = HashMap::new(); let mut environment: HashMap<String, String> = HashMap::new();
environment.insert("XRT_JSON_LOG".into(), "1".into()); environment.insert("XRT_JSON_LOG".into(), "1".into());
environment.insert("XRT_COMPOSITOR_SCALE_PERCENTAGE".into(), "140".into()); environment.insert("XRT_COMPOSITOR_SCALE_PERCENTAGE".into(), "140".into());

View file

@ -7,7 +7,7 @@ use std::collections::HashMap;
pub fn simulated_profile() -> Profile { pub fn simulated_profile() -> Profile {
let data_dir = get_data_dir(); let data_dir = get_data_dir();
let prefix = format!("{data}/prefixes/simulated_default", data = data_dir); let prefix = data_dir.join("prefixes/simulated_default");
let mut environment: HashMap<String, String> = HashMap::new(); let mut environment: HashMap<String, String> = HashMap::new();
environment.insert("QWERTY_ENABLE".into(), "1".into()); environment.insert("QWERTY_ENABLE".into(), "1".into());
environment.insert("XRT_JSON_LOG".into(), "1".into()); environment.insert("XRT_JSON_LOG".into(), "1".into());
@ -18,7 +18,7 @@ pub fn simulated_profile() -> Profile {
environment.insert("U_PACING_APP_USE_MIN_FRAME_PERIOD".into(), "1".into()); environment.insert("U_PACING_APP_USE_MIN_FRAME_PERIOD".into(), "1".into());
environment.insert( environment.insert(
"LD_LIBRARY_PATH".into(), "LD_LIBRARY_PATH".into(),
format!("{pfx}/lib:{pfx}/lib64", pfx = prefix), format!("{pfx}/lib:{pfx}/lib64", pfx = prefix.to_string_lossy()),
); );
Profile { Profile {
uuid: "simulated-default".into(), uuid: "simulated-default".into(),

View file

@ -10,7 +10,7 @@ use std::collections::HashMap;
pub fn survive_profile() -> Profile { pub fn survive_profile() -> Profile {
let data_dir = get_data_dir(); let data_dir = get_data_dir();
let prefix = format!("{data}/prefixes/survive_default", data = data_dir); let prefix = data_dir.join("prefixes/survive_default");
let mut environment: HashMap<String, String> = HashMap::new(); let mut environment: HashMap<String, String> = HashMap::new();
environment.insert("XRT_JSON_LOG".into(), "1".into()); environment.insert("XRT_JSON_LOG".into(), "1".into());
environment.insert("XRT_COMPOSITOR_SCALE_PERCENTAGE".into(), "140".into()); environment.insert("XRT_COMPOSITOR_SCALE_PERCENTAGE".into(), "140".into());

View file

@ -7,7 +7,7 @@ use std::collections::HashMap;
pub fn wivrn_profile() -> Profile { pub fn wivrn_profile() -> Profile {
let data_dir = get_data_dir(); let data_dir = get_data_dir();
let prefix = format!("{data}/prefixes/wivrn_default", data = data_dir); let prefix = data_dir.join("prefixes/wivrn_default");
let mut environment: HashMap<String, String> = HashMap::new(); let mut environment: HashMap<String, String> = HashMap::new();
environment.insert("LD_LIBRARY_PATH".into(), prepare_ld_library_path(&prefix)); environment.insert("LD_LIBRARY_PATH".into(), prepare_ld_library_path(&prefix));
environment.insert("XRT_DEBUG_GUI".into(), "1".into()); environment.insert("XRT_DEBUG_GUI".into(), "1".into());

View file

@ -10,7 +10,7 @@ use std::collections::HashMap;
pub fn wmr_profile() -> Profile { pub fn wmr_profile() -> Profile {
let data_dir = get_data_dir(); let data_dir = get_data_dir();
let prefix = format!("{data}/prefixes/wmr_default", data = data_dir); let prefix = data_dir.join("prefixes/wmr_default");
let mut environment: HashMap<String, String> = HashMap::new(); let mut environment: HashMap<String, String> = HashMap::new();
environment.insert("XRT_JSON_LOG".into(), "1".into()); environment.insert("XRT_JSON_LOG".into(), "1".into());
environment.insert("XRT_COMPOSITOR_SCALE_PERCENTAGE".into(), "140".into()); environment.insert("XRT_COMPOSITOR_SCALE_PERCENTAGE".into(), "140".into());

View file

@ -1,26 +1,28 @@
use crate::{ use crate::{
file_utils::{copy_file, get_writer}, file_utils::{copy_file, get_writer},
paths::{get_backup_dir, get_home_dir, get_xdg_data_dir}, paths::{get_backup_dir, get_home_dir},
profile::Profile, profile::Profile,
}; };
use anyhow::bail; use anyhow::bail;
use std::{fs::read_to_string, io::Write, path::Path}; use std::{
fs::read_to_string,
io::Write,
path::{Path, PathBuf},
};
fn get_runtime_entrypoint_path() -> Option<String> { fn get_runtime_entrypoint_path() -> Option<PathBuf> {
vec![format!( vec![get_home_dir()
"{home}/.steam/steam/steamapps/common/SteamLinuxRuntime_sniper/_v2-entry-point", .join(".steam/steam/steamapps/common/SteamLinuxRuntime_sniper/_v2-entry-point")]
home = get_home_dir(),
)]
.iter() .iter()
.find(|path| Path::new(&path).is_file()) .find(|p| p.is_file())
.cloned() .cloned()
} }
fn get_backup_runtime_entrypoint_location() -> String { fn get_backup_runtime_entrypoint_location() -> PathBuf {
format!("{backup}/_v2-entry-point.bak", backup = get_backup_dir()) get_backup_dir().join("_v2-entry-point.bak")
} }
fn backup_runtime_entrypoint(path: &str) { fn backup_runtime_entrypoint(path: &PathBuf) {
copy_file(&path, &get_backup_runtime_entrypoint_location()); copy_file(&path, &get_backup_runtime_entrypoint_location());
} }
@ -33,7 +35,7 @@ pub fn restore_runtime_entrypoint() {
} }
} }
fn append_to_runtime_entrypoint(data: &str, path: &str) -> anyhow::Result<()> { fn append_to_runtime_entrypoint(data: &str, path: &PathBuf) -> anyhow::Result<()> {
let existing = read_to_string(path)?; let existing = read_to_string(path)?;
let new = existing.replace( let new = existing.replace(
"exec \"${here}/${run}\" \"$@\"\nexit 125", "exec \"${here}/${run}\" \"$@\"\nexit 125",

View file

@ -1,11 +1,7 @@
use crate::paths::get_home_dir; use crate::paths::get_home_dir;
use std::path::Path;
pub fn chaperone_info_exists() -> bool { pub fn chaperone_info_exists() -> bool {
let path_s = format!( get_home_dir()
"{}/.steam/steam/config/chaperone_info.vrchap", .join(".steam/steam/config/chaperone_info.vrchap")
get_home_dir() .is_file()
);
let path = Path::new(&path_s);
path.is_file()
} }

View file

@ -644,10 +644,7 @@ impl SimpleComponent for App {
println!("pkexec not found, skipping setcap"); println!("pkexec not found, skipping setcap");
} else { } else {
let profile = self.get_selected_profile(); let profile = self.get_selected_profile();
setcap_cap_sys_nice_eip(format!( setcap_cap_sys_nice_eip(&profile.prefix.join("bin/monado-service"));
"{pfx}/bin/monado-service",
pfx = profile.prefix
));
} }
} }
Msg::ProfileSelected(prof) => { Msg::ProfileSelected(prof) => {
@ -692,12 +689,12 @@ impl SimpleComponent for App {
self.application.quit(); self.application.quit();
} }
Msg::DebugOpenData => { Msg::DebugOpenData => {
open_with_default_handler(&format!("file://{}", get_data_dir())); open_with_default_handler(&format!("file://{}", get_data_dir().to_string_lossy()));
} }
Msg::DebugOpenPrefix => { Msg::DebugOpenPrefix => {
open_with_default_handler(&format!( open_with_default_handler(&format!(
"file://{}", "file://{}",
self.get_selected_profile().prefix self.get_selected_profile().prefix.to_string_lossy()
)); ));
} }
Msg::DebugCopyEnvVars => { Msg::DebugCopyEnvVars => {

View file

@ -196,14 +196,21 @@ impl InternalJobWorker {
match launch_opts.contains(LAUNCH_OPTS_CMD_PLACEHOLDER) { match launch_opts.contains(LAUNCH_OPTS_CMD_PLACEHOLDER) {
true => launch_opts.replacen( true => launch_opts.replacen(
LAUNCH_OPTS_CMD_PLACEHOLDER, LAUNCH_OPTS_CMD_PLACEHOLDER,
prof.xrservice_binary().as_str(), &prof.xrservice_binary().to_string_lossy().to_string(),
1, 1,
), ),
false => format!("{} {}", prof.xrservice_binary(), launch_opts), false => format!(
"{} {}",
prof.xrservice_binary().to_string_lossy(),
launch_opts
),
}, },
], ],
), ),
true => (prof.xrservice_binary(), vec![]), true => (
prof.xrservice_binary().to_string_lossy().to_string(),
vec![],
),
}; };
let data = CmdWorkerData { let data = CmdWorkerData {
environment: env, environment: env,

View file

@ -7,7 +7,13 @@ use crate::{
use adw::prelude::*; use adw::prelude::*;
use gtk::glib; use gtk::glib;
use relm4::prelude::*; use relm4::prelude::*;
use std::{cell::Cell, collections::HashMap, path::Path, rc::Rc, time::Duration}; use std::{
cell::Cell,
collections::HashMap,
path::{Path, PathBuf},
rc::Rc,
time::Duration,
};
const NO_FILE_MSG: &str = "(No file selected)"; const NO_FILE_MSG: &str = "(No file selected)";
const CALIBRATION_RUN_TIME_SECONDS: f64 = 30.0; const CALIBRATION_RUN_TIME_SECONDS: f64 = 30.0;
@ -57,7 +63,7 @@ pub enum LibsurviveSetupMsg {
} }
impl LibsurviveSetupWindow { impl LibsurviveSetupWindow {
fn create_calibration_runner(&mut self, survive_cli_path: String) -> CmdRunner { fn create_calibration_runner(&mut self, survive_cli_path: &PathBuf) -> CmdRunner {
let lh_path = self.steam_lighthouse_path.clone(); let lh_path = self.steam_lighthouse_path.clone();
let mut env = HashMap::new(); let mut env = HashMap::new();
let profile_prefix = &self.profile.as_ref().unwrap().prefix; let profile_prefix = &self.profile.as_ref().unwrap().prefix;
@ -67,7 +73,7 @@ impl LibsurviveSetupWindow {
); );
CmdRunner::new( CmdRunner::new(
Some(env), Some(env),
survive_cli_path, survive_cli_path.to_string_lossy().to_string(),
vec!["--steamvr-calibration".into(), lh_path], vec!["--steamvr-calibration".into(), lh_path],
) )
} }
@ -369,7 +375,7 @@ impl SimpleComponent for LibsurviveSetupWindow {
if let Some(survive_cli_path) = if let Some(survive_cli_path) =
self.profile.as_ref().unwrap().get_survive_cli_path() self.profile.as_ref().unwrap().get_survive_cli_path()
{ {
let mut runner = self.create_calibration_runner(survive_cli_path); let mut runner = self.create_calibration_runner(&survive_cli_path);
self.calibration_running.set(true); self.calibration_running.set(true);
self.first_run_done = false; self.first_run_done = false;
self.calibration_seconds_elapsed = 0.0; self.calibration_seconds_elapsed = 0.0;
@ -444,7 +450,8 @@ impl SimpleComponent for LibsurviveSetupWindow {
let runner = self.calibration_runner.as_mut().unwrap(); let runner = self.calibration_runner.as_mut().unwrap();
runner.terminate(); runner.terminate();
let mut n_runner = self.create_calibration_runner( let mut n_runner = self.create_calibration_runner(
self.profile &self
.profile
.as_ref() .as_ref()
.unwrap() .unwrap()
.get_survive_cli_path() .get_survive_cli_path()

View file

@ -188,13 +188,13 @@ impl SimpleComponent for MainView {
add_css_class: "card", add_css_class: "card",
add_css_class: "padded", add_css_class: "padded",
#[track = "model.changed(Self::selected_profile())"] #[track = "model.changed(Self::selected_profile())"]
set_visible: match mount_has_nosuid(model.selected_profile.prefix.as_str()) { set_visible: match mount_has_nosuid(&model.selected_profile.prefix) {
Ok(b) => b, Ok(b) => b,
Err(_) => { Err(_) => {
// TODO: handle this error better // TODO: handle this error better
println!( eprintln!(
"Warning: could not get stat on path {}", "Warning: could not get stat on path {}",
model.selected_profile.prefix); model.selected_profile.prefix.to_string_lossy());
false false
}, },
}, },

View file

@ -13,7 +13,7 @@ use crate::{
use adw::prelude::*; use adw::prelude::*;
use gtk::glib::clone; use gtk::glib::clone;
use relm4::{factory::AsyncFactoryVecDeque, prelude::*}; use relm4::{factory::AsyncFactoryVecDeque, prelude::*};
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, path::PathBuf, rc::Rc};
#[tracker::track] #[tracker::track]
pub struct ProfileEditor { pub struct ProfileEditor {
@ -110,10 +110,10 @@ impl SimpleComponent for ProfileEditor {
add: &path_row( add: &path_row(
"Install Prefix", "Install Prefix",
None, None,
Some(model.profile.borrow().prefix.clone()), Some(model.profile.borrow().prefix.to_string_lossy().to_string()),
Some(init.root_win.clone()), Some(init.root_win.clone()),
clone!(@strong prof => move |n_path| { clone!(@strong prof => move |n_path| {
prof.borrow_mut().prefix = n_path.unwrap_or_default() prof.borrow_mut().prefix = n_path.unwrap_or_default().into();
}), }),
), ),
add: &entry_row("Autostart Command", add: &entry_row("Autostart Command",
@ -171,10 +171,10 @@ impl SimpleComponent for ProfileEditor {
add: &path_row( add: &path_row(
"XR Service Path", "XR Service Path",
None, None,
Some(model.profile.borrow().xrservice_path.clone()), Some(model.profile.borrow().xrservice_path.clone().to_string_lossy().to_string()),
Some(init.root_win.clone()), Some(init.root_win.clone()),
clone!(@strong prof => move |n_path| { clone!(@strong prof => move |n_path| {
prof.borrow_mut().xrservice_path = n_path.unwrap_or_default() prof.borrow_mut().xrservice_path = n_path.unwrap_or_default().into();
}), }),
), ),
add: &entry_row( add: &entry_row(
@ -200,10 +200,10 @@ impl SimpleComponent for ProfileEditor {
set_description: Some("OpenVR driver built on top of OpenXR"), set_description: Some("OpenVR driver built on top of OpenXR"),
add: &path_row( add: &path_row(
"OpenComposite Path", None, "OpenComposite Path", None,
Some(model.profile.borrow().opencomposite_path.clone()), Some(model.profile.borrow().opencomposite_path.clone().to_string_lossy().to_string()),
Some(init.root_win.clone()), Some(init.root_win.clone()),
clone!(@strong prof => move |n_path| { clone!(@strong prof => move |n_path| {
prof.borrow_mut().opencomposite_path = n_path.unwrap_or_default(); prof.borrow_mut().opencomposite_path = n_path.unwrap_or_default().into();
}) })
), ),
add: &entry_row( add: &entry_row(
@ -236,10 +236,10 @@ impl SimpleComponent for ProfileEditor {
), ),
add: &path_row( add: &path_row(
"Libsurvive Path", None, "Libsurvive Path", None,
model.profile.borrow().features.libsurvive.path.clone(), model.profile.borrow().features.libsurvive.path.clone().map(|p| p.to_string_lossy().to_string()),
Some(init.root_win.clone()), Some(init.root_win.clone()),
clone!(@strong prof => move |n_path| { clone!(@strong prof => move |n_path| {
prof.borrow_mut().features.libsurvive.path = n_path; prof.borrow_mut().features.libsurvive.path = n_path.map(PathBuf::from);
}) })
), ),
add: &entry_row( add: &entry_row(
@ -272,10 +272,10 @@ impl SimpleComponent for ProfileEditor {
), ),
add: &path_row( add: &path_row(
"OpenHMD Path", None, "OpenHMD Path", None,
model.profile.borrow().features.openhmd.path.clone(), model.profile.borrow().features.openhmd.path.clone().map(|p| p.to_string_lossy().to_string()),
Some(init.root_win.clone()), Some(init.root_win.clone()),
clone!(@strong prof => move |n_path| { clone!(@strong prof => move |n_path| {
prof.borrow_mut().features.openhmd.path = n_path; prof.borrow_mut().features.openhmd.path = n_path.map(PathBuf::from);
}) })
), ),
add: &entry_row( add: &entry_row(
@ -308,10 +308,10 @@ impl SimpleComponent for ProfileEditor {
), ),
add: &path_row( add: &path_row(
"Basalt Path", None, "Basalt Path", None,
model.profile.borrow().features.basalt.path.clone(), model.profile.borrow().features.basalt.path.clone().map(|p| p.to_string_lossy().to_string()),
Some(init.root_win.clone()), Some(init.root_win.clone()),
clone!(@strong prof => move |n_path| { clone!(@strong prof => move |n_path| {
prof.borrow_mut().features.basalt.path = n_path; prof.borrow_mut().features.basalt.path = n_path.map(PathBuf::from);
}) })
), ),
add: &entry_row( add: &entry_row(

View file

@ -5,7 +5,6 @@ use super::job_worker::{
}; };
use crate::paths::get_steamvr_bin_dir_path; use crate::paths::get_steamvr_bin_dir_path;
use relm4::{ use relm4::{
binding::{Binding, ConnectBinding, StringBinding},
gtk::{self, prelude::*}, gtk::{self, prelude::*},
ComponentParts, ComponentSender, RelmWidgetExt, SimpleComponent, ComponentParts, ComponentSender, RelmWidgetExt, SimpleComponent,
}; };
@ -145,7 +144,7 @@ impl SimpleComponent for SteamVrCalibrationBox {
} }
Self::Input::RunCalibration => { Self::Input::RunCalibration => {
self.set_calibration_result(None); self.set_calibration_result(None);
let steamvr_bin_dir = get_steamvr_bin_dir_path(); let steamvr_bin_dir = get_steamvr_bin_dir_path().to_string_lossy().to_string();
if !Path::new(&steamvr_bin_dir).is_dir() { if !Path::new(&steamvr_bin_dir).is_dir() {
self.set_calibration_success(false); self.set_calibration_success(false);
self.set_calibration_result(Some("SteamVR not found".into())); self.set_calibration_result(Some("SteamVR not found".into()));
@ -154,7 +153,7 @@ impl SimpleComponent for SteamVrCalibrationBox {
let mut env: HashMap<String, String> = HashMap::new(); let mut env: HashMap<String, String> = HashMap::new();
env.insert("LD_LIBRARY_PATH".into(), steamvr_bin_dir.clone()); env.insert("LD_LIBRARY_PATH".into(), steamvr_bin_dir.clone());
let vrcmd = format!("{steamvr_bin_dir}/vrcmd"); let vrcmd = format!("{steamvr_bin_dir}/vrcmd");
let mut server_worker = { let server_worker = {
let mut jobs: VecDeque<WorkerJob> = VecDeque::new(); let mut jobs: VecDeque<WorkerJob> = VecDeque::new();
jobs.push_back(WorkerJob::new_cmd( jobs.push_back(WorkerJob::new_cmd(
Some(env.clone()), Some(env.clone()),
@ -166,7 +165,7 @@ impl SimpleComponent for SteamVrCalibrationBox {
JobWorkerOut::Exit(code) => Self::Input::OnServerWorkerExit(code), JobWorkerOut::Exit(code) => Self::Input::OnServerWorkerExit(code),
}) })
}; };
let mut cal_worker = { let cal_worker = {
let mut jobs: VecDeque<WorkerJob> = VecDeque::new(); let mut jobs: VecDeque<WorkerJob> = VecDeque::new();
jobs.push_back(WorkerJob::new_func(Box::new(move || { jobs.push_back(WorkerJob::new_func(Box::new(move || {
sleep(Duration::from_secs(2)); sleep(Duration::from_secs(2));

7
src/xdg.rs Normal file
View file

@ -0,0 +1,7 @@
use lazy_static::lazy_static;
use xdg::BaseDirectories;
lazy_static! {
pub static ref XDG: BaseDirectories =
BaseDirectories::new().expect("Failed to get XDG base directories");
}