feat: tooling in place to support wivrn as an alternative to monado

This commit is contained in:
Gabriele Musco 2023-06-19 07:45:39 +02:00
commit 75e4ffb3b4
No known key found for this signature in database
GPG key ID: 1068D795C80E51DE
8 changed files with 143 additions and 54 deletions

30
scripts/build_wivrn.sh Executable file
View file

@ -0,0 +1,30 @@
#!/bin/bash
# exit on error
# echo commands
set -ev
REPO_DIR=$1
PREFIX=$2
if [[ -z $REPO_DIR ]] || [[ -z $PREFIX ]]; then
echo "Usage: $0 REPO_DIR PREFIX"
exit 1
fi
"$(dirname -- "$0")/_clone_or_pull.sh" "https://github.com/Meumeu/WiVRn" "$REPO_DIR"
cd "$REPO_DIR"
mkdir -p build
cd build
export PKG_CONFIG_PATH="${PREFIX}/lib/pkgconfig"
cmake -B build-server -DCMAKE_BUILD_TYPE=Release \
-DWIVRN_BUILD_CLIENT=OFF \
-DCMAKE_LIBDIR="${PREFIX}/lib" \
-DCMAKE_INSTALL_PREFIX="${PREFIX}" \
-DCMAKE_C_FLAGS="-Wl,-rpath ${PREFIX}/lib" \
-DCMAKE_CXX_FLAGS="-Wl,-rpath ${PREFIX}/lib" \
.. -GNinja
cmake --build build-server
ninja install

View file

@ -0,0 +1,14 @@
use crate::{profile::Profile, runner::Runner, constants::PKG_DATA_DIR};
pub fn get_build_wivrn_runner(profile: Profile) -> Runner {
let runner = Runner::new(
None,
format!("{sysdata}/scripts/build_wivrn.sh", sysdata = PKG_DATA_DIR),
vec![
profile.xrservice_path,
profile.prefix,
]
);
runner
}

View file

@ -2,3 +2,4 @@ pub mod build_monado;
pub mod build_libsurvive; pub mod build_libsurvive;
pub mod build_opencomposite; pub mod build_opencomposite;
pub mod build_basalt; pub mod build_basalt;
pub mod build_wivrn;

View file

@ -1,3 +1,4 @@
pub mod monado_deps; pub mod monado_deps;
pub mod libsurvive_deps; pub mod libsurvive_deps;
pub mod basalt_deps; pub mod basalt_deps;
pub mod wivrn_deps;

View file

@ -0,0 +1,18 @@
use crate::depcheck::{Dependency, check_dependencies, DependencyCheckResult};
fn wivrn_deps() -> Vec<Dependency> {
// TODO: populate!
vec![]
}
pub fn check_wivrn_deps() -> Vec<DependencyCheckResult> {
check_dependencies(wivrn_deps())
}
pub fn get_missing_wivrn_deps() -> Vec<Dependency> {
check_wivrn_deps()
.iter()
.filter(|res| !res.found)
.map(|res| res.dependency.clone())
.collect()
}

View file

@ -1,4 +1,4 @@
use crate::{file_utils::get_writer, profile::Profile}; use crate::{file_utils::get_writer, profile::{Profile, XRServiceType}};
use expect_dialog::ExpectDialog; use expect_dialog::ExpectDialog;
use nix::{ use nix::{
sys::signal::{kill, Signal::SIGTERM}, sys::signal::{kill, Signal::SIGTERM},
@ -87,10 +87,13 @@ impl Runner {
} }
} }
pub fn monado_runner_from_profile(profile: Profile) -> Self { pub fn xrservice_runner_from_profile(profile: Profile) -> Self {
Self::new( Self::new(
Some(profile.environment), Some(profile.environment),
format!("{pfx}/bin/monado-service", pfx = profile.prefix), match profile.xrservice_type {
XRServiceType::Monado => format!("{pfx}/bin/monado-service", pfx = profile.prefix),
XRServiceType::Wivrn => format!("{pfx}/bin/wivrn-server", pfx = profile.prefix),
},
vec![], vec![],
) )
} }
@ -241,7 +244,7 @@ mod tests {
#[test] #[test]
fn can_create_from_profile() { fn can_create_from_profile() {
Runner::monado_runner_from_profile(Profile::load_profile( Runner::xrservice_runner_from_profile(Profile::load_profile(
&"./test/files/profile.json".to_string(), &"./test/files/profile.json".to_string(),
)); ));
} }

View file

@ -7,11 +7,13 @@ use crate::builders::build_basalt::get_build_basalt_runner;
use crate::builders::build_libsurvive::get_build_libsurvive_runner; use crate::builders::build_libsurvive::get_build_libsurvive_runner;
use crate::builders::build_monado::get_build_monado_runner; use crate::builders::build_monado::get_build_monado_runner;
use crate::builders::build_opencomposite::get_build_opencomposite_runner; use crate::builders::build_opencomposite::get_build_opencomposite_runner;
use crate::builders::build_wivrn::get_build_wivrn_runner;
use crate::config::{get_config, save_config, Config}; use crate::config::{get_config, save_config, Config};
use crate::constants::APP_NAME; use crate::constants::APP_NAME;
use crate::dependencies::basalt_deps::get_missing_basalt_deps; use crate::dependencies::basalt_deps::get_missing_basalt_deps;
use crate::dependencies::libsurvive_deps::get_missing_libsurvive_deps; use crate::dependencies::libsurvive_deps::get_missing_libsurvive_deps;
use crate::dependencies::monado_deps::get_missing_monado_deps; use crate::dependencies::monado_deps::get_missing_monado_deps;
use crate::dependencies::wivrn_deps::get_missing_wivrn_deps;
use crate::file_builders::active_runtime_json::{ use crate::file_builders::active_runtime_json::{
set_current_active_runtime_to_profile, set_current_active_runtime_to_steam, set_current_active_runtime_to_profile, set_current_active_runtime_to_steam,
}; };
@ -19,7 +21,7 @@ use crate::file_builders::openvrpaths_vrpath::{
set_current_openvrpaths_to_profile, set_current_openvrpaths_to_steam, set_current_openvrpaths_to_profile, set_current_openvrpaths_to_steam,
}; };
use crate::file_utils::setcap_cap_sys_nice_eip; use crate::file_utils::setcap_cap_sys_nice_eip;
use crate::profile::Profile; use crate::profile::{Profile, XRServiceType};
use crate::profiles::system_valve_index::system_valve_index_profile; use crate::profiles::system_valve_index::system_valve_index_profile;
use crate::profiles::valve_index::valve_index_profile; use crate::profiles::valve_index::valve_index_profile;
use crate::runner::{Runner, RunnerStatus}; use crate::runner::{Runner, RunnerStatus};
@ -60,9 +62,9 @@ pub struct App {
#[tracker::do_not_track] #[tracker::do_not_track]
config: Config, config: Config,
#[tracker::do_not_track] #[tracker::do_not_track]
monado_runner: Option<Runner>, xrservice_runner: Option<Runner>,
#[tracker::do_not_track] #[tracker::do_not_track]
monado_log: Vec<String>, xrservice_log: Vec<String>,
#[tracker::do_not_track] #[tracker::do_not_track]
build_pipeline: Option<RunnerPipeline>, build_pipeline: Option<RunnerPipeline>,
#[tracker::do_not_track] #[tracker::do_not_track]
@ -74,9 +76,9 @@ pub enum Msg {
UpdateView, UpdateView,
BuildProfile, BuildProfile,
EnableDebugViewChanged(bool), EnableDebugViewChanged(bool),
DoStartStopMonado, DoStartStopXRService,
ProfileSelected(String), ProfileSelected(String),
SetMonadoRuntime(bool), SetXRServiceRuntime(bool),
RunSetCap, RunSetCap,
OpenLibsurviveSetup, OpenLibsurviveSetup,
} }
@ -98,11 +100,11 @@ impl App {
} }
} }
pub fn start_monado(&mut self) { pub fn start_xrservice(&mut self) {
self.monado_log.clear(); self.xrservice_log.clear();
let mut runner = Runner::monado_runner_from_profile(self.get_selected_profile().clone()); let mut runner = Runner::xrservice_runner_from_profile(self.get_selected_profile().clone());
runner.start(); runner.start();
self.monado_runner = Some(runner); self.xrservice_runner = Some(runner);
} }
} }
@ -139,7 +141,7 @@ impl SimpleComponent for App {
} }
fn shutdown(&mut self, widgets: &mut Self::Widgets, output: relm4::Sender<Self::Output>) { fn shutdown(&mut self, widgets: &mut Self::Widgets, output: relm4::Sender<Self::Output>) {
match &mut self.monado_runner { match &mut self.xrservice_runner {
None => {} None => {}
Some(runner) => { Some(runner) => {
runner.terminate(); runner.terminate();
@ -152,22 +154,22 @@ impl SimpleComponent for App {
match message { match message {
Msg::UpdateView => { Msg::UpdateView => {
match &mut self.monado_runner { match &mut self.xrservice_runner {
None => {} None => {}
Some(runner) => { Some(runner) => {
let n_rows = runner.consume_rows(); let n_rows = runner.consume_rows();
if !n_rows.is_empty() { if !n_rows.is_empty() {
self.monado_log.extend(n_rows); self.xrservice_log.extend(n_rows);
self.debug_view self.debug_view
.sender() .sender()
.emit(DebugViewMsg::LogUpdated(self.monado_log.clone())); .emit(DebugViewMsg::LogUpdated(self.xrservice_log.clone()));
} }
match runner.status() { match runner.status() {
RunnerStatus::Running => {} RunnerStatus::Running => {}
RunnerStatus::Stopped(_) => { RunnerStatus::Stopped(_) => {
self.main_view self.main_view
.sender() .sender()
.emit(MainViewMsg::MonadoActiveChanged(false)); .emit(MainViewMsg::XRServiceActiveChanged(false));
} }
}; };
} }
@ -218,15 +220,15 @@ impl SimpleComponent for App {
.sender() .sender()
.emit(MainViewMsg::EnableDebugViewChanged(val)); .emit(MainViewMsg::EnableDebugViewChanged(val));
} }
Msg::DoStartStopMonado => match &mut self.monado_runner { Msg::DoStartStopXRService => match &mut self.xrservice_runner {
None => { None => {
self.debug_view self.debug_view
.sender() .sender()
.emit(DebugViewMsg::LogUpdated(vec![])); .emit(DebugViewMsg::LogUpdated(vec![]));
self.start_monado(); self.start_xrservice();
self.main_view self.main_view
.sender() .sender()
.emit(MainViewMsg::MonadoActiveChanged(true)); .emit(MainViewMsg::XRServiceActiveChanged(true));
self.main_view self.main_view
.sender() .sender()
.emit(MainViewMsg::SteamLaunchOptionsChanged( .emit(MainViewMsg::SteamLaunchOptionsChanged(
@ -238,16 +240,16 @@ impl SimpleComponent for App {
runner.terminate(); runner.terminate();
self.main_view self.main_view
.sender() .sender()
.emit(MainViewMsg::MonadoActiveChanged(false)); .emit(MainViewMsg::XRServiceActiveChanged(false));
} }
RunnerStatus::Stopped(_) => { RunnerStatus::Stopped(_) => {
self.debug_view self.debug_view
.sender() .sender()
.emit(DebugViewMsg::LogUpdated(vec![])); .emit(DebugViewMsg::LogUpdated(vec![]));
self.start_monado(); self.start_xrservice();
self.main_view self.main_view
.sender() .sender()
.emit(MainViewMsg::MonadoActiveChanged(true)); .emit(MainViewMsg::XRServiceActiveChanged(true));
} }
}, },
}, },
@ -257,7 +259,10 @@ impl SimpleComponent for App {
let mut runners: Vec<Runner> = vec![]; let mut runners: Vec<Runner> = vec![];
// profile per se can't be built, but we still need opencomp // profile per se can't be built, but we still need opencomp
if profile.can_be_built { if profile.can_be_built {
missing_deps.extend(get_missing_monado_deps()); missing_deps.extend(match profile.xrservice_type {
XRServiceType::Monado => get_missing_monado_deps(),
XRServiceType::Wivrn => get_missing_wivrn_deps(),
});
if profile.libsurvive_enabled { if profile.libsurvive_enabled {
missing_deps.extend(get_missing_libsurvive_deps()); missing_deps.extend(get_missing_libsurvive_deps());
runners.push(get_build_libsurvive_runner(profile.clone())); runners.push(get_build_libsurvive_runner(profile.clone()));
@ -270,7 +275,12 @@ impl SimpleComponent for App {
// missing_deps.extend(get_missing_mercury_deps()); // missing_deps.extend(get_missing_mercury_deps());
// runners.push(get_build_mercury_runner(profile.clone())); // runners.push(get_build_mercury_runner(profile.clone()));
// } // }
runners.push(get_build_monado_runner(profile.clone())); runners.push(
match profile.xrservice_type {
XRServiceType::Monado => get_build_monado_runner(profile.clone()),
XRServiceType::Wivrn => get_build_wivrn_runner(profile.clone()),
}
);
// no listed deps for opencomp // no listed deps for opencomp
} }
runners.push(get_build_opencomposite_runner(profile.clone())); runners.push(get_build_opencomposite_runner(profile.clone()));
@ -307,7 +317,10 @@ impl SimpleComponent for App {
} }
Msg::RunSetCap => { Msg::RunSetCap => {
let profile = self.get_selected_profile(); let profile = self.get_selected_profile();
setcap_cap_sys_nice_eip(format!("{pfx}/bin/monado-service", pfx = profile.prefix)); setcap_cap_sys_nice_eip(match profile.xrservice_type {
XRServiceType::Monado => format!("{pfx}/bin/monado-service", pfx = profile.prefix),
XRServiceType::Wivrn => format!("{pfx}/bin/wivrn-service", pfx = profile.prefix),
});
} }
Msg::ProfileSelected(prof_name) => { Msg::ProfileSelected(prof_name) => {
if prof_name == self.config.selected_profile_name { if prof_name == self.config.selected_profile_name {
@ -316,8 +329,8 @@ impl SimpleComponent for App {
self.config.selected_profile_name = prof_name; self.config.selected_profile_name = prof_name;
save_config(&self.config); save_config(&self.config);
} }
Msg::SetMonadoRuntime(monado) => { Msg::SetXRServiceRuntime(is_rex) => {
if monado { if is_rex {
let profile = self.get_selected_profile(); let profile = self.get_selected_profile();
set_current_active_runtime_to_profile(profile.clone()); set_current_active_runtime_to_profile(profile.clone());
set_current_openvrpaths_to_profile(profile.clone()); set_current_openvrpaths_to_profile(profile.clone());
@ -354,10 +367,10 @@ impl SimpleComponent for App {
let setcap_confirm_dialog = adw::MessageDialog::builder() let setcap_confirm_dialog = adw::MessageDialog::builder()
.modal(true) .modal(true)
.transient_for(root) .transient_for(root)
.heading("Set monado-service Capabilities") .heading("Set Capabilities")
.body(concat!( .body(concat!(
"We need to set certain capabilities (CAP_SYS_NICE=eip) on the ", "We need to set certain capabilities (CAP_SYS_NICE=eip) on the ",
"monado-service executable. This requires your superuser password.\n\n", "OpenXR server executable. This requires your superuser password.\n\n",
"Do you want to continue?", "Do you want to continue?",
)) ))
.hide_on_close(true) .hide_on_close(true)
@ -384,9 +397,9 @@ impl SimpleComponent for App {
.forward(sender.input_sender(), |message| match message { .forward(sender.input_sender(), |message| match message {
MainViewOutMsg::UpdateView => Msg::UpdateView, MainViewOutMsg::UpdateView => Msg::UpdateView,
MainViewOutMsg::EnableDebugViewChanged(val) => Msg::EnableDebugViewChanged(val), MainViewOutMsg::EnableDebugViewChanged(val) => Msg::EnableDebugViewChanged(val),
MainViewOutMsg::DoStartStopMonado => Msg::DoStartStopMonado, MainViewOutMsg::DoStartStopXRService => Msg::DoStartStopXRService,
MainViewOutMsg::ProfileSelected(name) => Msg::ProfileSelected(name), MainViewOutMsg::ProfileSelected(name) => Msg::ProfileSelected(name),
MainViewOutMsg::SetMonadoRuntime(monado) => Msg::SetMonadoRuntime(monado), MainViewOutMsg::SetXRServiceRuntime(is_rex) => Msg::SetXRServiceRuntime(is_rex),
}), }),
debug_view: DebugView::builder() debug_view: DebugView::builder()
.launch(DebugViewInit { .launch(DebugViewInit {
@ -413,8 +426,8 @@ impl SimpleComponent for App {
config, config,
tracker: 0, tracker: 0,
profiles, profiles,
monado_runner: None, xrservice_runner: None,
monado_log: vec![], xrservice_log: vec![],
build_pipeline: None, build_pipeline: None,
}; };
let widgets = view_output!(); let widgets = view_output!();

View file

@ -12,7 +12,7 @@ use relm4_icons::icon_name;
#[tracker::track] #[tracker::track]
pub struct MainView { pub struct MainView {
monado_active: bool, xrservice_active: bool,
enable_debug_view: bool, enable_debug_view: bool,
profile_names: Vec<String>, profile_names: Vec<String>,
steam_launch_options: String, steam_launch_options: String,
@ -23,7 +23,7 @@ pub struct MainView {
#[derive(Debug)] #[derive(Debug)]
pub enum MainViewMsg { pub enum MainViewMsg {
StartStopClicked, StartStopClicked,
MonadoActiveChanged(bool), XRServiceActiveChanged(bool),
EnableDebugViewChanged(bool), EnableDebugViewChanged(bool),
UpdateProfileNames(Vec<String>, Config), UpdateProfileNames(Vec<String>, Config),
SetSelectedProfile(u32), SetSelectedProfile(u32),
@ -36,9 +36,9 @@ pub enum MainViewMsg {
pub enum MainViewOutMsg { pub enum MainViewOutMsg {
UpdateView, UpdateView,
EnableDebugViewChanged(bool), EnableDebugViewChanged(bool),
DoStartStopMonado, DoStartStopXRService,
ProfileSelected(String), ProfileSelected(String),
SetMonadoRuntime(bool), SetXRServiceRuntime(bool),
} }
pub struct MainViewInit { pub struct MainViewInit {
@ -115,10 +115,10 @@ impl SimpleComponent for MainView {
add_css_class: "destructive-action", add_css_class: "destructive-action",
set_hexpand: true, set_hexpand: true,
set_halign: gtk::Align::Center, set_halign: gtk::Align::Center,
#[track = "model.changed(MainView::monado_active())"] #[track = "model.changed(MainView::xrservice_active())"]
set_class_active: ("suggested-action", !model.monado_active), set_class_active: ("suggested-action", !model.xrservice_active),
#[track = "model.changed(MainView::monado_active())"] #[track = "model.changed(MainView::xrservice_active())"]
set_label: match model.monado_active { set_label: match model.xrservice_active {
true => "Stop", true => "Stop",
false => "Start", false => "Start",
}, },
@ -154,7 +154,7 @@ impl SimpleComponent for MainView {
gtk::Switch { gtk::Switch {
}, },
gtk::Label { gtk::Label {
set_label: "Monado" set_label: APP_NAME,
}, },
}, },
gtk::Box { gtk::Box {
@ -164,8 +164,8 @@ impl SimpleComponent for MainView {
set_spacing: 12, set_spacing: 12,
set_margin_top: 12, set_margin_top: 12,
set_margin_bottom: 12, set_margin_bottom: 12,
#[track = "model.changed(MainView::monado_active())"] #[track = "model.changed(MainView::xrservice_active())"]
set_visible: model.monado_active, set_visible: model.xrservice_active,
gtk::Separator { gtk::Separator {
set_orientation: gtk::Orientation::Horizontal, set_orientation: gtk::Orientation::Horizontal,
set_hexpand: true, set_hexpand: true,
@ -186,7 +186,10 @@ impl SimpleComponent for MainView {
set_xalign: 0.0, set_xalign: 0.0,
set_margin_start: 12, set_margin_start: 12,
set_margin_end: 12, set_margin_end: 12,
set_label: "Set this string in the launch options of Steam games, so that they can pick up Monado and OpenOVR correctly", set_label: format!(
"Set this string in the launch options of Steam games, so that they can pick up the {app} runtime correctly",
app = APP_NAME)
.as_str(),
set_wrap: true, set_wrap: true,
set_wrap_mode: gtk::pango::WrapMode::Word, set_wrap_mode: gtk::pango::WrapMode::Word,
}, },
@ -236,10 +239,10 @@ impl SimpleComponent for MainView {
match message { match message {
MainViewMsg::StartStopClicked => { MainViewMsg::StartStopClicked => {
sender.output(MainViewOutMsg::DoStartStopMonado); sender.output(MainViewOutMsg::DoStartStopXRService);
} }
MainViewMsg::MonadoActiveChanged(active) => { MainViewMsg::XRServiceActiveChanged(active) => {
self.set_monado_active(active); self.set_xrservice_active(active);
} }
MainViewMsg::EnableDebugViewChanged(val) => { MainViewMsg::EnableDebugViewChanged(val) => {
self.set_enable_debug_view(val); self.set_enable_debug_view(val);
@ -251,7 +254,9 @@ impl SimpleComponent for MainView {
// draw, so selecting here will result in nothing cause the // draw, so selecting here will result in nothing cause the
// dropdown is effectively empty // dropdown is effectively empty
sender.input(MainViewMsg::SetSelectedProfile({ sender.input(MainViewMsg::SetSelectedProfile({
let pos = self.profile_names.iter() let pos = self
.profile_names
.iter()
.position(|p| p.clone() == config.selected_profile_name); .position(|p| p.clone() == config.selected_profile_name);
match pos { match pos {
Some(idx) => idx as u32, Some(idx) => idx as u32,
@ -260,7 +265,11 @@ impl SimpleComponent for MainView {
})); }));
} }
MainViewMsg::SetSelectedProfile(index) => { MainViewMsg::SetSelectedProfile(index) => {
self.profiles_dropdown.as_ref().unwrap().clone().set_selected(index); self.profiles_dropdown
.as_ref()
.unwrap()
.clone()
.set_selected(index);
} }
MainViewMsg::SteamLaunchOptionsChanged(lo) => self.set_steam_launch_options(lo), MainViewMsg::SteamLaunchOptionsChanged(lo) => self.set_steam_launch_options(lo),
MainViewMsg::CopySteamLaunchOptions => gtk::gdk::Display::default() MainViewMsg::CopySteamLaunchOptions => gtk::gdk::Display::default()
@ -281,7 +290,7 @@ impl SimpleComponent for MainView {
sender: ComponentSender<Self>, sender: ComponentSender<Self>,
) -> ComponentParts<Self> { ) -> ComponentParts<Self> {
let mut model = MainView { let mut model = MainView {
monado_active: false, xrservice_active: false,
enable_debug_view: init.config.debug_view_enabled, enable_debug_view: init.config.debug_view_enabled,
profiles_dropdown: None, profiles_dropdown: None,
profile_names: vec![], profile_names: vec![],
@ -300,7 +309,7 @@ impl SimpleComponent for MainView {
} }
} }
widgets.runtime_switch.connect_state_set(move |_, state| { widgets.runtime_switch.connect_state_set(move |_, state| {
sender.output(MainViewOutMsg::SetMonadoRuntime(state)); sender.output(MainViewOutMsg::SetXRServiceRuntime(state));
gtk::Inhibit(false) gtk::Inhibit(false)
}); });
} }