diff --git a/scripts/build_wivrn.sh b/scripts/build_wivrn.sh new file mode 100755 index 0000000..ad8957f --- /dev/null +++ b/scripts/build_wivrn.sh @@ -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 diff --git a/src/builders/build_wivrn.rs b/src/builders/build_wivrn.rs new file mode 100644 index 0000000..dcbaba6 --- /dev/null +++ b/src/builders/build_wivrn.rs @@ -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 +} + diff --git a/src/builders/mod.rs b/src/builders/mod.rs index fd107dd..8490707 100644 --- a/src/builders/mod.rs +++ b/src/builders/mod.rs @@ -2,3 +2,4 @@ pub mod build_monado; pub mod build_libsurvive; pub mod build_opencomposite; pub mod build_basalt; +pub mod build_wivrn; diff --git a/src/dependencies/mod.rs b/src/dependencies/mod.rs index 85eef44..e50dd18 100644 --- a/src/dependencies/mod.rs +++ b/src/dependencies/mod.rs @@ -1,3 +1,4 @@ pub mod monado_deps; pub mod libsurvive_deps; pub mod basalt_deps; +pub mod wivrn_deps; diff --git a/src/dependencies/wivrn_deps.rs b/src/dependencies/wivrn_deps.rs new file mode 100644 index 0000000..5a19ee0 --- /dev/null +++ b/src/dependencies/wivrn_deps.rs @@ -0,0 +1,18 @@ +use crate::depcheck::{Dependency, check_dependencies, DependencyCheckResult}; + +fn wivrn_deps() -> Vec { + // TODO: populate! + vec![] +} + +pub fn check_wivrn_deps() -> Vec { + check_dependencies(wivrn_deps()) +} + +pub fn get_missing_wivrn_deps() -> Vec { + check_wivrn_deps() + .iter() + .filter(|res| !res.found) + .map(|res| res.dependency.clone()) + .collect() +} diff --git a/src/runner.rs b/src/runner.rs index c0579e4..b2872cd 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -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 nix::{ 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( 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![], ) } @@ -241,7 +244,7 @@ mod tests { #[test] 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(), )); } diff --git a/src/ui/app.rs b/src/ui/app.rs index 3e1f795..762ab33 100644 --- a/src/ui/app.rs +++ b/src/ui/app.rs @@ -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_monado::get_build_monado_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::constants::APP_NAME; use crate::dependencies::basalt_deps::get_missing_basalt_deps; use crate::dependencies::libsurvive_deps::get_missing_libsurvive_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::{ 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, }; 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::valve_index::valve_index_profile; use crate::runner::{Runner, RunnerStatus}; @@ -60,9 +62,9 @@ pub struct App { #[tracker::do_not_track] config: Config, #[tracker::do_not_track] - monado_runner: Option, + xrservice_runner: Option, #[tracker::do_not_track] - monado_log: Vec, + xrservice_log: Vec, #[tracker::do_not_track] build_pipeline: Option, #[tracker::do_not_track] @@ -74,9 +76,9 @@ pub enum Msg { UpdateView, BuildProfile, EnableDebugViewChanged(bool), - DoStartStopMonado, + DoStartStopXRService, ProfileSelected(String), - SetMonadoRuntime(bool), + SetXRServiceRuntime(bool), RunSetCap, OpenLibsurviveSetup, } @@ -98,11 +100,11 @@ impl App { } } - pub fn start_monado(&mut self) { - self.monado_log.clear(); - let mut runner = Runner::monado_runner_from_profile(self.get_selected_profile().clone()); + pub fn start_xrservice(&mut self) { + self.xrservice_log.clear(); + let mut runner = Runner::xrservice_runner_from_profile(self.get_selected_profile().clone()); 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) { - match &mut self.monado_runner { + match &mut self.xrservice_runner { None => {} Some(runner) => { runner.terminate(); @@ -152,22 +154,22 @@ impl SimpleComponent for App { match message { Msg::UpdateView => { - match &mut self.monado_runner { + match &mut self.xrservice_runner { None => {} Some(runner) => { let n_rows = runner.consume_rows(); if !n_rows.is_empty() { - self.monado_log.extend(n_rows); + self.xrservice_log.extend(n_rows); self.debug_view .sender() - .emit(DebugViewMsg::LogUpdated(self.monado_log.clone())); + .emit(DebugViewMsg::LogUpdated(self.xrservice_log.clone())); } match runner.status() { RunnerStatus::Running => {} RunnerStatus::Stopped(_) => { self.main_view .sender() - .emit(MainViewMsg::MonadoActiveChanged(false)); + .emit(MainViewMsg::XRServiceActiveChanged(false)); } }; } @@ -218,15 +220,15 @@ impl SimpleComponent for App { .sender() .emit(MainViewMsg::EnableDebugViewChanged(val)); } - Msg::DoStartStopMonado => match &mut self.monado_runner { + Msg::DoStartStopXRService => match &mut self.xrservice_runner { None => { self.debug_view .sender() .emit(DebugViewMsg::LogUpdated(vec![])); - self.start_monado(); + self.start_xrservice(); self.main_view .sender() - .emit(MainViewMsg::MonadoActiveChanged(true)); + .emit(MainViewMsg::XRServiceActiveChanged(true)); self.main_view .sender() .emit(MainViewMsg::SteamLaunchOptionsChanged( @@ -238,16 +240,16 @@ impl SimpleComponent for App { runner.terminate(); self.main_view .sender() - .emit(MainViewMsg::MonadoActiveChanged(false)); + .emit(MainViewMsg::XRServiceActiveChanged(false)); } RunnerStatus::Stopped(_) => { self.debug_view .sender() .emit(DebugViewMsg::LogUpdated(vec![])); - self.start_monado(); + self.start_xrservice(); self.main_view .sender() - .emit(MainViewMsg::MonadoActiveChanged(true)); + .emit(MainViewMsg::XRServiceActiveChanged(true)); } }, }, @@ -257,7 +259,10 @@ impl SimpleComponent for App { let mut runners: Vec = vec![]; // profile per se can't be built, but we still need opencomp 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 { missing_deps.extend(get_missing_libsurvive_deps()); runners.push(get_build_libsurvive_runner(profile.clone())); @@ -270,7 +275,12 @@ impl SimpleComponent for App { // missing_deps.extend(get_missing_mercury_deps()); // 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 } runners.push(get_build_opencomposite_runner(profile.clone())); @@ -307,7 +317,10 @@ impl SimpleComponent for App { } Msg::RunSetCap => { 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) => { if prof_name == self.config.selected_profile_name { @@ -316,8 +329,8 @@ impl SimpleComponent for App { self.config.selected_profile_name = prof_name; save_config(&self.config); } - Msg::SetMonadoRuntime(monado) => { - if monado { + Msg::SetXRServiceRuntime(is_rex) => { + if is_rex { let profile = self.get_selected_profile(); set_current_active_runtime_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() .modal(true) .transient_for(root) - .heading("Set monado-service Capabilities") + .heading("Set Capabilities") .body(concat!( "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?", )) .hide_on_close(true) @@ -384,9 +397,9 @@ impl SimpleComponent for App { .forward(sender.input_sender(), |message| match message { MainViewOutMsg::UpdateView => Msg::UpdateView, MainViewOutMsg::EnableDebugViewChanged(val) => Msg::EnableDebugViewChanged(val), - MainViewOutMsg::DoStartStopMonado => Msg::DoStartStopMonado, + MainViewOutMsg::DoStartStopXRService => Msg::DoStartStopXRService, MainViewOutMsg::ProfileSelected(name) => Msg::ProfileSelected(name), - MainViewOutMsg::SetMonadoRuntime(monado) => Msg::SetMonadoRuntime(monado), + MainViewOutMsg::SetXRServiceRuntime(is_rex) => Msg::SetXRServiceRuntime(is_rex), }), debug_view: DebugView::builder() .launch(DebugViewInit { @@ -413,8 +426,8 @@ impl SimpleComponent for App { config, tracker: 0, profiles, - monado_runner: None, - monado_log: vec![], + xrservice_runner: None, + xrservice_log: vec![], build_pipeline: None, }; let widgets = view_output!(); diff --git a/src/ui/main_view.rs b/src/ui/main_view.rs index b2a5620..2fee28e 100644 --- a/src/ui/main_view.rs +++ b/src/ui/main_view.rs @@ -12,7 +12,7 @@ use relm4_icons::icon_name; #[tracker::track] pub struct MainView { - monado_active: bool, + xrservice_active: bool, enable_debug_view: bool, profile_names: Vec, steam_launch_options: String, @@ -23,7 +23,7 @@ pub struct MainView { #[derive(Debug)] pub enum MainViewMsg { StartStopClicked, - MonadoActiveChanged(bool), + XRServiceActiveChanged(bool), EnableDebugViewChanged(bool), UpdateProfileNames(Vec, Config), SetSelectedProfile(u32), @@ -36,9 +36,9 @@ pub enum MainViewMsg { pub enum MainViewOutMsg { UpdateView, EnableDebugViewChanged(bool), - DoStartStopMonado, + DoStartStopXRService, ProfileSelected(String), - SetMonadoRuntime(bool), + SetXRServiceRuntime(bool), } pub struct MainViewInit { @@ -115,10 +115,10 @@ impl SimpleComponent for MainView { add_css_class: "destructive-action", set_hexpand: true, set_halign: gtk::Align::Center, - #[track = "model.changed(MainView::monado_active())"] - set_class_active: ("suggested-action", !model.monado_active), - #[track = "model.changed(MainView::monado_active())"] - set_label: match model.monado_active { + #[track = "model.changed(MainView::xrservice_active())"] + set_class_active: ("suggested-action", !model.xrservice_active), + #[track = "model.changed(MainView::xrservice_active())"] + set_label: match model.xrservice_active { true => "Stop", false => "Start", }, @@ -154,7 +154,7 @@ impl SimpleComponent for MainView { gtk::Switch { }, gtk::Label { - set_label: "Monado" + set_label: APP_NAME, }, }, gtk::Box { @@ -164,8 +164,8 @@ impl SimpleComponent for MainView { set_spacing: 12, set_margin_top: 12, set_margin_bottom: 12, - #[track = "model.changed(MainView::monado_active())"] - set_visible: model.monado_active, + #[track = "model.changed(MainView::xrservice_active())"] + set_visible: model.xrservice_active, gtk::Separator { set_orientation: gtk::Orientation::Horizontal, set_hexpand: true, @@ -186,7 +186,10 @@ impl SimpleComponent for MainView { set_xalign: 0.0, set_margin_start: 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_mode: gtk::pango::WrapMode::Word, }, @@ -236,10 +239,10 @@ impl SimpleComponent for MainView { match message { MainViewMsg::StartStopClicked => { - sender.output(MainViewOutMsg::DoStartStopMonado); + sender.output(MainViewOutMsg::DoStartStopXRService); } - MainViewMsg::MonadoActiveChanged(active) => { - self.set_monado_active(active); + MainViewMsg::XRServiceActiveChanged(active) => { + self.set_xrservice_active(active); } MainViewMsg::EnableDebugViewChanged(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 // dropdown is effectively empty sender.input(MainViewMsg::SetSelectedProfile({ - let pos = self.profile_names.iter() + let pos = self + .profile_names + .iter() .position(|p| p.clone() == config.selected_profile_name); match pos { Some(idx) => idx as u32, @@ -260,7 +265,11 @@ impl SimpleComponent for MainView { })); } 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::CopySteamLaunchOptions => gtk::gdk::Display::default() @@ -281,7 +290,7 @@ impl SimpleComponent for MainView { sender: ComponentSender, ) -> ComponentParts { let mut model = MainView { - monado_active: false, + xrservice_active: false, enable_debug_view: init.config.debug_view_enabled, profiles_dropdown: None, profile_names: vec![], @@ -300,7 +309,7 @@ impl SimpleComponent for MainView { } } widgets.runtime_switch.connect_state_set(move |_, state| { - sender.output(MainViewOutMsg::SetMonadoRuntime(state)); + sender.output(MainViewOutMsg::SetXRServiceRuntime(state)); gtk::Inhibit(false) }); }