mirror of
https://gitlab.com/gabmus/envision.git
synced 2025-08-03 06:38:52 +00:00
feat: prefer symlinks over generating files for openxr active runtime json file
This commit is contained in:
parent
a9fa4f8cf4
commit
4ea0ce53b0
5 changed files with 102 additions and 94 deletions
|
@ -1,18 +1,17 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
paths::{get_backup_dir, SYSTEM_PREFIX},
|
paths::SYSTEM_PREFIX,
|
||||||
profile::Profile,
|
profile::Profile,
|
||||||
util::{
|
util::file_utils::{deserialize_file, get_writer, set_file_readonly},
|
||||||
file_utils::{copy_file, deserialize_file, get_writer, set_file_readonly},
|
|
||||||
steam_library_folder::SteamLibraryFolder,
|
|
||||||
},
|
|
||||||
xdg::XDG,
|
xdg::XDG,
|
||||||
};
|
};
|
||||||
|
use anyhow::bail;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
fs::remove_file,
|
fs::{remove_file, rename},
|
||||||
|
os::unix::fs::symlink,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
use tracing::error;
|
use tracing::{info, warn};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct ActiveRuntimeInnerRuntime {
|
pub struct ActiveRuntimeInnerRuntime {
|
||||||
|
@ -38,46 +37,6 @@ fn get_active_runtime_json_path() -> PathBuf {
|
||||||
get_openxr_conf_dir().join("1/active_runtime.json")
|
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> {
|
fn get_active_runtime_from_path(path: &Path) -> Option<ActiveRuntime> {
|
||||||
deserialize_file(path)
|
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())
|
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> {
|
pub fn build_profile_active_runtime(profile: &Profile) -> anyhow::Result<ActiveRuntime> {
|
||||||
let Some(libopenxr_path) = profile.libopenxr_so() else {
|
let Some(libopenxr_path) = profile.libopenxr_so() else {
|
||||||
anyhow::bail!(
|
anyhow::bail!(
|
||||||
|
@ -158,18 +94,66 @@ fn relativize_active_runtime_lib_path(ar: &ActiveRuntime, path: &Path) -> Active
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ACTIVE_RUNTIME_BAK: &str = "active_runtime.json.envision.bak";
|
||||||
|
|
||||||
pub fn set_current_active_runtime_to_profile(profile: &Profile) -> anyhow::Result<()> {
|
pub fn set_current_active_runtime_to_profile(profile: &Profile) -> anyhow::Result<()> {
|
||||||
let dest = get_active_runtime_json_path();
|
let dest = get_active_runtime_json_path();
|
||||||
set_file_readonly(&dest, false)?;
|
if dest.is_dir() {
|
||||||
backup_steam_active_runtime();
|
bail!("{} is a directory", dest.to_string_lossy());
|
||||||
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)?;
|
if !dest.is_symlink() {
|
||||||
set_file_readonly(&dest, true)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
src/main.rs
11
src/main.rs
|
@ -1,7 +1,7 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use constants::{resources, APP_ID, APP_NAME, GETTEXT_PACKAGE, LOCALE_DIR, RESOURCES_BASE_PATH};
|
use constants::{resources, APP_ID, APP_NAME, GETTEXT_PACKAGE, LOCALE_DIR, RESOURCES_BASE_PATH};
|
||||||
use file_builders::{
|
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},
|
openvrpaths_vrpath::{get_current_openvrpaths, set_current_openvrpaths_to_steam},
|
||||||
};
|
};
|
||||||
use gettextrs::LocaleCategory;
|
use gettextrs::LocaleCategory;
|
||||||
|
@ -48,14 +48,9 @@ pub mod xdg;
|
||||||
pub mod xr_devices;
|
pub mod xr_devices;
|
||||||
|
|
||||||
fn restore_steam_xr_files() {
|
fn restore_steam_xr_files() {
|
||||||
let active_runtime = get_current_active_runtime();
|
|
||||||
let openvrpaths = get_current_openvrpaths();
|
let openvrpaths = get_current_openvrpaths();
|
||||||
if let Some(ar) = active_runtime {
|
if let Err(e) = restore_active_runtime_backup() {
|
||||||
if !file_builders::active_runtime_json::is_steam(&ar) {
|
warn!("failed to restore active runtime to steam: {e}");
|
||||||
if let Err(e) = set_current_active_runtime_to_steam() {
|
|
||||||
warn!("failed to restore active runtime to steam: {e}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if let Some(ovrp) = openvrpaths {
|
if let Some(ovrp) = openvrpaths {
|
||||||
if !file_builders::openvrpaths_vrpath::is_steam(&ovrp) {
|
if !file_builders::openvrpaths_vrpath::is_steam(&ovrp) {
|
||||||
|
|
|
@ -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 {
|
pub fn ipc_file_path(&self) -> PathBuf {
|
||||||
XDG.get_runtime_directory()
|
XDG.get_runtime_directory()
|
||||||
.expect("XDG runtime directory is not available")
|
.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.dedup(); // dedup only works if sorted, hence the above
|
||||||
missing_deps
|
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 {
|
pub fn prepare_ld_library_path(prefix: &Path) -> String {
|
||||||
|
|
|
@ -26,7 +26,8 @@ use crate::{
|
||||||
depcheck::common::dep_pkexec,
|
depcheck::common::dep_pkexec,
|
||||||
file_builders::{
|
file_builders::{
|
||||||
active_runtime_json::{
|
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::{
|
openvrpaths_vrpath::{
|
||||||
set_current_openvrpaths_to_profile, set_current_openvrpaths_to_steam,
|
set_current_openvrpaths_to_profile, set_current_openvrpaths_to_steam,
|
||||||
|
@ -250,9 +251,16 @@ impl App {
|
||||||
|
|
||||||
pub fn restore_openxr_openvr_files(&self) {
|
pub fn restore_openxr_openvr_files(&self) {
|
||||||
restore_runtime_entrypoint();
|
restore_runtime_entrypoint();
|
||||||
if let Err(e) = set_current_active_runtime_to_steam() {
|
if let Err(e) = remove_current_active_runtime() {
|
||||||
alert(
|
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(&format!("{e}")),
|
||||||
Some(&self.app_win.clone().upcast::<gtk::Window>()),
|
Some(&self.app_win.clone().upcast::<gtk::Window>()),
|
||||||
);
|
);
|
||||||
|
|
|
@ -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() {
|
if !path.is_file() {
|
||||||
debug!(
|
debug!(
|
||||||
"trying to set readonly on a file that does not exist: {}",
|
"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")
|
.expect("Could not get metadata for file")
|
||||||
.permissions();
|
.permissions();
|
||||||
perms.set_readonly(readonly);
|
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> {
|
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()));
|
.unwrap_or_else(|_| panic!("Failed to create dir {}", parent.to_str().unwrap()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set_file_readonly(dest, false)
|
if !dest.is_symlink() {
|
||||||
.unwrap_or_else(|_| panic!("Failed to set file {} as rw", dest.to_string_lossy()));
|
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| {
|
copy(source, dest).unwrap_or_else(|e| {
|
||||||
panic!(
|
panic!(
|
||||||
"Failed to copy {} to {}: {e}",
|
"Failed to copy {} to {}: {e}",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue