feat: move steam library folders parser to own module; function to find steam openxr json; format
Some checks failed
/ cargo-fmtcheck (push) Has been cancelled
/ cargo-clippy (push) Has been cancelled
/ cargo-test (push) Has been cancelled
/ appimage (push) Has been cancelled

This commit is contained in:
Gabriele Musco 2024-12-02 18:25:00 +01:00
commit a9fa4f8cf4
15 changed files with 119 additions and 93 deletions

View file

@ -1,7 +1,10 @@
use crate::{ use crate::{
paths::{get_backup_dir, SYSTEM_PREFIX}, paths::{get_backup_dir, SYSTEM_PREFIX},
profile::Profile, profile::Profile,
util::file_utils::{copy_file, deserialize_file, get_writer, set_file_readonly}, util::{
file_utils::{copy_file, deserialize_file, get_writer, set_file_readonly},
steam_library_folder::SteamLibraryFolder,
},
xdg::XDG, xdg::XDG,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -9,6 +12,7 @@ use std::{
fs::remove_file, fs::remove_file,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use tracing::error;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ActiveRuntimeInnerRuntime { pub struct ActiveRuntimeInnerRuntime {
@ -38,6 +42,23 @@ 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))
} }
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 { fn get_backup_steam_active_runtime_path() -> PathBuf {
get_backup_dir().join("active_runtime.json.steam.bak") get_backup_dir().join("active_runtime.json.steam.bak")
} }

View file

@ -5,7 +5,6 @@ use file_builders::{
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;
use tracing::warn;
use relm4::{ use relm4::{
adw, adw,
gtk::{self, gdk, gio, glib, prelude::*}, gtk::{self, gdk, gio, glib, prelude::*},
@ -13,6 +12,7 @@ use relm4::{
}; };
use std::env; use std::env;
use steam_linux_runtime_injector::restore_runtime_entrypoint; use steam_linux_runtime_injector::restore_runtime_entrypoint;
use tracing::warn;
use tracing_subscriber::{filter::LevelFilter, EnvFilter}; use tracing_subscriber::{filter::LevelFilter, EnvFilter};
use ui::{ use ui::{
app::{App, AppInit, Msg}, app::{App, AppInit, Msg},

View file

@ -1,76 +1,33 @@
use crate::{ use crate::{
paths::{get_backup_dir, get_home_dir}, paths::get_backup_dir,
profile::Profile, profile::Profile,
util::file_utils::{copy_file, get_writer}, util::{
file_utils::{copy_file, get_writer},
steam_library_folder::SteamLibraryFolder,
},
}; };
use anyhow::bail; use anyhow::bail;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use tracing::error;
use serde::Deserialize;
use std::{ use std::{
collections::HashMap,
fs::read_to_string, fs::read_to_string,
io::Write, io::Write,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use tracing::error;
#[derive(Deserialize)]
struct LibraryFolder {
pub path: String,
pub apps: HashMap<u32, usize>,
}
pub const PRESSURE_VESSEL_STEAM_APPID: u32 = 1628350; pub const PRESSURE_VESSEL_STEAM_APPID: u32 = 1628350;
fn get_steam_main_dir_path() -> anyhow::Result<PathBuf> {
let steam_root: PathBuf = get_home_dir().join(".steam/root");
if steam_root.is_symlink() {
Ok(steam_root.read_link()?)
} else if steam_root.is_dir() {
Ok(steam_root)
} else {
bail!(
"Canonical steam root '{}' is not a dir nor a symlink!",
steam_root.to_string_lossy()
)
}
}
fn parse_steam_libraryfolders_vdf(path: &Path) -> anyhow::Result<HashMap<u32, LibraryFolder>> {
Ok(keyvalues_serde::from_str(read_to_string(path)?.as_str())?)
}
fn get_runtime_entrypoint_path() -> Option<PathBuf> { fn get_runtime_entrypoint_path() -> Option<PathBuf> {
match get_steam_main_dir_path() { match SteamLibraryFolder::get_folders() {
Ok(steam_root) => { Ok(libraryfolders) => libraryfolders
let steam_libraryfolders_path = steam_root.join("steamapps/libraryfolders.vdf");
if !steam_libraryfolders_path.is_file() {
error!(
"Steam libraryfolders.vdf does not exist in its canonical location {}",
steam_libraryfolders_path.to_string_lossy()
);
return None;
}
let libraryfolders: HashMap<u32, LibraryFolder> =
parse_steam_libraryfolders_vdf(&steam_libraryfolders_path).ok()?;
libraryfolders
.iter() .iter()
.find(|(_, libraryfolder)| { .find(|(_, folder)| folder.apps.contains_key(&PRESSURE_VESSEL_STEAM_APPID))
libraryfolder .map(|(_, folder)| {
.apps PathBuf::from(&folder.path)
.contains_key(&PRESSURE_VESSEL_STEAM_APPID)
})
.map(|(_, libraryfolder)| {
PathBuf::from(&libraryfolder.path)
.join("steamapps/common/SteamLinuxRuntime_sniper/_v2-entry-point") .join("steamapps/common/SteamLinuxRuntime_sniper/_v2-entry-point")
}) }),
}
Err(e) => { Err(e) => {
error!("Error getting steam root path: {e}"); error!("unable to get runtime entrypoint path: {e}");
None None
} }
} }
@ -126,22 +83,3 @@ pub fn set_runtime_entrypoint_launch_opts_from_profile(profile: &Profile) -> any
} }
bail!("Could not find valid runtime entrypoint"); bail!("Could not find valid runtime entrypoint");
} }
#[cfg(test)]
mod tests {
use std::path::Path;
use super::parse_steam_libraryfolders_vdf;
#[test]
fn deserialize_steam_libraryfolders_vdf() {
let lf = parse_steam_libraryfolders_vdf(Path::new("./test/files/steam_libraryfolders.vdf"))
.unwrap();
assert_eq!(lf.len(), 1);
let first = lf.get(&0).unwrap();
assert_eq!(first.path, "/home/gabmus/.local/share/Steam");
assert_eq!(first.apps.len(), 10);
assert_eq!(first.apps.get(&228980).unwrap(), &29212173);
assert_eq!(first.apps.get(&632360).unwrap(), &0);
}
}

View file

@ -47,7 +47,6 @@ use crate::{
}; };
use adw::{prelude::*, ResponseAppearance}; use adw::{prelude::*, ResponseAppearance};
use gtk::glib::{self, clone}; use gtk::glib::{self, clone};
use tracing::error;
use notify_rust::NotificationHandle; use notify_rust::NotificationHandle;
use relm4::{ use relm4::{
actions::{AccelsPlus, ActionGroupName, RelmAction, RelmActionGroup}, actions::{AccelsPlus, ActionGroupName, RelmAction, RelmActionGroup},
@ -55,6 +54,7 @@ use relm4::{
prelude::*, prelude::*,
}; };
use std::{collections::VecDeque, fs::remove_file, time::Duration}; use std::{collections::VecDeque, fs::remove_file, time::Duration};
use tracing::error;
pub struct App { pub struct App {
application: adw::Application, application: adw::Application,

View file

@ -6,9 +6,9 @@ use crate::{
profile::{Profile, XRServiceType}, profile::{Profile, XRServiceType},
}; };
use gtk::prelude::*; use gtk::prelude::*;
use tracing::error;
use relm4::{new_action_group, new_stateless_action, prelude::*}; use relm4::{new_action_group, new_stateless_action, prelude::*};
use std::fs::remove_file; use std::fs::remove_file;
use tracing::error;
const WIVRN_LATEST_RELEASE_APK_URL: &str = const WIVRN_LATEST_RELEASE_APK_URL: &str =
"https://github.com/WiVRn/WiVRn/releases/latest/download/WiVRn-standard-release.apk"; "https://github.com/WiVRn/WiVRn/releases/latest/download/WiVRn-standard-release.apk";
@ -185,10 +185,7 @@ impl AsyncComponent for InstallWivrnBox {
let existing = cache_file_path(WIVRN_LATEST_RELEASE_APK_URL, Some("apk")); let existing = cache_file_path(WIVRN_LATEST_RELEASE_APK_URL, Some("apk"));
if existing.is_file() { if existing.is_file() {
if let Err(e) = remove_file(&existing) { if let Err(e) = remove_file(&existing) {
error!( error!("failed to remove file {}: {e}", existing.to_string_lossy());
"failed to remove file {}: {e}",
existing.to_string_lossy()
);
} }
} }
sender.input(Self::Input::DoInstall(WIVRN_LATEST_RELEASE_APK_URL.into())); sender.input(Self::Input::DoInstall(WIVRN_LATEST_RELEASE_APK_URL.into()));

View file

@ -4,7 +4,6 @@ use self::{
state::JobWorkerState, state::JobWorkerState,
}; };
use crate::profile::Profile; use crate::profile::Profile;
use tracing::{error, warn};
use nix::sys::signal::{ use nix::sys::signal::{
kill, kill,
Signal::{SIGKILL, SIGTERM}, Signal::{SIGKILL, SIGTERM},
@ -16,6 +15,7 @@ use std::{
thread::{self, sleep}, thread::{self, sleep},
time::Duration, time::Duration,
}; };
use tracing::{error, warn};
pub mod internal_worker; pub mod internal_worker;
pub mod job; pub mod job;

View file

@ -30,13 +30,13 @@ use crate::{
}; };
use adw::{prelude::*, ResponseAppearance}; use adw::{prelude::*, ResponseAppearance};
use gtk::glib::clone; use gtk::glib::clone;
use tracing::{error, warn};
use relm4::{ use relm4::{
actions::{ActionGroupName, RelmAction, RelmActionGroup}, actions::{ActionGroupName, RelmAction, RelmActionGroup},
new_action_group, new_stateless_action, new_action_group, new_stateless_action,
prelude::*, prelude::*,
}; };
use std::{fs::read_to_string, io::Write}; use std::{fs::read_to_string, io::Write};
use tracing::{error, warn};
#[tracker::track] #[tracker::track]
pub struct MainView { pub struct MainView {

View file

@ -1,9 +1,9 @@
use crate::{constants::APP_NAME, xdg::XDG}; use crate::{constants::APP_NAME, xdg::XDG};
use tracing::{debug, error};
use relm4::{ use relm4::{
gtk::{self, prelude::*}, gtk::{self, prelude::*},
ComponentParts, ComponentSender, SimpleComponent, ComponentParts, ComponentSender, SimpleComponent,
}; };
use tracing::{debug, error};
#[tracker::track] #[tracker::track]
pub struct OpenHmdCalibrationBox { pub struct OpenHmdCalibrationBox {

View file

@ -4,7 +4,6 @@ use super::job_worker::{
JobWorker, JobWorker,
}; };
use crate::paths::get_steamvr_bin_dir_path; use crate::paths::get_steamvr_bin_dir_path;
use tracing::error;
use relm4::{ use relm4::{
gtk::{self, prelude::*}, gtk::{self, prelude::*},
ComponentParts, ComponentSender, RelmWidgetExt, SimpleComponent, ComponentParts, ComponentSender, RelmWidgetExt, SimpleComponent,
@ -15,6 +14,7 @@ use std::{
thread::sleep, thread::sleep,
time::Duration, time::Duration,
}; };
use tracing::error;
#[tracker::track] #[tracker::track]
pub struct SteamVrCalibrationBox { pub struct SteamVrCalibrationBox {

View file

@ -20,8 +20,8 @@ use crate::{
}; };
use adw::prelude::*; use adw::prelude::*;
use gtk::glib::clone; use gtk::glib::clone;
use tracing::error;
use relm4::{factory::AsyncFactoryVecDeque, prelude::*}; use relm4::{factory::AsyncFactoryVecDeque, prelude::*};
use tracing::error;
#[tracker::track] #[tracker::track]
pub struct WivrnConfEditor { pub struct WivrnConfEditor {

View file

@ -6,8 +6,8 @@ use crate::{
profile::{Profile, XRServiceType}, profile::{Profile, XRServiceType},
}; };
use gtk::prelude::*; use gtk::prelude::*;
use tracing::error;
use relm4::prelude::*; use relm4::prelude::*;
use tracing::error;
#[derive(PartialEq, Eq, Debug, Clone)] #[derive(PartialEq, Eq, Debug, Clone)]
pub enum StartClientStatus { pub enum StartClientStatus {

View file

@ -1,6 +1,5 @@
use crate::{async_process::async_process, profile::Profile}; use crate::{async_process::async_process, profile::Profile};
use anyhow::bail; use anyhow::bail;
use tracing::{debug, error};
use nix::{ use nix::{
errno::Errno, errno::Errno,
sys::statvfs::{statvfs, FsFlags}, sys::statvfs::{statvfs, FsFlags},
@ -10,6 +9,7 @@ use std::{
io::{BufReader, BufWriter}, io::{BufReader, BufWriter},
path::Path, path::Path,
}; };
use tracing::{debug, error};
pub fn get_writer(path: &Path) -> anyhow::Result<BufWriter<std::fs::File>> { pub fn get_writer(path: &Path) -> anyhow::Result<BufWriter<std::fs::File>> {
if let Some(parent) = path.parent() { if let Some(parent) = path.parent() {

View file

@ -1,3 +1,4 @@
pub mod file_utils; pub mod file_utils;
pub mod hash; pub mod hash;
pub mod steam_library_folder;
pub mod steamvr_utils; pub mod steamvr_utils;

View file

@ -0,0 +1,69 @@
use crate::paths::get_home_dir;
use anyhow::bail;
use serde::Deserialize;
use std::{
collections::HashMap,
fs::read_to_string,
path::{Path, PathBuf},
};
#[derive(Deserialize)]
pub struct SteamLibraryFolder {
pub path: String,
pub apps: HashMap<u32, usize>,
}
fn get_steam_main_dir_path() -> anyhow::Result<PathBuf> {
let steam_root: PathBuf = get_home_dir().join(".steam/root");
if steam_root.is_symlink() {
Ok(steam_root.read_link()?)
} else if steam_root.is_dir() {
Ok(steam_root)
} else {
bail!(
"Canonical steam root '{}' is not a dir nor a symlink!",
steam_root.to_string_lossy()
)
}
}
impl SteamLibraryFolder {
pub fn get_folders() -> anyhow::Result<HashMap<u32, Self>> {
let libraryfolders_path = get_steam_main_dir_path()?.join("steamapps/libraryfolders.vdf");
if !libraryfolders_path.is_file() {
bail!(
"Steam libraryfolders.vdf does not exist in its canonical location {}",
libraryfolders_path.to_string_lossy()
);
}
Self::get_folders_from_path(&libraryfolders_path)
}
/// Do not use this: use get_folders() instead as it always uses Steam's
/// canonical root path. This is intended to be directly used only for
/// unit tests
pub fn get_folders_from_path(p: &Path) -> anyhow::Result<HashMap<u32, Self>> {
Ok(keyvalues_serde::from_str(read_to_string(p)?.as_str())?)
}
}
#[cfg(test)]
mod tests {
use super::SteamLibraryFolder;
use std::path::Path;
#[test]
fn deserialize_steam_libraryfolders_vdf() {
let lf = SteamLibraryFolder::get_folders_from_path(Path::new(
"./test/files/steam_libraryfolders.vdf",
))
.unwrap();
assert_eq!(lf.len(), 1);
let first = lf.get(&0).unwrap();
assert_eq!(first.path, "/home/gabmus/.local/share/Steam");
assert_eq!(first.apps.len(), 10);
assert_eq!(first.apps.get(&228980).unwrap(), &29212173);
assert_eq!(first.apps.get(&632360).unwrap(), &0);
}
}

View file

@ -1,6 +1,6 @@
use libmonado::{self, BatteryStatus, DeviceRole}; use libmonado::{self, BatteryStatus, DeviceRole};
use tracing::error;
use std::{collections::HashMap, fmt::Display, slice::Iter}; use std::{collections::HashMap, fmt::Display, slice::Iter};
use tracing::error;
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub enum XRDeviceRole { pub enum XRDeviceRole {