Merge branch 'feat/injectSteamEnvVarsInRuntime' into 'main'

feat: inject steam env vars in steam sniper runtime

See merge request gabmus/envision!47
This commit is contained in:
GabMus 2024-06-14 04:52:44 +00:00
commit 8c79353e05
14 changed files with 149 additions and 35 deletions

View file

@ -7,7 +7,7 @@ use nix::{
}; };
use crate::{ use crate::{
file_utils::get_writer, file_utils::get_writer_legacy,
profile::{Profile, XRServiceType}, profile::{Profile, XRServiceType},
runner::{Runner, RunnerStatus}, runner::{Runner, RunnerStatus},
}; };
@ -172,7 +172,7 @@ impl CmdRunner {
} }
fn save_log(path_s: String, log: &[String]) -> Result<(), std::io::Error> { fn save_log(path_s: String, log: &[String]) -> Result<(), std::io::Error> {
let mut writer = get_writer(&path_s); let mut writer = get_writer_legacy(&path_s);
let log_s = log.concat(); let log_s = log.concat();
writer.write_all(log_s.as_ref()) writer.write_all(log_s.as_ref())
} }

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
constants::CMD_NAME, constants::CMD_NAME,
device_prober::get_xr_usb_devices, device_prober::get_xr_usb_devices,
file_utils::get_writer, file_utils::get_writer_legacy,
paths::get_config_dir, paths::get_config_dir,
profile::Profile, profile::Profile,
profiles::{ profiles::{
@ -78,7 +78,7 @@ impl Config {
} }
fn save_to_path(&self, path_s: &String) -> Result<(), serde_json::Error> { fn save_to_path(&self, path_s: &String) -> Result<(), serde_json::Error> {
let writer = get_writer(path_s); let writer = get_writer_legacy(path_s);
serde_json::to_writer_pretty(writer, self) serde_json::to_writer_pretty(writer, self)
} }

View file

@ -1,4 +1,4 @@
use crate::{constants::APP_ID, file_utils::get_writer}; use crate::{constants::APP_ID, file_utils::get_writer_legacy};
use reqwest::{ use reqwest::{
header::{HeaderMap, USER_AGENT}, header::{HeaderMap, USER_AGENT},
Method, Method,
@ -32,7 +32,7 @@ pub fn download_file(url: String, path: String) -> JoinHandle<Result<(), reqwest
if status.is_client_error() || status.is_server_error() { if status.is_client_error() || status.is_server_error() {
return Err(res.error_for_status().unwrap_err()); return Err(res.error_for_status().unwrap_err());
} }
let mut writer = get_writer(&path); let mut writer = get_writer_legacy(&path);
for chunk in res for chunk in res
.bytes() .bytes()
.expect("Could not get HTTP response bytes") .expect("Could not get HTTP response bytes")

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
file_utils::{copy_file, deserialize_file, get_writer, set_file_readonly}, file_utils::{copy_file, deserialize_file, get_writer_legacy, set_file_readonly},
paths::{get_backup_dir, get_xdg_config_dir, get_xdg_data_dir, SYSTEM_PREFIX}, paths::{get_backup_dir, get_xdg_config_dir, get_xdg_data_dir, SYSTEM_PREFIX},
profile::{Profile, XRServiceType}, profile::{Profile, XRServiceType},
}; };
@ -69,7 +69,7 @@ fn dump_active_runtime_to_path(
active_runtime: &ActiveRuntime, active_runtime: &ActiveRuntime,
path_s: &String, path_s: &String,
) -> Result<(), serde_json::Error> { ) -> Result<(), serde_json::Error> {
let writer = get_writer(path_s); let writer = get_writer_legacy(path_s);
serde_json::to_writer_pretty(writer, active_runtime) serde_json::to_writer_pretty(writer, active_runtime)
} }

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
file_utils::{deserialize_file, get_writer}, file_utils::{deserialize_file, get_writer_legacy},
paths::get_xdg_config_dir, paths::get_xdg_config_dir,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -45,7 +45,7 @@ pub fn get_monado_autorun_config() -> MonadoAutorunConfig {
} }
fn dump_monado_autorun_config_to_path(config: &MonadoAutorunConfig, path_s: &String) { fn dump_monado_autorun_config_to_path(config: &MonadoAutorunConfig, path_s: &String) {
let writer = get_writer(path_s); let writer = get_writer_legacy(path_s);
serde_json::to_writer_pretty(writer, config).expect("Unable to save Monado Autorun config"); serde_json::to_writer_pretty(writer, config).expect("Unable to save Monado Autorun config");
} }

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
file_utils::{deserialize_file, get_writer}, file_utils::{deserialize_file, get_writer_legacy},
paths::get_xdg_config_dir, paths::get_xdg_config_dir,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -202,7 +202,7 @@ pub fn get_monado_config_v0() -> MonadoConfigV0 {
} }
fn dump_monado_config_v0_to_path(config: &MonadoConfigV0, path_s: &String) { fn dump_monado_config_v0_to_path(config: &MonadoConfigV0, path_s: &String) {
let writer = get_writer(path_s); let writer = get_writer_legacy(path_s);
serde_json::to_writer_pretty(writer, config).expect("Unable to save Monado config V0"); serde_json::to_writer_pretty(writer, config).expect("Unable to save Monado config V0");
} }

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
file_utils::{copy_file, deserialize_file, get_writer, set_file_readonly}, file_utils::{copy_file, deserialize_file, get_writer_legacy, set_file_readonly},
paths::{get_backup_dir, get_xdg_config_dir, get_xdg_data_dir}, paths::{get_backup_dir, get_xdg_config_dir, get_xdg_data_dir},
profile::Profile, profile::Profile,
}; };
@ -67,7 +67,7 @@ fn dump_openvrpaths_to_path(
ovr_paths: &OpenVrPaths, ovr_paths: &OpenVrPaths,
path_s: &String, path_s: &String,
) -> Result<(), serde_json::Error> { ) -> Result<(), serde_json::Error> {
let writer = get_writer(path_s); let writer = get_writer_legacy(path_s);
serde_json::to_writer_pretty(writer, ovr_paths) serde_json::to_writer_pretty(writer, ovr_paths)
} }

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
file_utils::{deserialize_file, get_writer}, file_utils::{deserialize_file, get_writer_legacy},
paths::get_xdg_config_dir, paths::get_xdg_config_dir,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -136,7 +136,7 @@ pub fn get_wivrn_config() -> WivrnConfig {
} }
fn dump_wivrn_config_to_path(config: &WivrnConfig, path_s: &String) { fn dump_wivrn_config_to_path(config: &WivrnConfig, path_s: &String) {
let writer = get_writer(path_s); let writer = get_writer_legacy(path_s);
serde_json::to_writer_pretty(writer, config).expect("Unable to save WiVRn config"); serde_json::to_writer_pretty(writer, config).expect("Unable to save WiVRn config");
} }

View file

@ -9,7 +9,23 @@ use std::{
path::Path, path::Path,
}; };
pub fn get_writer(path_s: &String) -> BufWriter<std::fs::File> { pub fn get_writer(path_s: &str) -> anyhow::Result<BufWriter<std::fs::File>> {
let path = Path::new(path_s);
if let Some(parent) = path.parent() {
if !parent.is_dir() {
create_dir_all(parent)?;
}
};
let file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(path)?;
Ok(BufWriter::new(file))
}
#[deprecated]
pub fn get_writer_legacy(path_s: &str) -> BufWriter<std::fs::File> {
let path = Path::new(path_s); let path = Path::new(path_s);
if let Some(parent) = path.parent() { if let Some(parent) = path.parent() {
if !parent.is_dir() { if !parent.is_dir() {
@ -52,7 +68,7 @@ pub fn deserialize_file<T: serde::de::DeserializeOwned>(path_s: &String) -> Opti
} }
} }
pub fn set_file_readonly(path_s: &String, readonly: bool) -> Result<(), std::io::Error> { pub fn set_file_readonly(path_s: &str, readonly: bool) -> Result<(), std::io::Error> {
let path = Path::new(&path_s); let path = Path::new(&path_s);
if !path.is_file() { if !path.is_file() {
println!("WARN: trying to set readonly on a file that does not exist"); println!("WARN: trying to set readonly on a file that does not exist");
@ -81,7 +97,7 @@ pub fn rm_rf(path_s: &String) {
} }
} }
pub fn copy_file(source_s: &String, dest_s: &String) { pub fn copy_file(source_s: &str, dest_s: &str) {
let source = Path::new(source_s); let source = Path::new(source_s);
let dest = Path::new(dest_s); let dest = Path::new(dest_s);
if let Some(parent) = dest.parent() { if let Some(parent) = dest.parent() {

View file

@ -11,6 +11,7 @@ use relm4::{
gtk::{self, gdk, gio, glib, prelude::*}, gtk::{self, gdk, gio, glib, prelude::*},
MessageBroker, RelmApp, MessageBroker, RelmApp,
}; };
use steam_linux_runtime_injector::restore_runtime_entrypoint;
use ui::app::{App, AppInit}; use ui::app::{App, AppInit};
pub mod adb; pub mod adb;
@ -37,6 +38,7 @@ pub mod profile;
pub mod profiles; pub mod profiles;
pub mod runner; pub mod runner;
pub mod runner_pipeline; pub mod runner_pipeline;
pub mod steam_linux_runtime_injector;
pub mod steamvr_utils; pub mod steamvr_utils;
pub mod ui; pub mod ui;
pub mod xr_devices; pub mod xr_devices;
@ -60,6 +62,7 @@ fn restore_steam_xr_files() {
}; };
} }
} }
restore_runtime_entrypoint();
} }
fn main() -> Result<()> { fn main() -> Result<()> {

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
file_utils::get_writer, file_utils::get_writer_legacy,
paths::{get_data_dir, get_ipc_file_path, BWRAP_SYSTEM_PREFIX, SYSTEM_PREFIX}, paths::{get_data_dir, get_ipc_file_path, BWRAP_SYSTEM_PREFIX, SYSTEM_PREFIX},
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -288,14 +288,19 @@ impl Default for Profile {
} }
impl Profile { impl Profile {
pub fn get_steam_launch_options(&self) -> String { pub fn get_env_vars(&self) -> Vec<String> {
if self.can_be_built {
return vec![
"PRESSURE_VESSEL_FILESYSTEMS_RW=\"$XDG_RUNTIME_DIR/wivrn_comp_ipc:$XDG_RUNTIME_DIR/monado_comp_ipc\"".into(),
];
}
vec![ vec![
// format!( // format!(
// "VR_OVERRIDE={opencomp}/build", // "VR_OVERRIDE={opencomp}/build",
// opencomp = self.opencomposite_path, // opencomp = self.opencomposite_path,
// ), // ),
format!( format!(
"XR_RUNTIME_JSON={prefix}/share/openxr/1/openxr_{runtime}.json", "XR_RUNTIME_JSON=\"{prefix}/share/openxr/1/openxr_{runtime}.json\"",
prefix = match self.prefix.as_str() { prefix = match self.prefix.as_str() {
SYSTEM_PREFIX => BWRAP_SYSTEM_PREFIX, SYSTEM_PREFIX => BWRAP_SYSTEM_PREFIX,
other => other, other => other,
@ -306,12 +311,16 @@ impl Profile {
} }
), ),
format!( format!(
"PRESSURE_VESSEL_FILESYSTEMS_RW={path}", "PRESSURE_VESSEL_FILESYSTEMS_RW=\"{path}\"",
path = get_ipc_file_path(&self.xrservice_type), path = get_ipc_file_path(&self.xrservice_type),
), ),
"%command%".into(),
] ]
.join(" ") }
pub fn get_steam_launch_options(&self) -> String {
let mut vars = self.get_env_vars();
vars.push("%command%".into());
vars.join(" ")
} }
pub fn get_survive_cli_path(&self) -> Option<String> { pub fn get_survive_cli_path(&self) -> Option<String> {
@ -329,7 +338,7 @@ impl Profile {
} }
pub fn dump_profile(&self, path_s: &String) { pub fn dump_profile(&self, path_s: &String) {
let writer = get_writer(path_s); let writer = get_writer_legacy(path_s);
serde_json::to_writer_pretty(writer, self).expect("Could not write profile") serde_json::to_writer_pretty(writer, self).expect("Could not write profile")
} }

View file

@ -0,0 +1,80 @@
use crate::{
file_utils::{copy_file, get_writer},
paths::{get_backup_dir, get_home_dir, get_xdg_data_dir},
profile::Profile,
};
use anyhow::bail;
use std::{
fs::read_to_string,
io::{Read, Write},
path::Path,
};
fn get_runtime_entrypoint_path() -> Option<String> {
let mut out = format!(
"{home}/.steam/steam/steamapps/common/SteamLinuxRuntime_sniper/_v2-entry-point",
home = get_home_dir(),
);
if Path::new(&out).is_file() {
return Some(out);
}
out = format!(
"{data}/Steam/ubuntu12_64/steam-runtime-sniper/_v2-entry-point",
data = get_xdg_data_dir()
);
if Path::new(&out).is_file() {
return Some(out);
}
None
}
fn get_backup_runtime_entrypoint_location() -> String {
format!("{backup}/_v2-entry-point.bak", backup = get_backup_dir())
}
fn backup_runtime_entrypoint(path: &str) {
copy_file(&path, &get_backup_runtime_entrypoint_location());
}
pub fn restore_runtime_entrypoint() {
if let Some(path) = get_runtime_entrypoint_path() {
let backup = get_backup_runtime_entrypoint_location();
if Path::new(&backup).is_file() {
copy_file(&backup, &path);
}
}
}
fn append_to_runtime_entrypoint(data: &str, path: &str) -> anyhow::Result<()> {
let existing = read_to_string(path)?;
let new = existing.replace(
"exec \"${here}/${run}\" \"$@\"\nexit 125",
&format!("\n\n# envision\n{data}\n\nexec \"${{here}}/${{run}}\" \"$@\"\nexit 125"),
);
let mut writer = get_writer(path)?;
writer.write(&new.as_bytes())?;
Ok(())
}
pub fn set_runtime_entrypoint_launch_opts_from_profile(profile: &Profile) -> anyhow::Result<()> {
restore_runtime_entrypoint();
if let Some(dest) = get_runtime_entrypoint_path() {
backup_runtime_entrypoint(&dest);
append_to_runtime_entrypoint(
&profile
.get_env_vars()
.iter()
.map(|ev| "export ".to_string() + ev)
.collect::<Vec<String>>()
.join("\n"),
&dest,
)?;
return Ok(());
}
bail!("Could not find valid runtime entrypoint");
}

View file

@ -39,6 +39,9 @@ use crate::log_parser::MonadoLog;
use crate::paths::{get_data_dir, get_ipc_file_path}; use crate::paths::{get_data_dir, get_ipc_file_path};
use crate::profile::{Profile, XRServiceType}; use crate::profile::{Profile, XRServiceType};
use crate::stateless_action; use crate::stateless_action;
use crate::steam_linux_runtime_injector::{
restore_runtime_entrypoint, set_runtime_entrypoint_launch_opts_from_profile,
};
use crate::ui::build_window::{BuildWindowMsg, BuildWindowOutMsg}; use crate::ui::build_window::{BuildWindowMsg, BuildWindowOutMsg};
use crate::ui::debug_view::{DebugViewInit, DebugViewOutMsg}; use crate::ui::debug_view::{DebugViewInit, DebugViewOutMsg};
use crate::ui::libsurvive_setup_window::LibsurviveSetupMsg; use crate::ui::libsurvive_setup_window::LibsurviveSetupMsg;
@ -199,7 +202,7 @@ impl App {
debug, debug,
); );
worker.start(); worker.start();
if let Some(autostart_cmd) = prof.autostart_command { if let Some(autostart_cmd) = &prof.autostart_command {
let autostart_worker = JobWorker::new_with_timer( let autostart_worker = JobWorker::new_with_timer(
Duration::from_secs(5), Duration::from_secs(5),
WorkerJob::new_cmd( WorkerJob::new_cmd(
@ -222,6 +225,8 @@ impl App {
.emit(MainViewMsg::XRServiceActiveChanged( .emit(MainViewMsg::XRServiceActiveChanged(
true, true,
Some(self.get_selected_profile()), Some(self.get_selected_profile()),
// show launch opts only if setting the runtime entrypoint fails
set_runtime_entrypoint_launch_opts_from_profile(&prof).is_err(),
)); ));
self.debug_view self.debug_view
.sender() .sender()
@ -265,10 +270,9 @@ impl App {
worker.stop(); worker.stop();
} }
self.libmonado = None; self.libmonado = None;
self.restore_openxr_openvr_files();
self.main_view self.main_view
.sender() .sender()
.emit(MainViewMsg::XRServiceActiveChanged(false, None)); .emit(MainViewMsg::XRServiceActiveChanged(false, None, false));
self.debug_view self.debug_view
.sender() .sender()
.emit(DebugViewMsg::XRServiceActiveChanged(false)); .emit(DebugViewMsg::XRServiceActiveChanged(false));
@ -334,9 +338,11 @@ impl SimpleComponent for App {
} }
} }
Msg::OnServiceExit(code) => { Msg::OnServiceExit(code) => {
self.restore_openxr_openvr_files();
restore_runtime_entrypoint();
self.main_view self.main_view
.sender() .sender()
.emit(MainViewMsg::XRServiceActiveChanged(false, None)); .emit(MainViewMsg::XRServiceActiveChanged(false, None, false));
self.debug_view self.debug_view
.sender() .sender()
.emit(DebugViewMsg::XRServiceActiveChanged(false)); .emit(DebugViewMsg::XRServiceActiveChanged(false));

View file

@ -55,7 +55,7 @@ pub enum MainViewMsg {
ClockTicking, ClockTicking,
StartStopClicked, StartStopClicked,
RestartXRService, RestartXRService,
XRServiceActiveChanged(bool, Option<Profile>), XRServiceActiveChanged(bool, Option<Profile>, bool),
EnableDebugViewChanged(bool), EnableDebugViewChanged(bool),
UpdateProfiles(Vec<Profile>, Config), UpdateProfiles(Vec<Profile>, Config),
SetSelectedProfile(u32), SetSelectedProfile(u32),
@ -450,7 +450,7 @@ impl SimpleComponent for MainView {
.output(Self::Output::RestartXRService) .output(Self::Output::RestartXRService)
.expect("Sender output failed"); .expect("Sender output failed");
} }
Self::Input::XRServiceActiveChanged(active, profile) => { Self::Input::XRServiceActiveChanged(active, profile, show_launch_opts) => {
self.set_xrservice_active(active); self.set_xrservice_active(active);
self.steamvr_calibration_box self.steamvr_calibration_box
.sender() .sender()
@ -458,9 +458,9 @@ impl SimpleComponent for MainView {
if !active { if !active {
sender.input(Self::Input::UpdateDevices(vec![])); sender.input(Self::Input::UpdateDevices(vec![]));
} }
self.steam_launch_options_box self.steam_launch_options_box.sender().emit(
.sender() SteamLaunchOptionsBoxMsg::UpdateXRServiceActive(show_launch_opts),
.emit(SteamLaunchOptionsBoxMsg::UpdateXRServiceActive(active)); );
match profile { match profile {
None => {} None => {}
Some(prof) => { Some(prof) => {