Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions alvr/dashboard/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ rust-version.workspace = true
authors.workspace = true
license.workspace = true

[features]
steamvr-server = []
# monado-server = [] # todo
mock-server = []
default = ["steamvr-server"]

[dependencies]
alvr_adb.workspace = true
alvr_common.workspace = true
Expand Down
4 changes: 2 additions & 2 deletions alvr/dashboard/src/dashboard/components/devices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ impl DevicesTab {

#[cfg(not(target_arch = "wasm32"))]
ui.with_layout(Layout::right_to_left(eframe::emath::Align::Center), |ui| {
if ui.button("Launch SteamVR").clicked() {
crate::steamvr_launcher::LAUNCHER.lock().launch_steamvr();
if ui.button("Launch server").clicked() {
crate::server_launcher::LAUNCHER.lock().launch_server();
}
});
})
Expand Down
20 changes: 10 additions & 10 deletions alvr/dashboard/src/dashboard/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ impl Dashboard {
let server_restarting = Arc::clone(&self.server_restarting);
let condvar = Arc::clone(&self.server_restarting_condvar);
move || {
crate::steamvr_launcher::LAUNCHER.lock().restart_steamvr();
crate::server_launcher::LAUNCHER.lock().restart_server();

*server_restarting.lock() = false;
condvar.notify_one();
Expand Down Expand Up @@ -170,7 +170,7 @@ impl eframe::App for Dashboard {
// todo: find a way to center both vertically and horizontally
ui.vertical_centered(|ui| {
ui.add_space(100.0);
ui.heading(RichText::new("SteamVR is restarting").size(30.0));
ui.heading(RichText::new("The server is restarting").size(30.0));
});
});

Expand Down Expand Up @@ -231,16 +231,16 @@ impl eframe::App for Dashboard {
ui.add_space(5.0);

if connected_to_server {
if ui.button("Restart SteamVR").clicked() {
if ui.button("Restart server").clicked() {
self.restart_steamvr(&mut requests);
}
} else if ui.button("Launch SteamVR").clicked() {
crate::steamvr_launcher::LAUNCHER.lock().launch_steamvr();
} else if ui.button("Launch server").clicked() {
crate::server_launcher::LAUNCHER.lock().launch_server();
}

ui.horizontal(|ui| {
ui.add_space(5.0);
ui.label(RichText::new("SteamVR:").size(13.0));
ui.label(RichText::new("Server:").size(13.0));
ui.add_space(-10.0);
if connected_to_server {
ui.label(
Expand Down Expand Up @@ -307,9 +307,9 @@ impl eframe::App for Dashboard {
let shutdown_alvr = || {
self.data_sources.request(ServerRequest::ShutdownSteamvr);

crate::steamvr_launcher::LAUNCHER
crate::server_launcher::LAUNCHER
.lock()
.ensure_steamvr_shutdown();
.ensure_server_shutdown();
};

if let Some(popup) = &self.new_version_popup
Expand All @@ -330,8 +330,8 @@ impl eframe::App for Dashboard {
&& self.session.as_ref().is_some_and(|s| {
s.to_settings()
.extra
.steamvr_launcher
.open_close_steamvr_with_dashboard
.server_launcher
.open_close_server_with_dashboard
})
{
shutdown_alvr();
Expand Down
8 changes: 4 additions & 4 deletions alvr/dashboard/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ mod linux_checks;
#[cfg(not(target_arch = "wasm32"))]
mod logging_backend;
#[cfg(not(target_arch = "wasm32"))]
mod steamvr_launcher;
mod server_launcher;

#[cfg(not(target_arch = "wasm32"))]
use data_sources::DataSources;
Expand Down Expand Up @@ -65,10 +65,10 @@ fn main() {
if data_sources::get_read_only_local_session()
.settings()
.extra
.steamvr_launcher
.open_close_steamvr_with_dashboard
.server_launcher
.open_close_server_with_dashboard
{
steamvr_launcher::LAUNCHER.lock().launch_steamvr()
server_launcher::LAUNCHER.lock().launch_server()
}

let ico = IconDir::read(Cursor::new(include_bytes!("../resources/dashboard.ico"))).unwrap();
Expand Down
198 changes: 198 additions & 0 deletions alvr/dashboard/src/server_launcher/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
#[cfg(target_os = "linux")]
mod linux_steamvr;
#[cfg(windows)]
mod windows_steamvr;

use crate::data_sources;
use alvr_adb::commands as adb;
use alvr_common::{
anyhow::{Context, Result},
debug,
glam::bool,
parking_lot::Mutex,
warn,
};
use alvr_filesystem as afs;
use serde_json::{self, json};
use std::{
ffi::OsStr,
fs,
marker::PhantomData,
process::Command,
slice, thread,
time::{Duration, Instant},
};
use sysinfo::{ProcessesToUpdate, System};

const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(10);
const DRIVER_KEY: &str = "driver_alvr_server";
const BLOCKED_KEY: &str = "blocked_by_safe_mode";

pub fn is_server_running() -> bool {
#[cfg(feature = "steamvr-server")]
let process_name = afs::exec_fname("vrserver");
#[cfg(feature = "mock-server")]
let process_name = afs::mock_server_fname();

System::new_all()
.processes_by_name(OsStr::new(&process_name))
.count()
!= 0
}

pub fn maybe_kill_server() {
let mut system = System::new_all();

#[cfg(feature = "steamvr-server")]
let process_names = [afs::exec_fname("vrmonitor"), afs::exec_fname("vrserver")];
#[cfg(feature = "mock-server")]
let process_names = [afs::mock_server_fname()];

#[cfg_attr(target_os = "macos", expect(unused_variables))]
for process_name in process_names {
system.refresh_processes(ProcessesToUpdate::All, true);

for process in system.processes_by_name(OsStr::new(&process_name)) {
debug!("Killing {}", process_name);

#[cfg(target_os = "linux")]
linux_steamvr::terminate_process(process);
#[cfg(windows)]
windows_steamvr::kill_process(process.pid().as_u32());

thread::sleep(Duration::from_secs(1));
}
}
}

fn unblock_alvr_driver() -> Result<()> {
if !cfg!(target_os = "linux") {
return Ok(());
}

let path = alvr_server_io::steamvr_settings_file_path()?;
let text = fs::read_to_string(&path).with_context(|| format!("Failed to read {path:?}"))?;
let new_text = unblock_alvr_driver_within_vrsettings(text.as_str())
.with_context(|| "Failed to rewrite .vrsettings.")?;
fs::write(&path, new_text)
.with_context(|| "Failed to write .vrsettings back after changing it.")?;
Ok(())
}

// Reads and writes back steamvr.vrsettings in order to
// ensure the ALVR driver is not blocked (safe mode).
fn unblock_alvr_driver_within_vrsettings(text: &str) -> Result<String> {
let mut settings = serde_json::from_str::<serde_json::Value>(text)?;
let values = settings
.as_object_mut()
.with_context(|| "Failed to parse .vrsettings.")?;
let blocked = values
.get(DRIVER_KEY)
.and_then(|driver| driver.get(BLOCKED_KEY))
.and_then(|blocked| blocked.as_bool())
.unwrap_or(false);

if blocked {
debug!("Unblocking ALVR driver in SteamVR.");
if !values.contains_key(DRIVER_KEY) {
values.insert(DRIVER_KEY.into(), json!({}));
}
let driver = settings[DRIVER_KEY]
.as_object_mut()
.with_context(|| "Did not find ALVR key in settings.")?;
driver.insert(BLOCKED_KEY.into(), json!(false)); // overwrites if present
} else {
debug!("ALVR is not blocked in SteamVR.");
}

Ok(serde_json::to_string_pretty(&settings)?)
}

pub struct Launcher {
_phantom: PhantomData<()>,
}

impl Launcher {
pub fn launch_server(&self) {
let filesystem_layout = crate::get_filesystem_layout();

// The ADB server might be left running because of a unclean termination of SteamVR
// Note that this will also kill a system wide ADB server not started by ALVR
let wired_enabled = data_sources::get_read_only_local_session()
.session()
.client_connections
.contains_key(alvr_sockets::WIRED_CLIENT_HOSTNAME);
if wired_enabled && let Some(path) = adb::get_adb_path(&filesystem_layout) {
adb::kill_server(&path).ok();
}

if cfg!(feature = "steamvr-server") {
#[cfg(target_os = "linux")]
linux_steamvr::linux_hardware_checks();

let alvr_driver_dir = &filesystem_layout.openvr_driver_root_dir;

// Make sure to unregister any other ALVR driver because it would cause a socket conflict
let other_alvr_dirs = alvr_server_io::get_registered_drivers()
.unwrap_or_default()
.into_iter()
.filter(|path| {
path.to_string_lossy().to_lowercase().contains("alvr")
&& path != alvr_driver_dir
})
.collect::<Vec<_>>();
alvr_server_io::driver_registration(&other_alvr_dirs, false).ok();

alvr_server_io::driver_registration(slice::from_ref(alvr_driver_dir), true).ok();

if let Err(err) = unblock_alvr_driver() {
warn!("Failed to unblock ALVR driver: {:?}", err);
}

#[cfg(target_os = "linux")]
{
let vrcompositor_wrap_result = linux_steamvr::maybe_wrap_vrcompositor_launcher();
alvr_common::show_err(linux_steamvr::maybe_wrap_vrcompositor_launcher());
if vrcompositor_wrap_result.is_err() {
return;
}
}
}

if !is_server_running() {
debug!("Server is dead. Launching...");

if cfg!(feature = "steamvr-server") {
#[cfg(windows)]
windows_steamvr::start_steamvr();

#[cfg(target_os = "linux")]
linux_steamvr::start_steamvr();
} else if cfg!(feature = "mock-server") {
Command::new(filesystem_layout.mock_server_exe())
.spawn()
.ok();
}
}
}

pub fn ensure_server_shutdown(&self) {
debug!("Waiting for server to shutdown...");
let start_time = Instant::now();
while start_time.elapsed() < SHUTDOWN_TIMEOUT && is_server_running() {
thread::sleep(Duration::from_millis(500));
}

maybe_kill_server();
}

pub fn restart_server(&self) {
self.ensure_server_shutdown();
self.launch_server();
}
}

// Singleton with exclusive access
pub static LAUNCHER: Mutex<Launcher> = Mutex::new(Launcher {
_phantom: PhantomData,
});
Loading
Loading