mirror of
https://gitlab.com/gabmus/envision.git
synced 2025-04-20 03:24:52 +00:00
feat: steamvr quick calibration in main view
This commit is contained in:
parent
352a873528
commit
b3dd0aa494
4 changed files with 240 additions and 1 deletions
|
@ -125,3 +125,10 @@ pub fn get_ipc_file_path(xrservice_type: &XRServiceType) -> String {
|
|||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_steamvr_bin_dir_path() -> String {
|
||||
format!(
|
||||
"{data}/Steam/steamapps/common/SteamVR/bin/linux64",
|
||||
data = get_xdg_data_dir()
|
||||
)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use super::devices_box::{DevicesBox, DevicesBoxMsg};
|
|||
use super::install_wivrn_box::{InstallWivrnBox, InstallWivrnBoxInit, InstallWivrnBoxMsg};
|
||||
use super::profile_editor::{ProfileEditor, ProfileEditorMsg, ProfileEditorOutMsg};
|
||||
use super::steam_launch_options_box::{SteamLaunchOptionsBox, SteamLaunchOptionsBoxMsg};
|
||||
use super::steamvr_calibration_box::SteamVrCalibrationBox;
|
||||
use crate::config::Config;
|
||||
use crate::constants::APP_NAME;
|
||||
use crate::file_utils::mount_has_nosuid;
|
||||
|
@ -17,6 +18,7 @@ use crate::ui::app::{
|
|||
BuildProfileDebugAction, ConfigFbtAction, DebugViewToggleAction,
|
||||
};
|
||||
use crate::ui::profile_editor::ProfileEditorInit;
|
||||
use crate::ui::steamvr_calibration_box::SteamVrCalibrationBoxMsg;
|
||||
use crate::ui::util::{limit_dropdown_width, warning_heading};
|
||||
use crate::xr_devices::XRDevice;
|
||||
use gtk::prelude::*;
|
||||
|
@ -47,6 +49,8 @@ pub struct MainView {
|
|||
#[tracker::do_not_track]
|
||||
profile_editor: Option<Controller<ProfileEditor>>,
|
||||
#[tracker::do_not_track]
|
||||
steamvr_calibration_box: Controller<SteamVrCalibrationBox>,
|
||||
#[tracker::do_not_track]
|
||||
root_win: gtk::Window,
|
||||
}
|
||||
|
||||
|
@ -334,6 +338,7 @@ impl SimpleComponent for MainView {
|
|||
},
|
||||
model.steam_launch_options_box.widget(),
|
||||
model.install_wivrn_box.widget(),
|
||||
model.steamvr_calibration_box.widget(),
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
set_hexpand: true,
|
||||
|
@ -368,7 +373,7 @@ impl SimpleComponent for MainView {
|
|||
set_label: "Calibrate",
|
||||
set_halign: gtk::Align::Start,
|
||||
connect_clicked[sender] => move |_| {
|
||||
sender.output(Self::Output::OpenLibsurviveSetup).expect("Sender outut failed");
|
||||
sender.output(Self::Output::OpenLibsurviveSetup).expect("Sender output failed");
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -465,6 +470,9 @@ impl SimpleComponent for MainView {
|
|||
}
|
||||
Self::Input::XRServiceActiveChanged(active, profile) => {
|
||||
self.set_xrservice_active(active);
|
||||
self.steamvr_calibration_box
|
||||
.sender()
|
||||
.emit(SteamVrCalibrationBoxMsg::XRServiceActiveChanged(active));
|
||||
if !active {
|
||||
sender.input(Self::Input::UpdateDevices(vec![]));
|
||||
}
|
||||
|
@ -492,6 +500,11 @@ impl SimpleComponent for MainView {
|
|||
}
|
||||
Self::Input::UpdateSelectedProfile(prof) => {
|
||||
self.set_selected_profile(prof.clone());
|
||||
self.steamvr_calibration_box
|
||||
.sender()
|
||||
.emit(SteamVrCalibrationBoxMsg::SetVisible(
|
||||
prof.lighthouse_driver == LighthouseDriver::SteamVR,
|
||||
));
|
||||
self.install_wivrn_box
|
||||
.sender()
|
||||
.emit(InstallWivrnBoxMsg::UpdateSelectedProfile(prof.clone()));
|
||||
|
@ -630,6 +643,13 @@ impl SimpleComponent for MainView {
|
|||
});
|
||||
}
|
||||
|
||||
let steamvr_calibration_box = SteamVrCalibrationBox::builder().launch(()).detach();
|
||||
steamvr_calibration_box
|
||||
.sender()
|
||||
.emit(SteamVrCalibrationBoxMsg::SetVisible(
|
||||
init.selected_profile.lighthouse_driver == LighthouseDriver::SteamVR,
|
||||
));
|
||||
|
||||
let mut model = Self {
|
||||
xrservice_active: false,
|
||||
enable_debug_view: init.config.debug_view_enabled,
|
||||
|
@ -647,6 +667,7 @@ impl SimpleComponent for MainView {
|
|||
profile_not_editable_dialog,
|
||||
profile_delete_confirm_dialog,
|
||||
root_win: init.root_win.clone(),
|
||||
steamvr_calibration_box,
|
||||
profile_editor: None,
|
||||
tracker: 0,
|
||||
};
|
||||
|
|
|
@ -15,5 +15,6 @@ pub mod main_view;
|
|||
pub mod preference_rows;
|
||||
pub mod profile_editor;
|
||||
pub mod steam_launch_options_box;
|
||||
mod steamvr_calibration_box;
|
||||
pub mod util;
|
||||
pub mod wivrn_conf_editor;
|
||||
|
|
210
src/ui/steamvr_calibration_box.rs
Normal file
210
src/ui/steamvr_calibration_box.rs
Normal file
|
@ -0,0 +1,210 @@
|
|||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
path::Path,
|
||||
thread::sleep,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use crate::paths::get_steamvr_bin_dir_path;
|
||||
|
||||
use super::job_worker::{
|
||||
internal_worker::JobWorkerOut,
|
||||
job::{FuncWorkerOut, WorkerJob},
|
||||
JobWorker,
|
||||
};
|
||||
use relm4::{
|
||||
gtk::{self, prelude::*},
|
||||
ComponentParts, ComponentSender, RelmWidgetExt, SimpleComponent,
|
||||
};
|
||||
|
||||
#[tracker::track]
|
||||
pub struct SteamVrCalibrationBox {
|
||||
calibration_running: bool,
|
||||
calibration_result: Option<String>,
|
||||
calibration_success: bool,
|
||||
visible: bool,
|
||||
xrservice_active: bool,
|
||||
#[tracker::do_not_track]
|
||||
server_worker: Option<JobWorker>,
|
||||
#[tracker::do_not_track]
|
||||
calibration_worker: Option<JobWorker>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SteamVrCalibrationBoxMsg {
|
||||
SetVisible(bool),
|
||||
RunCalibration,
|
||||
OnServerWorkerExit(i32),
|
||||
OnCalWorkerExit(i32),
|
||||
XRServiceActiveChanged(bool),
|
||||
NoOp,
|
||||
}
|
||||
|
||||
#[relm4::component(pub)]
|
||||
impl SimpleComponent for SteamVrCalibrationBox {
|
||||
type Init = ();
|
||||
type Input = SteamVrCalibrationBoxMsg;
|
||||
type Output = ();
|
||||
|
||||
view! {
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
set_hexpand: true,
|
||||
set_vexpand: false,
|
||||
set_spacing: 12,
|
||||
add_css_class: "card",
|
||||
add_css_class: "padded",
|
||||
#[track = "model.changed(Self::visible())"]
|
||||
set_visible: model.visible,
|
||||
gtk::Label {
|
||||
add_css_class: "heading",
|
||||
set_hexpand: true,
|
||||
set_xalign: 0.0,
|
||||
set_label: "SteamVR Calibration",
|
||||
set_wrap: true,
|
||||
set_wrap_mode: gtk::pango::WrapMode::Word,
|
||||
},
|
||||
gtk::Label {
|
||||
add_css_class: "dim-label",
|
||||
set_hexpand: true,
|
||||
set_label: concat!(
|
||||
"Run a quick SteamVR calibration.\n\n",
|
||||
"\u{2022} Plug in your HMD and place it on the floor, ",
|
||||
"approximately in the middle of your play area\n",
|
||||
"\u{2022} Make sure your controllers and other VR devices ",
|
||||
"are turned off\n",
|
||||
"\u{2022} Click the Calibrate button and wait for the ",
|
||||
"process to finish\n\n",
|
||||
"Note that the orientation of your HMD during this process ",
|
||||
"will dictate the forward direction of your play area.",
|
||||
),
|
||||
set_xalign: 0.0,
|
||||
set_wrap: true,
|
||||
set_wrap_mode: gtk::pango::WrapMode::Word,
|
||||
},
|
||||
gtk::Label {
|
||||
add_css_class: "error",
|
||||
add_css_class: "success",
|
||||
set_hexpand: true,
|
||||
#[track = "model.changed(Self::calibration_result())"]
|
||||
set_visible: model.calibration_result.is_some(),
|
||||
#[track = "model.changed(Self::calibration_result())"]
|
||||
set_label: model.calibration_result.as_ref().unwrap_or(&String::new()),
|
||||
#[track = "model.changed(Self::calibration_success())"]
|
||||
set_class_active: ("error", !model.calibration_success),
|
||||
#[track = "model.changed(Self::calibration_success())"]
|
||||
set_class_active: ("success", model.calibration_success),
|
||||
set_xalign: 0.0,
|
||||
set_wrap: true,
|
||||
set_wrap_mode: gtk::pango::WrapMode::Word,
|
||||
},
|
||||
gtk::Button {
|
||||
add_css_class: "suggested-action",
|
||||
set_label: "Calibrate",
|
||||
set_halign: gtk::Align::Start,
|
||||
#[track = "model.changed(Self::calibration_running()) || model.changed(Self::xrservice_active())"]
|
||||
set_sensitive: !model.xrservice_active && !model.calibration_running,
|
||||
connect_clicked[sender] => move |_| {
|
||||
sender.input(Self::Input::RunCalibration);
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>) {
|
||||
self.reset();
|
||||
|
||||
match message {
|
||||
Self::Input::SetVisible(state) => {
|
||||
self.set_visible(state);
|
||||
}
|
||||
Self::Input::XRServiceActiveChanged(active) => {
|
||||
self.set_xrservice_active(active);
|
||||
}
|
||||
Self::Input::RunCalibration => {
|
||||
self.set_calibration_result(None);
|
||||
let steamvr_bin_dir = get_steamvr_bin_dir_path();
|
||||
if !Path::new(&steamvr_bin_dir).is_dir() {
|
||||
self.set_calibration_success(false);
|
||||
self.set_calibration_result(Some("SteamVR not found".into()));
|
||||
return;
|
||||
}
|
||||
let mut env: HashMap<String, String> = HashMap::new();
|
||||
env.insert("LD_LIBRARY_PATH".into(), steamvr_bin_dir.clone());
|
||||
let vrcmd = format!("{steamvr_bin_dir}/vrcmd");
|
||||
let mut server_worker = {
|
||||
let mut jobs: VecDeque<WorkerJob> = VecDeque::new();
|
||||
jobs.push_back(WorkerJob::new_cmd(
|
||||
Some(env.clone()),
|
||||
vrcmd.clone(),
|
||||
Some(vec!["--pollposes".into()]),
|
||||
));
|
||||
JobWorker::new(jobs, sender.input_sender(), |msg| match msg {
|
||||
JobWorkerOut::Log(_) => Self::Input::NoOp,
|
||||
JobWorkerOut::Exit(code) => Self::Input::OnServerWorkerExit(code),
|
||||
})
|
||||
};
|
||||
let mut cal_worker = {
|
||||
let mut jobs: VecDeque<WorkerJob> = VecDeque::new();
|
||||
jobs.push_back(WorkerJob::new_func(Box::new(move || {
|
||||
sleep(Duration::from_secs(2));
|
||||
FuncWorkerOut {
|
||||
success: true,
|
||||
out: vec![],
|
||||
}
|
||||
})));
|
||||
jobs.push_back(WorkerJob::new_cmd(
|
||||
Some(env),
|
||||
vrcmd,
|
||||
Some(vec!["--resetroomsetup".into()]),
|
||||
));
|
||||
JobWorker::new(jobs, sender.input_sender(), |msg| match msg {
|
||||
JobWorkerOut::Log(_) => Self::Input::NoOp,
|
||||
JobWorkerOut::Exit(code) => Self::Input::OnCalWorkerExit(code),
|
||||
})
|
||||
};
|
||||
|
||||
server_worker.start();
|
||||
cal_worker.start();
|
||||
self.server_worker = Some(server_worker);
|
||||
self.calibration_worker = Some(cal_worker);
|
||||
}
|
||||
Self::Input::OnServerWorkerExit(_) => {
|
||||
self.calibration_running = false;
|
||||
}
|
||||
Self::Input::OnCalWorkerExit(code) => {
|
||||
self.calibration_success = code == 0;
|
||||
self.calibration_result = if code == 0 {
|
||||
Some("Calibration completed".into())
|
||||
} else {
|
||||
Some(format!("Calibration failed with code {code}"))
|
||||
};
|
||||
if let Some(sw) = self.server_worker.as_ref() {
|
||||
sw.stop();
|
||||
}
|
||||
}
|
||||
Self::Input::NoOp => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn init(
|
||||
init: Self::Init,
|
||||
root: &Self::Root,
|
||||
sender: ComponentSender<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
let model = Self {
|
||||
tracker: 0,
|
||||
visible: false,
|
||||
calibration_result: None,
|
||||
calibration_running: false,
|
||||
calibration_success: false,
|
||||
xrservice_active: false,
|
||||
server_worker: None,
|
||||
calibration_worker: None,
|
||||
};
|
||||
|
||||
let widgets = view_output!();
|
||||
|
||||
ComponentParts { model, widgets }
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue