feat: prefer symlinks over generating files for openxr active runtime json file

This commit is contained in:
GabMus 2024-12-04 19:14:14 +00:00
parent a9fa4f8cf4
commit 4ea0ce53b0
5 changed files with 102 additions and 94 deletions

View file

@ -1,18 +1,17 @@
use crate::{
paths::{get_backup_dir, SYSTEM_PREFIX},
paths::SYSTEM_PREFIX,
profile::Profile,
util::{
file_utils::{copy_file, deserialize_file, get_writer, set_file_readonly},
steam_library_folder::SteamLibraryFolder,
},
util::file_utils::{deserialize_file, get_writer, set_file_readonly},
xdg::XDG,
};
use anyhow::bail;
use serde::{Deserialize, Serialize};
use std::{
fs::remove_file,
fs::{remove_file, rename},
os::unix::fs::symlink,
path::{Path, PathBuf},
};
use tracing::error;
use tracing::{info, warn};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ActiveRuntimeInnerRuntime {
@ -38,46 +37,6 @@ 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))
}
pub const STEAMVR_STEAM_APPID: u32 = 250820;
pub fn find_steam_openxr_json() -> Option<PathBuf> {
match SteamLibraryFolder::get_folders() {
Ok(libraryfolders) => libraryfolders
.iter()
.find(|(_, folder)| folder.apps.contains_key(&STEAMVR_STEAM_APPID))
.map(|(_, folder)| {
PathBuf::from(&folder.path).join("steamapps/common/SteamVR/steamxr_linux64.json")
}),
Err(e) => {
error!("unable to find steam openxr json: unable to load steam libraryfolders: {e}");
None
}
}
}
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<ActiveRuntime> {
get_active_runtime_from_path(&get_backup_steam_active_runtime_path())
}
fn backup_steam_active_runtime() {
if let Some(ar) = get_current_active_runtime() {
if is_steam(&ar) {
copy_file(
&get_active_runtime_json_path(),
&get_backup_steam_active_runtime_path(),
);
}
}
}
fn get_active_runtime_from_path(path: &Path) -> Option<ActiveRuntime> {
deserialize_file(path)
}
@ -99,29 +58,6 @@ pub fn dump_current_active_runtime(active_runtime: &ActiveRuntime) -> anyhow::Re
dump_active_runtime_to_path(active_runtime, &get_active_runtime_json_path())
}
fn build_steam_active_runtime() -> ActiveRuntime {
if let Some(backup) = get_backed_up_steam_active_runtime() {
return backup;
}
ActiveRuntime {
file_format_version: "1.0.0".into(),
runtime: ActiveRuntimeInnerRuntime {
valve_runtime_is_steamvr: Some(true),
libmonado_path: None,
library_path: XDG
.get_data_home()
.join("Steam/steamapps/common/SteamVR/bin/linux64/vrclient.so"),
name: Some("SteamVR".into()),
},
}
}
pub fn set_current_active_runtime_to_steam() -> anyhow::Result<()> {
set_file_readonly(&get_active_runtime_json_path(), false)?;
dump_current_active_runtime(&build_steam_active_runtime())?;
Ok(())
}
pub fn build_profile_active_runtime(profile: &Profile) -> anyhow::Result<ActiveRuntime> {
let Some(libopenxr_path) = profile.libopenxr_so() else {
anyhow::bail!(
@ -158,18 +94,66 @@ fn relativize_active_runtime_lib_path(ar: &ActiveRuntime, path: &Path) -> Active
res
}
const ACTIVE_RUNTIME_BAK: &str = "active_runtime.json.envision.bak";
pub fn set_current_active_runtime_to_profile(profile: &Profile) -> anyhow::Result<()> {
let dest = get_active_runtime_json_path();
set_file_readonly(&dest, false)?;
backup_steam_active_runtime();
let pfx = profile.clone().prefix;
let mut ar = build_profile_active_runtime(profile)?;
// hack: relativize libopenxr_monado.so path for system installs
if pfx == PathBuf::from(SYSTEM_PREFIX) {
ar = relativize_active_runtime_lib_path(&ar, &dest);
if dest.is_dir() {
bail!("{} is a directory", dest.to_string_lossy());
}
dump_current_active_runtime(&ar)?;
set_file_readonly(&dest, true)?;
if !dest.is_symlink() {
set_file_readonly(&dest, false)?;
}
if dest.is_file() || dest.is_symlink() {
rename(&dest, dest.parent().unwrap().join(ACTIVE_RUNTIME_BAK))?;
} else {
info!("no active_runtime.json file to backup")
}
let profile_openxr_json = profile.openxr_json_path();
if profile_openxr_json.is_file() {
symlink(profile_openxr_json, &dest)?;
} else {
warn!("profile openxr json file doesn't exist");
// fallback: build the file from scratch
let pfx = profile.clone().prefix;
let mut ar = build_profile_active_runtime(profile)?;
// hack: relativize libopenxr_monado.so path for system installs
if pfx == PathBuf::from(SYSTEM_PREFIX) {
ar = relativize_active_runtime_lib_path(&ar, &dest);
}
dump_current_active_runtime(&ar)?;
set_file_readonly(&dest, true)?;
}
Ok(())
}
pub fn remove_current_active_runtime() -> anyhow::Result<()> {
let dest = get_active_runtime_json_path();
if dest.is_dir() {
bail!("{} is a directory", dest.to_string_lossy());
}
if !dest.exists() {
info!("no current active_runtime.json to remove")
}
Ok(remove_file(dest)?)
}
pub fn restore_active_runtime_backup() -> anyhow::Result<()> {
let dest = get_active_runtime_json_path();
let bak = dest.parent().unwrap().join(ACTIVE_RUNTIME_BAK);
if bak.is_file() || bak.is_symlink() {
if dest.is_dir() {
bail!("{} is a directory", dest.to_string_lossy());
}
if !dest.is_symlink() {
set_file_readonly(&dest, false)?;
}
rename(&bak, &dest)?;
} else {
info!("{ACTIVE_RUNTIME_BAK} does not exist, nothing to restore");
}
Ok(())
}

View file

@ -1,7 +1,7 @@
use anyhow::Result;
use constants::{resources, APP_ID, APP_NAME, GETTEXT_PACKAGE, LOCALE_DIR, RESOURCES_BASE_PATH};
use file_builders::{
active_runtime_json::{get_current_active_runtime, set_current_active_runtime_to_steam},
active_runtime_json::restore_active_runtime_backup,
openvrpaths_vrpath::{get_current_openvrpaths, set_current_openvrpaths_to_steam},
};
use gettextrs::LocaleCategory;
@ -48,14 +48,9 @@ pub mod xdg;
pub mod xr_devices;
fn restore_steam_xr_files() {
let active_runtime = get_current_active_runtime();
let openvrpaths = get_current_openvrpaths();
if let Some(ar) = active_runtime {
if !file_builders::active_runtime_json::is_steam(&ar) {
if let Err(e) = set_current_active_runtime_to_steam() {
warn!("failed to restore active runtime to steam: {e}");
}
}
if let Err(e) = restore_active_runtime_backup() {
warn!("failed to restore active runtime to steam: {e}");
}
if let Some(ovrp) = openvrpaths {
if !file_builders::openvrpaths_vrpath::is_steam(&ovrp) {

View file

@ -47,6 +47,13 @@ impl XRServiceType {
}
}
pub fn openxr_json_rel_path(&self) -> &'static str {
match self {
Self::Monado => "share/openxr/1/openxr_monado.json",
Self::Wivrn => "share/openxr/1/openxr_wivrn.json",
}
}
pub fn ipc_file_path(&self) -> PathBuf {
XDG.get_runtime_directory()
.expect("XDG runtime directory is not available")
@ -586,6 +593,12 @@ impl Profile {
missing_deps.dedup(); // dedup only works if sorted, hence the above
missing_deps
}
/// the file that will become active_runtime.json, as installed in the
/// prefix
pub fn openxr_json_path(&self) -> PathBuf {
self.prefix.join(self.xrservice_type.openxr_json_rel_path())
}
}
pub fn prepare_ld_library_path(prefix: &Path) -> String {

View file

@ -26,7 +26,8 @@ use crate::{
depcheck::common::dep_pkexec,
file_builders::{
active_runtime_json::{
set_current_active_runtime_to_profile, set_current_active_runtime_to_steam,
remove_current_active_runtime, restore_active_runtime_backup,
set_current_active_runtime_to_profile,
},
openvrpaths_vrpath::{
set_current_openvrpaths_to_profile, set_current_openvrpaths_to_steam,
@ -250,9 +251,16 @@ impl App {
pub fn restore_openxr_openvr_files(&self) {
restore_runtime_entrypoint();
if let Err(e) = set_current_active_runtime_to_steam() {
if let Err(e) = remove_current_active_runtime() {
alert(
"Could not restore Steam active runtime",
"Could not remove profile active runtime",
Some(&format!("{e}")),
Some(&self.app_win.clone().upcast::<gtk::Window>()),
);
}
if let Err(e) = restore_active_runtime_backup() {
alert(
"Could not restore previous active runtime",
Some(&format!("{e}")),
Some(&self.app_win.clone().upcast::<gtk::Window>()),
);

View file

@ -57,7 +57,13 @@ pub fn deserialize_file<T: serde::de::DeserializeOwned>(path: &Path) -> Option<T
}
}
pub fn set_file_readonly(path: &Path, readonly: bool) -> Result<(), std::io::Error> {
pub fn set_file_readonly(path: &Path, readonly: bool) -> anyhow::Result<()> {
if path.is_symlink() {
bail!(
"path {} is a symlink, trying to change its write permission will only change the original file",
path.to_string_lossy()
);
}
if !path.is_file() {
debug!(
"trying to set readonly on a file that does not exist: {}",
@ -69,7 +75,7 @@ pub fn set_file_readonly(path: &Path, readonly: bool) -> Result<(), std::io::Err
.expect("Could not get metadata for file")
.permissions();
perms.set_readonly(readonly);
fs::set_permissions(path, perms)
Ok(fs::set_permissions(path, perms)?)
}
pub fn setcap_cap_sys_nice_eip_cmd(profile: &Profile) -> Vec<String> {
@ -104,8 +110,10 @@ pub fn copy_file(source: &Path, dest: &Path) {
.unwrap_or_else(|_| panic!("Failed to create dir {}", parent.to_str().unwrap()));
}
}
set_file_readonly(dest, false)
.unwrap_or_else(|_| panic!("Failed to set file {} as rw", dest.to_string_lossy()));
if !dest.is_symlink() {
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(|e| {
panic!(
"Failed to copy {} to {}: {e}",