mirror of
https://gitlab.com/gabmus/envision.git
synced 2025-08-05 07:39:00 +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::install_wivrn_box::{InstallWivrnBox, InstallWivrnBoxInit, InstallWivrnBoxMsg};
|
||||||
use super::profile_editor::{ProfileEditor, ProfileEditorMsg, ProfileEditorOutMsg};
|
use super::profile_editor::{ProfileEditor, ProfileEditorMsg, ProfileEditorOutMsg};
|
||||||
use super::steam_launch_options_box::{SteamLaunchOptionsBox, SteamLaunchOptionsBoxMsg};
|
use super::steam_launch_options_box::{SteamLaunchOptionsBox, SteamLaunchOptionsBoxMsg};
|
||||||
|
use super::steamvr_calibration_box::SteamVrCalibrationBox;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::constants::APP_NAME;
|
use crate::constants::APP_NAME;
|
||||||
use crate::file_utils::mount_has_nosuid;
|
use crate::file_utils::mount_has_nosuid;
|
||||||
|
@ -17,6 +18,7 @@ use crate::ui::app::{
|
||||||
BuildProfileDebugAction, ConfigFbtAction, DebugViewToggleAction,
|
BuildProfileDebugAction, ConfigFbtAction, DebugViewToggleAction,
|
||||||
};
|
};
|
||||||
use crate::ui::profile_editor::ProfileEditorInit;
|
use crate::ui::profile_editor::ProfileEditorInit;
|
||||||
|
use crate::ui::steamvr_calibration_box::SteamVrCalibrationBoxMsg;
|
||||||
use crate::ui::util::{limit_dropdown_width, warning_heading};
|
use crate::ui::util::{limit_dropdown_width, warning_heading};
|
||||||
use crate::xr_devices::XRDevice;
|
use crate::xr_devices::XRDevice;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
@ -47,6 +49,8 @@ pub struct MainView {
|
||||||
#[tracker::do_not_track]
|
#[tracker::do_not_track]
|
||||||
profile_editor: Option<Controller<ProfileEditor>>,
|
profile_editor: Option<Controller<ProfileEditor>>,
|
||||||
#[tracker::do_not_track]
|
#[tracker::do_not_track]
|
||||||
|
steamvr_calibration_box: Controller<SteamVrCalibrationBox>,
|
||||||
|
#[tracker::do_not_track]
|
||||||
root_win: gtk::Window,
|
root_win: gtk::Window,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,6 +338,7 @@ impl SimpleComponent for MainView {
|
||||||
},
|
},
|
||||||
model.steam_launch_options_box.widget(),
|
model.steam_launch_options_box.widget(),
|
||||||
model.install_wivrn_box.widget(),
|
model.install_wivrn_box.widget(),
|
||||||
|
model.steamvr_calibration_box.widget(),
|
||||||
gtk::Box {
|
gtk::Box {
|
||||||
set_orientation: gtk::Orientation::Vertical,
|
set_orientation: gtk::Orientation::Vertical,
|
||||||
set_hexpand: true,
|
set_hexpand: true,
|
||||||
|
@ -368,7 +373,7 @@ impl SimpleComponent for MainView {
|
||||||
set_label: "Calibrate",
|
set_label: "Calibrate",
|
||||||
set_halign: gtk::Align::Start,
|
set_halign: gtk::Align::Start,
|
||||||
connect_clicked[sender] => move |_| {
|
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::Input::XRServiceActiveChanged(active, profile) => {
|
||||||
self.set_xrservice_active(active);
|
self.set_xrservice_active(active);
|
||||||
|
self.steamvr_calibration_box
|
||||||
|
.sender()
|
||||||
|
.emit(SteamVrCalibrationBoxMsg::XRServiceActiveChanged(active));
|
||||||
if !active {
|
if !active {
|
||||||
sender.input(Self::Input::UpdateDevices(vec![]));
|
sender.input(Self::Input::UpdateDevices(vec![]));
|
||||||
}
|
}
|
||||||
|
@ -492,6 +500,11 @@ impl SimpleComponent for MainView {
|
||||||
}
|
}
|
||||||
Self::Input::UpdateSelectedProfile(prof) => {
|
Self::Input::UpdateSelectedProfile(prof) => {
|
||||||
self.set_selected_profile(prof.clone());
|
self.set_selected_profile(prof.clone());
|
||||||
|
self.steamvr_calibration_box
|
||||||
|
.sender()
|
||||||
|
.emit(SteamVrCalibrationBoxMsg::SetVisible(
|
||||||
|
prof.lighthouse_driver == LighthouseDriver::SteamVR,
|
||||||
|
));
|
||||||
self.install_wivrn_box
|
self.install_wivrn_box
|
||||||
.sender()
|
.sender()
|
||||||
.emit(InstallWivrnBoxMsg::UpdateSelectedProfile(prof.clone()));
|
.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 {
|
let mut model = Self {
|
||||||
xrservice_active: false,
|
xrservice_active: false,
|
||||||
enable_debug_view: init.config.debug_view_enabled,
|
enable_debug_view: init.config.debug_view_enabled,
|
||||||
|
@ -647,6 +667,7 @@ impl SimpleComponent for MainView {
|
||||||
profile_not_editable_dialog,
|
profile_not_editable_dialog,
|
||||||
profile_delete_confirm_dialog,
|
profile_delete_confirm_dialog,
|
||||||
root_win: init.root_win.clone(),
|
root_win: init.root_win.clone(),
|
||||||
|
steamvr_calibration_box,
|
||||||
profile_editor: None,
|
profile_editor: None,
|
||||||
tracker: 0,
|
tracker: 0,
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,5 +15,6 @@ pub mod main_view;
|
||||||
pub mod preference_rows;
|
pub mod preference_rows;
|
||||||
pub mod profile_editor;
|
pub mod profile_editor;
|
||||||
pub mod steam_launch_options_box;
|
pub mod steam_launch_options_box;
|
||||||
|
mod steamvr_calibration_box;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod wivrn_conf_editor;
|
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
Add a link
Reference in a new issue