diff --git a/Cargo.lock b/Cargo.lock index 38d9964..18d5eca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -289,6 +289,7 @@ dependencies = [ "tracker", "uuid", "vte4", + "xdg", ] [[package]] @@ -2528,3 +2529,9 @@ dependencies = [ "cfg-if", "windows-sys 0.48.0", ] + +[[package]] +name = "xdg" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" diff --git a/Cargo.toml b/Cargo.toml index eb589e4..eb6e87d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,3 +40,4 @@ serde_json = "1.0.106" tracker = "0.2.1" uuid = { version = "1.8.0", features = ["v4", "fast-rng"] } vte4 = { version = "0.7.1", features = ["v0_72"] } +xdg = "2.5.2" diff --git a/src/adb.rs b/src/adb.rs index 70ea6fd..4f23a0f 100644 --- a/src/adb.rs +++ b/src/adb.rs @@ -1,12 +1,12 @@ -use crate::cmd_runner::CmdRunner; -use std::path::Path; +use std::path::PathBuf; -pub fn get_adb_install_runner(apk_path: &String) -> CmdRunner { - let path = Path::new(apk_path); +use crate::cmd_runner::CmdRunner; + +pub fn get_adb_install_runner(path: &PathBuf) -> CmdRunner { path.try_exists().expect("APK file provided does not exist"); CmdRunner::new( None, "adb".into(), - vec!["install".into(), path.to_str().unwrap().to_string()], + vec!["install".into(), path.to_string_lossy().to_string()], ) } diff --git a/src/build_tools/cmake.rs b/src/build_tools/cmake.rs index 1f3d39b..7c37855 100644 --- a/src/build_tools/cmake.rs +++ b/src/build_tools/cmake.rs @@ -1,10 +1,10 @@ use crate::ui::job_worker::job::WorkerJob; -use std::collections::HashMap; +use std::{collections::HashMap, path::PathBuf}; #[derive(Debug, Clone)] pub struct Cmake { - pub build_dir: String, - pub source_dir: String, + pub build_dir: PathBuf, + pub source_dir: PathBuf, pub vars: Option>, pub env: Option>, } @@ -13,7 +13,7 @@ impl Cmake { pub fn get_prepare_job(&self) -> WorkerJob { let mut args = vec![ "-B".into(), - self.build_dir.clone(), + self.build_dir.to_string_lossy().to_string(), "-G".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)) } @@ -37,7 +37,10 @@ impl Cmake { WorkerJob::new_cmd( self.env.clone(), "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( self.env.clone(), "cmake".into(), - Some(vec!["--install".into(), self.build_dir.clone()]), + Some(vec![ + "--install".into(), + self.build_dir.to_string_lossy().to_string(), + ]), ) } } diff --git a/src/build_tools/git.rs b/src/build_tools/git.rs index 8476938..eb5a90c 100644 --- a/src/build_tools/git.rs +++ b/src/build_tools/git.rs @@ -1,17 +1,17 @@ use crate::ui::job_worker::job::{FuncWorkerOut, WorkerJob}; use git2::Repository; -use std::path::Path; +use std::path::PathBuf; #[derive(Debug, Clone)] pub struct Git { pub repo: String, - pub dir: String, + pub dir: PathBuf, pub branch: String, } impl Git { fn cmd(&self, args: Vec) -> 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); WorkerJob::new_cmd(None, "git".into(), Some(nargs)) } @@ -81,7 +81,7 @@ impl Git { Some(vec![ "clone".into(), self.get_repo(), - self.dir.clone(), + self.dir.to_string_lossy().to_string(), "--recurse-submodules".into(), ]), ) @@ -101,8 +101,7 @@ impl Git { } pub fn clone_exists(&self) -> bool { - let path_s = format!("{}/.git", self.dir.clone()); - Path::new(&path_s).is_dir() + self.dir.join(".git").is_dir() } pub fn get_pre_build_jobs(&self, pull_on_build: bool) -> Vec { diff --git a/src/builders/build_basalt.rs b/src/builders/build_basalt.rs index 237fc24..94f7b1c 100644 --- a/src/builders/build_basalt.rs +++ b/src/builders/build_basalt.rs @@ -4,10 +4,7 @@ use crate::{ profile::Profile, ui::job_worker::job::WorkerJob, }; -use std::{ - collections::{HashMap, VecDeque}, - path::Path, -}; +use std::collections::{HashMap, VecDeque}; pub fn get_build_basalt_jobs(profile: &Profile, clean_build: bool) -> VecDeque { let mut jobs = VecDeque::::new(); @@ -32,16 +29,19 @@ pub fn get_build_basalt_jobs(profile: &Profile, clean_build: bool) -> VecDeque = HashMap::new(); cmake_vars.insert("CMAKE_EXPORT_COMPILE_COMMANDS".into(), "ON".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("BASALT_INSTANTIATIONS_DOUBLE".into(), "OFF".into()); cmake_vars.insert( "CMAKE_INSTALL_LIBDIR".into(), - format!("{}/lib", profile.prefix), + profile.prefix.join("lib").to_string_lossy().to_string(), ); let mut cmake_env: HashMap = HashMap::new(); @@ -56,7 +56,7 @@ pub fn get_build_basalt_jobs(profile: &Profile, clean_build: bool) -> VecDeque VecDeque VecDeque VecDeq jobs.extend(git.get_pre_build_jobs(profile.pull_on_build)); - let build_dir = format!( - "{}/build", - profile.features.libsurvive.path.as_ref().unwrap() - ); + let build_dir = profile + .features + .libsurvive + .path + .as_ref() + .unwrap() + .join("build"); let mut cmake_vars: HashMap = HashMap::new(); cmake_vars.insert("CMAKE_EXPORT_COMPILE_COMMANDS".into(), "ON".into()); cmake_vars.insert("CMAKE_BUILD_TYPE".into(), "RelWithDebInfo".into()); cmake_vars.insert("ENABLE_api_example".into(), "OFF".into()); cmake_vars.insert("USE_HIDAPI".into(), "ON".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_INSTALL_LIBDIR".into(), - format!("{}/lib", profile.prefix), + profile.prefix.join("lib").to_string_lossy().to_string(), ); let cmake = Cmake { diff --git a/src/builders/build_mercury.rs b/src/builders/build_mercury.rs index 43131a4..dd501b9 100644 --- a/src/builders/build_mercury.rs +++ b/src/builders/build_mercury.rs @@ -5,10 +5,13 @@ use crate::{ pub fn get_build_mercury_job(profile: &Profile) -> WorkerJob { WorkerJob::new_cmd( None, - format!( - "{sysdata}/scripts/build_mercury.sh", - sysdata = pkg_data_dir() - ), - Some(vec![profile.prefix.clone(), get_cache_dir()]), + pkg_data_dir() + .join("scripts/build_mercury.sh") + .to_string_lossy() + .to_string(), + Some(vec![ + profile.prefix.to_string_lossy().to_string(), + get_cache_dir().to_string_lossy().to_string(), + ]), ) } diff --git a/src/builders/build_monado.rs b/src/builders/build_monado.rs index fb049c5..2266c9d 100644 --- a/src/builders/build_monado.rs +++ b/src/builders/build_monado.rs @@ -28,25 +28,41 @@ pub fn get_build_monado_jobs(profile: &Profile, clean_build: bool) -> VecDeque = HashMap::new(); env.insert( "PKG_CONFIG_PATH".into(), - format!("{}/lib/pkgconfig", profile.prefix), + profile + .prefix + .join("lib/pkgconfig") + .to_string_lossy() + .to_string(), ); let mut cmake_vars: HashMap = HashMap::new(); cmake_vars.insert("CMAKE_EXPORT_COMPILE_COMMANDS".into(), "ON".into()); cmake_vars.insert("CMAKE_BUILD_TYPE".into(), "RelWithDebInfo".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_INSTALL_PREFIX".into(), profile.prefix.clone()); + cmake_vars.insert( + "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_C_FLAGS".into(), - format!("-Wl,-rpath='{}/lib'", profile.prefix,), + format!( + "-Wl,-rpath='{}/lib'", + profile.prefix.to_string_lossy().to_string(), + ), ); cmake_vars.insert( "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)| { if k == "CMAKE_C_FLAGS" || k == "CMAKE_CXX_FLAGS" { diff --git a/src/builders/build_opencomposite.rs b/src/builders/build_opencomposite.rs index bf45567..84d8125 100644 --- a/src/builders/build_opencomposite.rs +++ b/src/builders/build_opencomposite.rs @@ -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)); - let build_dir = format!("{}/build", profile.opencomposite_path); + let build_dir = profile.opencomposite_path.join("build"); let mut cmake_vars: HashMap = HashMap::new(); cmake_vars.insert("CMAKE_EXPORT_COMPILE_COMMANDS".into(), "ON".into()); cmake_vars.insert("CMAKE_BUILD_TYPE".into(), "RelWithDebInfo".into()); diff --git a/src/builders/build_openhmd.rs b/src/builders/build_openhmd.rs index 5fe612c..1573816 100644 --- a/src/builders/build_openhmd.rs +++ b/src/builders/build_openhmd.rs @@ -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)); - 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 = HashMap::new(); cmake_vars.insert("CMAKE_EXPORT_COMPILE_COMMANDS".into(), "ON".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_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()); diff --git a/src/builders/build_wivrn.rs b/src/builders/build_wivrn.rs index 52bfb66..5859875 100644 --- a/src/builders/build_wivrn.rs +++ b/src/builders/build_wivrn.rs @@ -28,13 +28,16 @@ pub fn get_build_wivrn_jobs(profile: &Profile, clean_build: bool) -> VecDeque = HashMap::new(); cmake_vars.insert("CMAKE_EXPORT_COMPILE_COMMANDS".into(), "ON".into()); cmake_vars.insert("CMAKE_BUILD_TYPE".into(), "RelWithDebInfo".into()); cmake_vars.insert("XRT_HAVE_SYSTEM_CJSON".into(), "NO".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)| { cmake_vars.insert(k.clone(), v.clone()); diff --git a/src/cmd_runner.rs b/src/cmd_runner.rs index c0ab455..efd1f11 100644 --- a/src/cmd_runner.rs +++ b/src/cmd_runner.rs @@ -14,6 +14,7 @@ use crate::{ use std::{ collections::HashMap, io::{BufRead, BufReader, Write}, + path::PathBuf, process::{Child, Command, Stdio}, sync::{ mpsc::{sync_channel, Receiver, SyncSender}, @@ -99,9 +100,11 @@ impl CmdRunner { Self::new( Some(env), match profile.xrservice_type { - XRServiceType::Monado => format!("{pfx}/bin/monado-service", pfx = profile.prefix), - XRServiceType::Wivrn => format!("{pfx}/bin/wivrn-server", pfx = profile.prefix), - }, + XRServiceType::Monado => profile.prefix.join("bin/monado-service"), + XRServiceType::Wivrn => profile.prefix.join("bin/wivrn-server"), + } + .to_string_lossy() + .to_string(), vec![], ) } @@ -171,13 +174,13 @@ impl CmdRunner { } } - fn save_log(path_s: String, log: &[String]) -> Result<(), std::io::Error> { - let mut writer = get_writer(&path_s).map_err(std::io::Error::other)?; + fn save_log(path: &PathBuf, log: &[String]) -> Result<(), std::io::Error> { + let mut writer = get_writer(path).map_err(std::io::Error::other)?; let log_s = log.concat(); 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) } } @@ -251,14 +254,14 @@ mod tests { } runner - .save_output("./target/testout/testlog".into()) + .save_output(&"./target/testout/testlog".into()) .expect("Failed to save output file"); } #[test] fn can_create_from_profile() { CmdRunner::xrservice_runner_from_profile(&Profile::load_profile( - &"./test/files/profile.json".to_string(), + &"./test/files/profile.json".into(), )); } } diff --git a/src/config.rs b/src/config.rs index 1e5578d..542c9f6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,3 +1,5 @@ +use serde::{de::Error, Deserialize, Serialize}; + use crate::{ constants::CMD_NAME, device_prober::get_xr_usb_devices, @@ -9,8 +11,7 @@ use crate::{ survive::survive_profile, wivrn::wivrn_profile, wmr::wmr_profile, }, }; -use serde::{ser::Error, Deserialize, Serialize}; -use std::{fs::File, io::BufReader}; +use std::{fs::File, io::BufReader, path::PathBuf}; fn default_win_size() -> [i32; 2] { [360, 400] @@ -56,16 +57,12 @@ impl Config { } } - pub fn config_file_path() -> String { - format!( - "{config}/{name}.json", - config = get_config_dir(), - name = CMD_NAME - ) + pub fn config_file_path() -> PathBuf { + get_config_dir().join(format!("{CMD_NAME}.json")) } - fn from_path(path_s: String) -> Self { - match File::open(path_s) { + fn from_path(path: &PathBuf) -> Self { + match File::open(path) { Ok(file) => { let reader = BufReader::new(file); match serde_json::from_reader(reader) { @@ -77,8 +74,8 @@ impl Config { } } - fn save_to_path(&self, path_s: &String) -> Result<(), serde_json::Error> { - let writer = get_writer(path_s).map_err(serde_json::Error::custom)?; + fn save_to_path(&self, path: &PathBuf) -> Result<(), serde_json::Error> { + let writer = get_writer(path).map_err(serde_json::Error::custom)?; serde_json::to_writer_pretty(writer, self) } @@ -88,7 +85,7 @@ impl Config { } 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]) { @@ -117,7 +114,7 @@ mod tests { #[test] fn will_load_default_if_config_does_not_exist() { 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 ) } diff --git a/src/constants.rs.in b/src/constants.rs.in index a77737c..08cf2fe 100644 --- a/src/constants.rs.in +++ b/src/constants.rs.in @@ -1,5 +1,7 @@ use crate::paths::get_exec_prefix; +use std::path::PathBuf; + pub const APP_NAME: &str = "@PRETTY_NAME@"; pub const APP_ID: &str = "@APP_ID@"; pub const RESOURCES_BASE_PATH: &str = "@RESOURCES_BASE_PATH@"; @@ -17,10 +19,10 @@ pub fn get_developers() -> Vec { vec!["Gabriele Musco ".to_string()] } -pub fn pkg_data_dir() -> String { - format!("{}/share/{}", get_exec_prefix(), CMD_NAME) +pub fn pkg_data_dir() -> PathBuf { + get_exec_prefix().join("share").join(CMD_NAME) } pub fn resources() -> String { - format!("{}/resources.gresource", pkg_data_dir()) + format!("{}/resources.gresource", pkg_data_dir().to_string_lossy().to_string()) } diff --git a/src/downloader.rs b/src/downloader.rs index 0909a9b..4c01c8d 100644 --- a/src/downloader.rs +++ b/src/downloader.rs @@ -3,7 +3,7 @@ use reqwest::{ header::{HeaderMap, USER_AGENT}, Method, }; -use std::{io::prelude::*, thread::JoinHandle}; +use std::{io::prelude::*, path::PathBuf, thread::JoinHandle}; use std::{thread, time::Duration}; const TIMEOUT: Duration = Duration::from_secs(60); @@ -23,7 +23,7 @@ fn client() -> reqwest::blocking::Client { .expect("Failed to build reqwest::Client") } -pub fn download_file(url: String, path: String) -> JoinHandle> { +pub fn download_file(url: String, path: PathBuf) -> JoinHandle> { thread::spawn(move || { let client = client(); match client.request(Method::GET, url).send() { diff --git a/src/file_builders/active_runtime_json.rs b/src/file_builders/active_runtime_json.rs index 46fef6a..9e583b7 100644 --- a/src/file_builders/active_runtime_json.rs +++ b/src/file_builders/active_runtime_json.rs @@ -1,7 +1,8 @@ use crate::{ 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}, + xdg::XDG, }; use serde::{ser::Error, Deserialize, Serialize}; use std::path::{Path, PathBuf}; @@ -10,9 +11,9 @@ use std::path::{Path, PathBuf}; pub struct ActiveRuntimeInnerRuntime { #[serde(rename = "VALVE_runtime_is_steamvr")] pub valve_runtime_is_steamvr: Option, - pub library_path: String, #[serde(rename = "MND_libmonado_path")] pub libmonado_path: Option, + pub library_path: PathBuf, pub name: Option, } @@ -22,26 +23,20 @@ pub struct ActiveRuntime { pub runtime: ActiveRuntimeInnerRuntime, } -pub fn get_openxr_conf_dir() -> String { - format!("{config}/openxr", config = get_xdg_config_dir()) +pub fn get_openxr_conf_dir() -> PathBuf { + XDG.get_config_home().join("openxr") } -fn get_active_runtime_json_path() -> String { - format!( - "{config}/1/active_runtime.json", - config = get_openxr_conf_dir() - ) +fn get_active_runtime_json_path() -> PathBuf { + get_openxr_conf_dir().join("1/active_runtime.json") } pub fn is_steam(active_runtime: &ActiveRuntime) -> bool { matches!(active_runtime.runtime.valve_runtime_is_steamvr, Some(true)) } -fn get_backup_steam_active_runtime_path() -> String { - format!( - "{backup}/active_runtime.json.steam.bak", - backup = get_backup_dir() - ) +fn get_backup_steam_active_runtime_path() -> PathBuf { + get_backup_dir().join("active_runtime.json.steam.bak") } fn get_backed_up_steam_active_runtime() -> Option { @@ -59,8 +54,8 @@ fn backup_steam_active_runtime() { } } -fn get_active_runtime_from_path(path_s: &String) -> Option { - deserialize_file(path_s) +fn get_active_runtime_from_path(path: &PathBuf) -> Option { + deserialize_file(path) } pub fn get_current_active_runtime() -> Option { @@ -69,9 +64,9 @@ pub fn get_current_active_runtime() -> Option { fn dump_active_runtime_to_path( active_runtime: &ActiveRuntime, - path_s: &String, + 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, active_runtime) } @@ -89,11 +84,10 @@ fn build_steam_active_runtime() -> ActiveRuntime { file_format_version: "1.0.0".into(), runtime: ActiveRuntimeInnerRuntime { 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, + library_path: XDG + .get_data_home() + .join("Steam/steamapps/common/SteamVR/bin/linux64/vrclient.so"), name: Some("SteamVR".into()), }, } @@ -144,26 +138,25 @@ pub fn build_profile_active_runtime(profile: &Profile) -> ActiveRuntime { name: None, valve_runtime_is_steamvr: None, libmonado_path: monado_so, - library_path: oxr_so.to_string_lossy().into_owned(), + library_path: oxr_so, }, } } // 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 path = Path::new(dest); let mut rel_chain = path .components() .map(|_| String::from("..")) .collect::>(); rel_chain.pop(); rel_chain.pop(); - res.runtime.library_path = format!( + res.runtime.library_path = PathBuf::from(format!( "{rels}{fullpath}", rels = rel_chain.join("/"), - fullpath = ar.runtime.library_path - ); + fullpath = ar.runtime.library_path.to_string_lossy() + )); res } @@ -174,7 +167,7 @@ pub fn set_current_active_runtime_to_profile(profile: &Profile) -> anyhow::Resul let pfx = profile.clone().prefix; let mut ar = build_profile_active_runtime(profile); // 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); } dump_current_active_runtime(&ar)?; @@ -184,6 +177,8 @@ pub fn set_current_active_runtime_to_profile(profile: &Profile) -> anyhow::Resul #[cfg(test)] mod tests { + use std::path::PathBuf; + use super::{ dump_active_runtime_to_path, get_active_runtime_from_path, relativize_active_runtime_lib_path, ActiveRuntime, ActiveRuntimeInnerRuntime, @@ -197,7 +192,9 @@ mod tests { assert!(ar.runtime.valve_runtime_is_steamvr.unwrap()); assert_eq!( 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"); } @@ -232,10 +229,14 @@ mod tests { }; let relativized = relativize_active_runtime_lib_path( &ar, - "/home/user/.config/openxr/1/active_runtime.json", + &PathBuf::from("/home/user/.config/openxr/1/active_runtime.json"), ); assert_eq!( - relativized.runtime.library_path, + relativized + .runtime + .library_path + .to_string_lossy() + .to_string(), "../../../../../usr/lib64/libopenxr_monado.so" ); } diff --git a/src/file_builders/monado_autorun.rs b/src/file_builders/monado_autorun.rs index 7eb3b05..588ae1e 100644 --- a/src/file_builders/monado_autorun.rs +++ b/src/file_builders/monado_autorun.rs @@ -1,6 +1,8 @@ +use std::path::PathBuf; + use crate::{ file_utils::{deserialize_file, get_writer}, - paths::get_xdg_config_dir, + xdg::XDG, }; use serde::{Deserialize, Serialize}; @@ -28,15 +30,12 @@ impl Default for MonadoAutorunConfig { } } -fn get_monado_autorun_config_path() -> String { - format!( - "{config}/monado/autorun_v0.json", - config = get_xdg_config_dir() - ) +fn get_monado_autorun_config_path() -> PathBuf { + XDG.get_config_home().join("monado/autorun_v0.json") } -fn get_monado_autorun_config_from_path(path_s: &String) -> Option { - deserialize_file(path_s) +fn get_monado_autorun_config_from_path(path: &PathBuf) -> Option { + deserialize_file(path) } pub fn get_monado_autorun_config() -> MonadoAutorunConfig { @@ -44,8 +43,8 @@ pub fn get_monado_autorun_config() -> MonadoAutorunConfig { .unwrap_or(MonadoAutorunConfig::default()) } -fn dump_monado_autorun_config_to_path(config: &MonadoAutorunConfig, path_s: &String) { - let writer = get_writer(path_s).expect("Unable to save Monado Autorun config"); +fn dump_monado_autorun_config_to_path(config: &MonadoAutorunConfig, path: &PathBuf) { + 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"); } diff --git a/src/file_builders/openvrpaths_vrpath.rs b/src/file_builders/openvrpaths_vrpath.rs index 28343ba..49459a2 100644 --- a/src/file_builders/openvrpaths_vrpath.rs +++ b/src/file_builders/openvrpaths_vrpath.rs @@ -1,43 +1,41 @@ +use std::path::PathBuf; + use crate::{ 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, + xdg::XDG, }; use serde::{ser::Error, Deserialize, Serialize}; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct OpenVrPaths { - config: Vec, + config: Vec, external_drivers: Option>, // never seen it populated jsonid: String, - log: Vec, - runtime: Vec, + log: Vec, + runtime: Vec, version: u32, } -pub fn get_openvr_conf_dir() -> String { - format!("{config}/openvr", config = get_xdg_config_dir()) +pub fn get_openvr_conf_dir() -> PathBuf { + XDG.get_config_home().join("openvr") } -fn get_openvrpaths_vrpath_path() -> String { - format!( - "{config}/openvrpaths.vrpath", - config = get_openvr_conf_dir() - ) +fn get_openvrpaths_vrpath_path() -> PathBuf { + get_openvr_conf_dir().join("openvrpaths.vrpath") } pub fn is_steam(ovr_paths: &OpenVrPaths) -> bool { ovr_paths.runtime.iter().any(|rt| { - rt.to_lowercase() + rt.to_string_lossy() + .to_lowercase() .ends_with("/steam/steamapps/common/steamvr") }) } -fn get_backup_steam_openvrpaths_path() -> String { - format!( - "{backup}/openvrpaths.vrpath.steam.bak", - backup = get_backup_dir() - ) +fn get_backup_steam_openvrpaths_path() -> PathBuf { + get_backup_dir().join("openvrpaths.vrpath.steam.bak") } fn get_backed_up_steam_openvrpaths() -> Option { @@ -55,8 +53,8 @@ fn backup_steam_openvrpaths() { } } -fn get_openvrpaths_from_path(path_s: &String) -> Option { - deserialize_file(path_s) +fn get_openvrpaths_from_path(path: &PathBuf) -> Option { + deserialize_file(path) } pub fn get_current_openvrpaths() -> Option { @@ -65,9 +63,9 @@ pub fn get_current_openvrpaths() -> Option { fn dump_openvrpaths_to_path( ovr_paths: &OpenVrPaths, - path_s: &String, + 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, ovr_paths) } @@ -79,16 +77,13 @@ fn build_steam_openvrpaths() -> OpenVrPaths { if let Some(backup) = get_backed_up_steam_openvrpaths() { return backup; } - let datadir = get_xdg_data_dir(); + let datadir = XDG.get_data_home(); OpenVrPaths { - config: vec![format!("{data}/Steam/config", data = datadir)], + config: vec![datadir.join("Steam/config")], external_drivers: None, jsonid: "vrpathreg".into(), - log: vec![format!("{data}/Steam/logs", data = datadir)], - runtime: vec![format!( - "{data}/Steam/steamapps/common/SteamVR", - data = datadir - )], + log: vec![datadir.join("Steam/logs")], + runtime: vec![datadir.join("Steam/steamapps/common/SteamVR")], version: 1, } } @@ -100,16 +95,13 @@ pub fn set_current_openvrpaths_to_steam() -> anyhow::Result<()> { } pub fn build_profile_openvrpaths(profile: &Profile) -> OpenVrPaths { - let datadir = get_xdg_data_dir(); + let datadir = XDG.get_data_home(); OpenVrPaths { - config: vec![format!("{data}/Steam/config", data = datadir)], + config: vec![datadir.join("Steam/config")], external_drivers: None, jsonid: "vrpathreg".into(), - log: vec![format!("{data}/Steam/logs", data = datadir)], - runtime: vec![format!( - "{opencomp_dir}/build", - opencomp_dir = profile.opencomposite_path - )], + log: vec![datadir.join("Steam/logs")], + runtime: vec![profile.opencomposite_path.join("build")], version: 1, } } @@ -125,6 +117,8 @@ pub fn set_current_openvrpaths_to_profile(profile: &Profile) -> anyhow::Result<( #[cfg(test)] mod tests { + use std::path::PathBuf; + use super::{dump_openvrpaths_to_path, get_openvrpaths_from_path, OpenVrPaths}; #[test] @@ -133,7 +127,7 @@ mod tests { assert_eq!(ovrp.config.len(), 1); assert_eq!( 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.jsonid, "vrpathreg"); diff --git a/src/file_builders/wivrn_config.rs b/src/file_builders/wivrn_config.rs index d27828a..ac41fe0 100644 --- a/src/file_builders/wivrn_config.rs +++ b/src/file_builders/wivrn_config.rs @@ -1,9 +1,9 @@ use crate::{ file_utils::{deserialize_file, get_writer}, - paths::get_xdg_config_dir, + xdg::XDG, }; 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)] #[serde(rename_all = "lowercase")] @@ -123,20 +123,20 @@ impl Default for WivrnConfig { } } -fn get_wivrn_config_path() -> String { - format!("{config}/wivrn/config.json", config = get_xdg_config_dir()) +fn get_wivrn_config_path() -> PathBuf { + XDG.get_config_home().join("wivrn/config.json") } -fn get_wivrn_config_from_path(path_s: &String) -> Option { - deserialize_file(path_s) +fn get_wivrn_config_from_path(path: &PathBuf) -> Option { + deserialize_file(path) } pub fn get_wivrn_config() -> WivrnConfig { get_wivrn_config_from_path(&get_wivrn_config_path()).unwrap_or(WivrnConfig::default()) } -fn dump_wivrn_config_to_path(config: &WivrnConfig, path_s: &String) { - let writer = get_writer(path_s).expect("Unable to save WiVRn config"); +fn dump_wivrn_config_to_path(config: &WivrnConfig, path: &PathBuf) { + let writer = get_writer(path).expect("Unable to save WiVRn config"); serde_json::to_writer_pretty(writer, config).expect("Unable to save WiVRn config"); } diff --git a/src/file_utils.rs b/src/file_utils.rs index 9e322d4..9d262a9 100644 --- a/src/file_utils.rs +++ b/src/file_utils.rs @@ -6,11 +6,10 @@ use nix::{ use std::{ fs::{self, copy, create_dir_all, remove_dir_all, File, OpenOptions}, io::{BufReader, BufWriter}, - path::Path, + path::{Path, PathBuf}, }; -pub fn get_writer(path_s: &str) -> anyhow::Result> { - let path = Path::new(path_s); +pub fn get_writer(path: &PathBuf) -> anyhow::Result> { if let Some(parent) = path.parent() { if !parent.is_dir() { create_dir_all(parent)?; @@ -24,26 +23,25 @@ pub fn get_writer(path_s: &str) -> anyhow::Result> { Ok(BufWriter::new(file)) } -pub fn get_reader(path_s: &str) -> Option> { - let path = Path::new(&path_s); +pub fn get_reader(path: &PathBuf) -> Option> { if !(path.is_file() || path.is_symlink()) { return None; } match File::open(path) { Err(e) => { - println!("Error opening {}: {}", path_s, e); + println!("Error opening {}: {}", path.to_string_lossy(), e); None } Ok(fd) => Some(BufReader::new(fd)), } } -pub fn deserialize_file(path_s: &String) -> Option { - match get_reader(path_s) { +pub fn deserialize_file(path: &PathBuf) -> Option { + match get_reader(path) { None => None, Some(reader) => match serde_json::from_reader(reader) { Err(e) => { - println!("Failed to deserialize {}: {}", path_s, e); + println!("Failed to deserialize {}: {}", path.to_string_lossy(), e); None } Ok(res) => Some(res), @@ -51,8 +49,7 @@ pub fn deserialize_file(path_s: &String) -> Opti } } -pub fn set_file_readonly(path_s: &str, readonly: bool) -> Result<(), std::io::Error> { - let path = Path::new(&path_s); +pub fn set_file_readonly(path: &PathBuf, readonly: bool) -> Result<(), std::io::Error> { if !path.is_file() { println!("WARN: trying to set readonly on a file that does not exist"); 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) } -pub fn setcap_cap_sys_nice_eip(file: String) { +pub fn setcap_cap_sys_nice_eip(file: &PathBuf) { let mut runner = CmdRunner::new( None, "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.join(); } -pub fn rm_rf(path_s: &String) { - if remove_dir_all(path_s).is_err() { - println!("Failed to remove path {}", path_s); +pub fn rm_rf(path: &PathBuf) { + if remove_dir_all(path).is_err() { + println!("Failed to remove path {}", path.to_string_lossy()); } } -pub fn copy_file(source_s: &str, dest_s: &str) { - let source = Path::new(source_s); - let dest = Path::new(dest_s); +pub fn copy_file(source: &PathBuf, dest: &PathBuf) { if let Some(parent) = dest.parent() { if !parent.is_dir() { create_dir_all(parent) .unwrap_or_else(|_| panic!("Failed to create dir {}", parent.to_str().unwrap())); } } - set_file_readonly(dest_s, false) - .unwrap_or_else(|_| panic!("Failed to set file {} as rw", dest_s)); - copy(source, dest).unwrap_or_else(|_| panic!("Failed to copy {} to {}", source_s, dest_s)); + set_file_readonly(dest, false) + .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.to_string_lossy(), + dest.to_string_lossy() + ) + }); } -pub fn mount_has_nosuid(path_s: &str) -> Result { - let path = Path::new(path_s); +pub fn mount_has_nosuid(path: &PathBuf) -> Result { match statvfs(path) { Ok(fstats) => Ok(fstats.flags().contains(FsFlags::ST_NOSUID)), Err(e) => Err(e), @@ -104,10 +108,12 @@ pub fn mount_has_nosuid(path_s: &str) -> Result { #[cfg(test)] mod tests { + use std::path::PathBuf; + use crate::file_utils::mount_has_nosuid; #[test] fn can_get_nosuid() { - mount_has_nosuid("/tmp").expect("Error running statvfs"); + mount_has_nosuid(&PathBuf::from("/tmp")).expect("Error running statvfs"); } } diff --git a/src/gpu_profile.rs b/src/gpu_profile.rs index a23744b..5ba54a1 100644 --- a/src/gpu_profile.rs +++ b/src/gpu_profile.rs @@ -1,16 +1,16 @@ 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"; -fn power_profile_mode_file(card_dir: &str) -> String { - format!("{}/device/pp_power_profile_mode", card_dir) +fn power_profile_mode_file(card_dir: &PathBuf) -> PathBuf { + 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!( "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)] pub enum GpuSysDrm { - Amd(String), - Intel(String), - Nvidia(String), - Other(String), + Amd(PathBuf), + Intel(PathBuf), + Nvidia(PathBuf), + Other(PathBuf), } fn list_gpus() -> Vec { @@ -104,8 +104,8 @@ fn list_gpus() -> Vec { for i in 0..5 { // arbitrary range, find a better way - let card_dir = format!("/sys/class/drm/card{}", i); - let vendor_file = format!("{}/device/vendor", card_dir); + let card_dir = PathBuf::from(format!("/sys/class/drm/card{}", i)); + let vendor_file = card_dir.join("device/vendor"); if let Some(mut reader) = get_reader(&vendor_file) { let mut buf = String::new(); if reader.read_to_string(&mut buf).is_ok() { @@ -131,7 +131,7 @@ pub fn get_first_amd_gpu() -> Option { pub fn get_amd_gpu_power_profile() -> Option { let amd_gpu = get_first_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(); reader.read_to_string(&mut txt).ok()?; for line in txt.split('\n') { diff --git a/src/linux_distro.rs b/src/linux_distro.rs index 247a5c8..783aefa 100644 --- a/src/linux_distro.rs +++ b/src/linux_distro.rs @@ -1,5 +1,8 @@ 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)] pub enum LinuxDistro { @@ -18,10 +21,10 @@ impl LinuxDistro { } fn get_from_etc_os_release() -> Option { - 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 { + fn get_from_etc_os_release_file(fp: &PathBuf) -> Option { if let Some(mut reader) = get_reader(fp) { let mut buf = String::new(); loop { @@ -47,7 +50,7 @@ impl LinuxDistro { } fn get_from_etc_issue() -> Option { - if let Some(mut reader) = get_reader("/etc/issue") { + if let Some(mut reader) = get_reader(&"/etc/issue".into()) { let mut buf = String::new(); if reader.read_to_string(&mut buf).is_ok() { buf = buf.trim().to_lowercase(); @@ -111,7 +114,7 @@ mod tests { #[test] fn can_detect_arch_linux_from_etc_os_release() { 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) ) } diff --git a/src/main.rs b/src/main.rs index 6859e7a..906c0b5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,6 +42,7 @@ pub mod runner_pipeline; pub mod steam_linux_runtime_injector; pub mod steamvr_utils; pub mod ui; +pub mod xdg; pub mod xr_devices; fn restore_steam_xr_files() { diff --git a/src/paths.rs b/src/paths.rs index bb34a64..5f36620 100644 --- a/src/paths.rs +++ b/src/paths.rs @@ -1,32 +1,36 @@ -use crate::{constants::CMD_NAME, profile::XRServiceType}; -use std::{env, fs::create_dir_all, path::Path}; +use crate::{constants::CMD_NAME, profile::XRServiceType, xdg::XDG}; +use std::{ + env, + fs::create_dir_all, + path::{Path, PathBuf}, +}; -pub fn data_opencomposite_path() -> String { - format!("{data}/opencomposite", data = get_data_dir()) +pub fn data_opencomposite_path() -> PathBuf { + get_data_dir().join("opencomposite") } -pub fn data_monado_path() -> String { - format!("{data}/monado", data = get_data_dir()) +pub fn data_monado_path() -> PathBuf { + get_data_dir().join("monado") } -pub fn data_wivrn_path() -> String { - format!("{data}/wivrn", data = get_data_dir()) +pub fn data_wivrn_path() -> PathBuf { + get_data_dir().join("wivrn") } -pub fn data_libsurvive_path() -> String { - format!("{data}/libsurvive", data = get_data_dir()) +pub fn data_libsurvive_path() -> PathBuf { + get_data_dir().join("libsurvive") } -pub fn data_openhmd_path() -> String { - format!("{data}/openhmd", data = get_data_dir()) +pub fn data_openhmd_path() -> PathBuf { + get_data_dir().join("openhmd") } -pub fn data_basalt_path() -> String { - format!("{data}/basalt", data = get_data_dir()) +pub fn data_basalt_path() -> PathBuf { + get_data_dir().join("basalt") } -pub fn wivrn_apk_download_path() -> String { - format!("{cache}/wivrn.apk", cache = get_cache_dir()) +pub fn wivrn_apk_download_path() -> PathBuf { + get_cache_dir().join("wivrn.apk") } 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) */ pub const BWRAP_SYSTEM_PREFIX: &str = "/run/host/usr"; -pub fn get_home_dir() -> String { - env::var("HOME").expect("HOME env var not defined") +pub fn get_home_dir() -> PathBuf { + env::var("HOME").expect("HOME env var not defined").into() } -pub fn get_xdg_config_dir() -> String { - match env::var("XDG_CONFIG_HOME") { - Ok(conf_home) => conf_home, - Err(_) => format!("{home}/.config", home = get_home_dir()), - } +pub fn get_config_dir() -> PathBuf { + XDG.get_config_home().join(CMD_NAME) } -pub fn get_xdg_data_dir() -> String { - match env::var("XDG_DATA_HOME") { - Ok(data_home) => data_home, - Err(_) => format!("{home}/.local/share", home = get_home_dir()), - } +pub fn get_data_dir() -> PathBuf { + XDG.get_data_home().join(CMD_NAME) } -pub fn get_xdg_cache_dir() -> String { - match env::var("XDG_CACHE_HOME") { - Ok(cache_home) => cache_home, - Err(_) => format!("{home}/.cache", home = get_home_dir()), - } +pub fn get_cache_dir() -> PathBuf { + XDG.get_cache_home().join(CMD_NAME) } -pub fn get_xdg_runtime_dir() -> String { - env::var("XDG_RUNTIME_DIR").expect("XDG_RUNTIME_DIR is not set") -} - -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); +pub fn get_backup_dir() -> PathBuf { + let p = get_data_dir().join("backups"); if !p.is_dir() { if p.is_file() { panic!( @@ -93,12 +63,12 @@ pub fn get_backup_dir() -> String { 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"); if !p.is_symlink() { panic!("/proc/self/exe is not a symlink!"); @@ -110,25 +80,19 @@ pub fn get_exec_prefix() -> String { .unwrap() .parent() .unwrap() - .to_str() - .unwrap() - .to_string() + .into() } -pub fn get_ipc_file_path(xrservice_type: &XRServiceType) -> String { - format!( - "{runtime}/{xrservice}_comp_ipc", - runtime = get_xdg_runtime_dir(), - xrservice = match xrservice_type { - XRServiceType::Monado => "monado", - XRServiceType::Wivrn => "wivrn", - } - ) +pub fn get_ipc_file_path(xrservice_type: &XRServiceType) -> PathBuf { + XDG.get_runtime_directory() + .expect("XDG runtime directory is not available") + .join(match xrservice_type { + XRServiceType::Monado => "monado_comp_ipc", + XRServiceType::Wivrn => "wivrn_comp_ipc", + }) } -pub fn get_steamvr_bin_dir_path() -> String { - format!( - "{data}/Steam/steamapps/common/SteamVR/bin/linux64", - data = get_xdg_data_dir() - ) +pub fn get_steamvr_bin_dir_path() -> PathBuf { + XDG.get_data_home() + .join("Steam/steamapps/common/SteamVR/bin/linux64") } diff --git a/src/profile.rs b/src/profile.rs index 2f3bde2..934b2a2 100644 --- a/src/profile.rs +++ b/src/profile.rs @@ -2,8 +2,16 @@ use crate::{ file_utils::get_writer, paths::{get_data_dir, get_ipc_file_path, BWRAP_SYSTEM_PREFIX, SYSTEM_PREFIX}, }; +use nix::NixPath; 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; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -85,7 +93,7 @@ impl Display for ProfileFeatureType { pub struct ProfileFeature { pub feature_type: ProfileFeatureType, pub enabled: bool, - pub path: Option, + pub path: Option, pub repo: Option, pub branch: Option, } @@ -207,18 +215,18 @@ pub struct Profile { pub uuid: String, pub name: String, pub xrservice_type: XRServiceType, - pub xrservice_path: String, + pub xrservice_path: PathBuf, pub xrservice_repo: Option, pub xrservice_branch: Option, #[serde(default = "HashMap::::default")] pub xrservice_cmake_flags: HashMap, - pub opencomposite_path: String, + pub opencomposite_path: PathBuf, pub opencomposite_repo: Option, pub opencomposite_branch: Option, pub features: ProfileFeatures, pub environment: HashMap, /** Install prefix */ - pub prefix: String, + pub prefix: PathBuf, pub can_be_built: bool, pub editable: bool, pub pull_on_build: bool, @@ -239,10 +247,10 @@ impl Display for Profile { impl Default for Profile { fn default() -> Self { let uuid = Uuid::new_v4().to_string(); - let profile_dir = format!("{}/{}", get_data_dir(), uuid); + let profile_dir = get_data_dir().join(&uuid); Self { name: "Default profile name".into(), - xrservice_path: format!("{}/xrservice", profile_dir), + xrservice_path: profile_dir.join("xrservice"), xrservice_type: XRServiceType::Monado, xrservice_repo: None, xrservice_branch: None, @@ -250,21 +258,21 @@ impl Default for Profile { features: ProfileFeatures { libsurvive: ProfileFeature { enabled: false, - path: Some(format!("{}/libsurvive", profile_dir)), + path: Some(profile_dir.join("libsurvive")), repo: None, branch: None, feature_type: ProfileFeatureType::Libsurvive, }, basalt: ProfileFeature { enabled: false, - path: Some(format!("{}/basalt", profile_dir)), + path: Some(profile_dir.join("basalt")), repo: None, branch: None, feature_type: ProfileFeatureType::Basalt, }, openhmd: ProfileFeature { enabled: false, - path: Some(format!("{}/openhmd", profile_dir)), + path: Some(profile_dir.join("openhmd")), repo: None, branch: None, feature_type: ProfileFeatureType::OpenHmd, @@ -272,10 +280,10 @@ impl Default for Profile { mercury_enabled: false, }, environment: HashMap::new(), - prefix: format!("{}/prefixes/{}", get_data_dir(), uuid), + prefix: get_data_dir().join("prefixes").join(&uuid), can_be_built: true, pull_on_build: true, - opencomposite_path: format!("{}/opencomposite", profile_dir), + opencomposite_path: profile_dir.join("opencomposite"), opencomposite_repo: None, opencomposite_branch: None, editable: true, @@ -291,7 +299,7 @@ impl Profile { pub fn xr_runtime_json_env_var(&self) -> String { format!( "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, other => other, }, @@ -312,7 +320,7 @@ impl Profile { self.xr_runtime_json_env_var(), format!( "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(" ") } - pub fn get_survive_cli_path(&self) -> Option { - let path_s = format!("{pfx}/bin/survive-cli", pfx = self.prefix); - if Path::new(&path_s).is_file() { - return Some(path_s); + pub fn get_survive_cli_path(&self) -> Option { + let path = self.prefix.join("bin/survive-cli"); + if path.is_file() { + return Some(path); } 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 reader = BufReader::new(file); serde_json::from_reader(reader).expect("Faiuled to deserialize profile") } - pub fn dump_profile(&self, path_s: &String) { - let writer = get_writer(path_s).expect("Could not write profile"); + pub fn dump_profile(&self, path: &PathBuf) { + let writer = get_writer(path).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.prefix.is_empty() && (!self.features.libsurvive.enabled - || !self + || self .features .libsurvive .path .as_ref() - .unwrap_or(&"".to_string()) - .is_empty()) + .is_some_and(|p| !p.is_empty())) && (!self.features.basalt.enabled - || !self + || self .features .basalt .path .as_ref() - .unwrap_or(&"".to_string()) - .is_empty()) + .is_some_and(|p| !p.is_empty())) && (!self.features.openhmd.enabled - || !self + || self .features .openhmd .path .as_ref() - .unwrap_or(&"".to_string()) - .is_empty()) + .is_some_and(|p| !p.is_empty())) } - pub fn xrservice_binary(&self) -> String { - format!( - "{}/bin/{}", - self.prefix, - match self.xrservice_type { - XRServiceType::Monado => "monado-service", - XRServiceType::Wivrn => "wivrn-server", - } - ) + pub fn xrservice_binary(&self) -> PathBuf { + self.prefix.join("bin").join(match self.xrservice_type { + XRServiceType::Monado => "monado-service", + XRServiceType::Wivrn => "wivrn-server", + }) } pub fn can_start(&self) -> bool { Path::new(&self.xrservice_binary()).is_file() } - pub fn libmonado_so(&self) -> Option { - let mut res = format!("{}/lib/libmonado.so", self.prefix); - if Path::new(&res).is_file() { + pub fn libmonado_so(&self) -> Option { + let res = self.prefix.join("lib/libmonado.so"); + if res.is_file() { return Some(res); } - res = format!("{}/lib64/libmonado.so", self.prefix); - if Path::new(&res).is_file() { + let res = self.prefix.join("lib64/libmonado.so"); + if res.is_file() { return Some(res); } @@ -458,7 +459,7 @@ impl Profile { #[cfg(test)] mod tests { - use std::collections::HashMap; + use std::{collections::HashMap, path::PathBuf}; use crate::profile::{ProfileFeature, ProfileFeatureType, ProfileFeatures, XRServiceType}; @@ -466,14 +467,17 @@ mod tests { #[test] 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.xrservice_path, "/home/user/monado"); - assert_eq!(profile.opencomposite_path, "/home/user/opencomposite"); - assert_eq!(profile.prefix, "/home/user/envisionprefix"); + assert_eq!(profile.xrservice_path, PathBuf::from("/home/user/monado")); assert_eq!( - profile.features.libsurvive.path.as_deref(), - Some("/home/user/libsurvive") + profile.opencomposite_path, + 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.libsurvive.enabled, true); @@ -496,14 +500,14 @@ mod tests { let p = Profile { uuid: "demo".into(), name: "Demo profile".into(), - xrservice_path: String::from("/home/user/monado"), + xrservice_path: PathBuf::from("/home/user/monado"), xrservice_type: XRServiceType::Monado, - opencomposite_path: String::from("/home/user/opencomposite"), + opencomposite_path: PathBuf::from("/home/user/opencomposite"), features: ProfileFeatures { libsurvive: ProfileFeature { feature_type: ProfileFeatureType::Libsurvive, enabled: true, - path: Some(String::from("/home/user/libsurvive")), + path: Some(PathBuf::from("/home/user/libsurvive")), repo: None, branch: None, }, @@ -512,17 +516,17 @@ mod tests { mercury_enabled: false, }, environment: env, - prefix: String::from("/home/user/envisionprefix"), + prefix: PathBuf::from("/home/user/envisionprefix"), editable: true, ..Default::default() }; - let fpath = String::from("./target/testout/testprofile.json"); + let fpath = PathBuf::from("./target/testout/testprofile.json"); p.dump_profile(&fpath); let loaded = Profile::load_profile(&fpath); assert_eq!(loaded.name, "Demo profile"); assert_eq!( loaded.features.libsurvive.path, - Some(String::from("/home/user/libsurvive")) + Some(PathBuf::from("/home/user/libsurvive")) ); assert_eq!( loaded @@ -534,6 +538,6 @@ mod tests { } } -pub fn prepare_ld_library_path(prefix: &str) -> String { - format!("{pfx}/lib:{pfx}/lib64", pfx = prefix) +pub fn prepare_ld_library_path(prefix: &PathBuf) -> String { + format!("{pfx}/lib:{pfx}/lib64", pfx = prefix.to_string_lossy()) } diff --git a/src/profiles/lighthouse.rs b/src/profiles/lighthouse.rs index 43a64eb..794ee3e 100644 --- a/src/profiles/lighthouse.rs +++ b/src/profiles/lighthouse.rs @@ -7,7 +7,7 @@ use std::collections::HashMap; pub fn lighthouse_profile() -> Profile { 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 = HashMap::new(); environment.insert("XRT_JSON_LOG".into(), "1".into()); environment.insert("XRT_COMPOSITOR_SCALE_PERCENTAGE".into(), "140".into()); diff --git a/src/profiles/openhmd.rs b/src/profiles/openhmd.rs index 3f20432..b045c1e 100644 --- a/src/profiles/openhmd.rs +++ b/src/profiles/openhmd.rs @@ -10,7 +10,7 @@ use std::collections::HashMap; pub fn openhmd_profile() -> Profile { 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 = HashMap::new(); environment.insert("XRT_JSON_LOG".into(), "1".into()); environment.insert("XRT_COMPOSITOR_SCALE_PERCENTAGE".into(), "140".into()); diff --git a/src/profiles/simulated.rs b/src/profiles/simulated.rs index d04d4c4..2283caf 100644 --- a/src/profiles/simulated.rs +++ b/src/profiles/simulated.rs @@ -7,7 +7,7 @@ use std::collections::HashMap; pub fn simulated_profile() -> Profile { 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 = HashMap::new(); environment.insert("QWERTY_ENABLE".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( "LD_LIBRARY_PATH".into(), - format!("{pfx}/lib:{pfx}/lib64", pfx = prefix), + format!("{pfx}/lib:{pfx}/lib64", pfx = prefix.to_string_lossy()), ); Profile { uuid: "simulated-default".into(), diff --git a/src/profiles/survive.rs b/src/profiles/survive.rs index ae767db..481f624 100644 --- a/src/profiles/survive.rs +++ b/src/profiles/survive.rs @@ -10,7 +10,7 @@ use std::collections::HashMap; pub fn survive_profile() -> Profile { 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 = HashMap::new(); environment.insert("XRT_JSON_LOG".into(), "1".into()); environment.insert("XRT_COMPOSITOR_SCALE_PERCENTAGE".into(), "140".into()); diff --git a/src/profiles/wivrn.rs b/src/profiles/wivrn.rs index 6f4d0c7..dd58da9 100644 --- a/src/profiles/wivrn.rs +++ b/src/profiles/wivrn.rs @@ -7,7 +7,7 @@ use std::collections::HashMap; pub fn wivrn_profile() -> Profile { 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 = HashMap::new(); environment.insert("LD_LIBRARY_PATH".into(), prepare_ld_library_path(&prefix)); environment.insert("XRT_DEBUG_GUI".into(), "1".into()); diff --git a/src/profiles/wmr.rs b/src/profiles/wmr.rs index 0b1124e..23e238d 100644 --- a/src/profiles/wmr.rs +++ b/src/profiles/wmr.rs @@ -10,7 +10,7 @@ use std::collections::HashMap; pub fn wmr_profile() -> Profile { 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 = HashMap::new(); environment.insert("XRT_JSON_LOG".into(), "1".into()); environment.insert("XRT_COMPOSITOR_SCALE_PERCENTAGE".into(), "140".into()); diff --git a/src/steam_linux_runtime_injector.rs b/src/steam_linux_runtime_injector.rs index 8ade9d8..71abbce 100644 --- a/src/steam_linux_runtime_injector.rs +++ b/src/steam_linux_runtime_injector.rs @@ -1,26 +1,28 @@ use crate::{ 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, }; 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 { - vec![format!( - "{home}/.steam/steam/steamapps/common/SteamLinuxRuntime_sniper/_v2-entry-point", - home = get_home_dir(), - )] +fn get_runtime_entrypoint_path() -> Option { + vec![get_home_dir() + .join(".steam/steam/steamapps/common/SteamLinuxRuntime_sniper/_v2-entry-point")] .iter() - .find(|path| Path::new(&path).is_file()) + .find(|p| p.is_file()) .cloned() } -fn get_backup_runtime_entrypoint_location() -> String { - format!("{backup}/_v2-entry-point.bak", backup = get_backup_dir()) +fn get_backup_runtime_entrypoint_location() -> PathBuf { + 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()); } @@ -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 new = existing.replace( "exec \"${here}/${run}\" \"$@\"\nexit 125", diff --git a/src/steamvr_utils.rs b/src/steamvr_utils.rs index 633e4b1..cc05edd 100644 --- a/src/steamvr_utils.rs +++ b/src/steamvr_utils.rs @@ -1,11 +1,7 @@ use crate::paths::get_home_dir; -use std::path::Path; pub fn chaperone_info_exists() -> bool { - let path_s = format!( - "{}/.steam/steam/config/chaperone_info.vrchap", - get_home_dir() - ); - let path = Path::new(&path_s); - path.is_file() + get_home_dir() + .join(".steam/steam/config/chaperone_info.vrchap") + .is_file() } diff --git a/src/ui/app.rs b/src/ui/app.rs index 57fe10f..46737ad 100644 --- a/src/ui/app.rs +++ b/src/ui/app.rs @@ -644,10 +644,7 @@ impl SimpleComponent for App { println!("pkexec not found, skipping setcap"); } else { let profile = self.get_selected_profile(); - setcap_cap_sys_nice_eip(format!( - "{pfx}/bin/monado-service", - pfx = profile.prefix - )); + setcap_cap_sys_nice_eip(&profile.prefix.join("bin/monado-service")); } } Msg::ProfileSelected(prof) => { @@ -692,12 +689,12 @@ impl SimpleComponent for App { self.application.quit(); } 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 => { open_with_default_handler(&format!( "file://{}", - self.get_selected_profile().prefix + self.get_selected_profile().prefix.to_string_lossy() )); } Msg::DebugCopyEnvVars => { diff --git a/src/ui/job_worker/internal_worker.rs b/src/ui/job_worker/internal_worker.rs index 3426a4c..bc1527d 100644 --- a/src/ui/job_worker/internal_worker.rs +++ b/src/ui/job_worker/internal_worker.rs @@ -196,14 +196,21 @@ impl InternalJobWorker { match launch_opts.contains(LAUNCH_OPTS_CMD_PLACEHOLDER) { true => launch_opts.replacen( LAUNCH_OPTS_CMD_PLACEHOLDER, - prof.xrservice_binary().as_str(), + &prof.xrservice_binary().to_string_lossy().to_string(), 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 { environment: env, diff --git a/src/ui/libsurvive_setup_window.rs b/src/ui/libsurvive_setup_window.rs index bf4d282..1712c89 100644 --- a/src/ui/libsurvive_setup_window.rs +++ b/src/ui/libsurvive_setup_window.rs @@ -7,7 +7,13 @@ use crate::{ use adw::prelude::*; use gtk::glib; 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 CALIBRATION_RUN_TIME_SECONDS: f64 = 30.0; @@ -57,7 +63,7 @@ pub enum LibsurviveSetupMsg { } 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 mut env = HashMap::new(); let profile_prefix = &self.profile.as_ref().unwrap().prefix; @@ -67,7 +73,7 @@ impl LibsurviveSetupWindow { ); CmdRunner::new( Some(env), - survive_cli_path, + survive_cli_path.to_string_lossy().to_string(), vec!["--steamvr-calibration".into(), lh_path], ) } @@ -369,7 +375,7 @@ impl SimpleComponent for LibsurviveSetupWindow { if let Some(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.first_run_done = false; self.calibration_seconds_elapsed = 0.0; @@ -444,7 +450,8 @@ impl SimpleComponent for LibsurviveSetupWindow { let runner = self.calibration_runner.as_mut().unwrap(); runner.terminate(); let mut n_runner = self.create_calibration_runner( - self.profile + &self + .profile .as_ref() .unwrap() .get_survive_cli_path() diff --git a/src/ui/main_view.rs b/src/ui/main_view.rs index 66b3117..4fb23be 100644 --- a/src/ui/main_view.rs +++ b/src/ui/main_view.rs @@ -188,13 +188,13 @@ impl SimpleComponent for MainView { add_css_class: "card", add_css_class: "padded", #[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, Err(_) => { // TODO: handle this error better - println!( + eprintln!( "Warning: could not get stat on path {}", - model.selected_profile.prefix); + model.selected_profile.prefix.to_string_lossy()); false }, }, diff --git a/src/ui/profile_editor.rs b/src/ui/profile_editor.rs index 6a5d888..112382a 100644 --- a/src/ui/profile_editor.rs +++ b/src/ui/profile_editor.rs @@ -13,7 +13,7 @@ use crate::{ use adw::prelude::*; use gtk::glib::clone; use relm4::{factory::AsyncFactoryVecDeque, prelude::*}; -use std::{cell::RefCell, rc::Rc}; +use std::{cell::RefCell, path::PathBuf, rc::Rc}; #[tracker::track] pub struct ProfileEditor { @@ -110,10 +110,10 @@ impl SimpleComponent for ProfileEditor { add: &path_row( "Install Prefix", None, - Some(model.profile.borrow().prefix.clone()), + Some(model.profile.borrow().prefix.to_string_lossy().to_string()), Some(init.root_win.clone()), 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", @@ -171,10 +171,10 @@ impl SimpleComponent for ProfileEditor { add: &path_row( "XR Service Path", 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()), 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( @@ -200,10 +200,10 @@ impl SimpleComponent for ProfileEditor { set_description: Some("OpenVR driver built on top of OpenXR"), add: &path_row( "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()), 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( @@ -236,10 +236,10 @@ impl SimpleComponent for ProfileEditor { ), add: &path_row( "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()), 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( @@ -272,10 +272,10 @@ impl SimpleComponent for ProfileEditor { ), add: &path_row( "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()), 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( @@ -308,10 +308,10 @@ impl SimpleComponent for ProfileEditor { ), add: &path_row( "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()), 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( diff --git a/src/ui/steamvr_calibration_box.rs b/src/ui/steamvr_calibration_box.rs index 807af36..08c1780 100644 --- a/src/ui/steamvr_calibration_box.rs +++ b/src/ui/steamvr_calibration_box.rs @@ -5,7 +5,6 @@ use super::job_worker::{ }; use crate::paths::get_steamvr_bin_dir_path; use relm4::{ - binding::{Binding, ConnectBinding, StringBinding}, gtk::{self, prelude::*}, ComponentParts, ComponentSender, RelmWidgetExt, SimpleComponent, }; @@ -145,7 +144,7 @@ impl SimpleComponent for SteamVrCalibrationBox { } Self::Input::RunCalibration => { 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() { self.set_calibration_success(false); self.set_calibration_result(Some("SteamVR not found".into())); @@ -154,7 +153,7 @@ impl SimpleComponent for SteamVrCalibrationBox { let mut env: HashMap = HashMap::new(); env.insert("LD_LIBRARY_PATH".into(), steamvr_bin_dir.clone()); let vrcmd = format!("{steamvr_bin_dir}/vrcmd"); - let mut server_worker = { + let server_worker = { let mut jobs: VecDeque = VecDeque::new(); jobs.push_back(WorkerJob::new_cmd( Some(env.clone()), @@ -166,7 +165,7 @@ impl SimpleComponent for SteamVrCalibrationBox { JobWorkerOut::Exit(code) => Self::Input::OnServerWorkerExit(code), }) }; - let mut cal_worker = { + let cal_worker = { let mut jobs: VecDeque = VecDeque::new(); jobs.push_back(WorkerJob::new_func(Box::new(move || { sleep(Duration::from_secs(2)); diff --git a/src/xdg.rs b/src/xdg.rs new file mode 100644 index 0000000..cf4e4b6 --- /dev/null +++ b/src/xdg.rs @@ -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"); +}