mirror of
https://gitlab.com/gabmus/envision.git
synced 2025-08-03 06:38:52 +00:00
Merge branch 'main' into feat/stardust
This commit is contained in:
commit
356656716c
20 changed files with 630 additions and 386 deletions
|
@ -5,6 +5,7 @@ pub static ENV_VAR_DESCRIPTIONS: Map<&str, &str> = phf_map! {
|
||||||
"XRT_COMPOSITOR_SCALE_PECENTAGE" =>
|
"XRT_COMPOSITOR_SCALE_PECENTAGE" =>
|
||||||
"Render resolution percentage. A percentage higher than the native resolution (>100) will help with antialiasing and image clarity.",
|
"Render resolution percentage. A percentage higher than the native resolution (>100) will help with antialiasing and image clarity.",
|
||||||
"XRT_COMPOSITOR_COMPUTE" => "Set to 1 to use GPU compute for the OpenXR compositor.",
|
"XRT_COMPOSITOR_COMPUTE" => "Set to 1 to use GPU compute for the OpenXR compositor.",
|
||||||
|
"U_PACING_APP_USE_MIN_FRAME_PERIOD" => "Set to 1 to unlimit the compositor refresh from a power of two of your HMD refresh, typically provides a large performance boost.",
|
||||||
"SURVIVE_GLOBALSCENESOLVER" =>
|
"SURVIVE_GLOBALSCENESOLVER" =>
|
||||||
"Continuously recalibrate lighthouse tracking during use. In the current state it's recommended to disable this feature by setting this value to 0.",
|
"Continuously recalibrate lighthouse tracking during use. In the current state it's recommended to disable this feature by setting this value to 0.",
|
||||||
// "SURVIVE_TIMECODE_OFFSET_MS" => "",
|
// "SURVIVE_TIMECODE_OFFSET_MS" => "",
|
||||||
|
|
|
@ -133,3 +133,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()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ pub fn lighthouse_profile() -> Profile {
|
||||||
environment.insert("XRT_COMPOSITOR_COMPUTE".into(), "1".into());
|
environment.insert("XRT_COMPOSITOR_COMPUTE".into(), "1".into());
|
||||||
environment.insert("XRT_DEBUG_GUI".into(), "1".into());
|
environment.insert("XRT_DEBUG_GUI".into(), "1".into());
|
||||||
environment.insert("XRT_CURATED_GUI".into(), "1".into());
|
environment.insert("XRT_CURATED_GUI".into(), "1".into());
|
||||||
|
environment.insert("U_PACING_APP_USE_MIN_FRAME_PERIOD".into(), "1".into());
|
||||||
environment.insert(
|
environment.insert(
|
||||||
"LD_LIBRARY_PATH".into(),
|
"LD_LIBRARY_PATH".into(),
|
||||||
format!("{pfx}/lib:{pfx}/lib64", pfx = prefix),
|
format!("{pfx}/lib:{pfx}/lib64", pfx = prefix),
|
||||||
|
|
|
@ -17,6 +17,7 @@ pub fn openhmd_profile() -> Profile {
|
||||||
environment.insert("XRT_COMPOSITOR_COMPUTE".into(), "1".into());
|
environment.insert("XRT_COMPOSITOR_COMPUTE".into(), "1".into());
|
||||||
environment.insert("XRT_DEBUG_GUI".into(), "1".into());
|
environment.insert("XRT_DEBUG_GUI".into(), "1".into());
|
||||||
environment.insert("XRT_CURATED_GUI".into(), "1".into());
|
environment.insert("XRT_CURATED_GUI".into(), "1".into());
|
||||||
|
environment.insert("U_PACING_APP_USE_MIN_FRAME_PERIOD".into(), "1".into());
|
||||||
environment.insert(
|
environment.insert(
|
||||||
"LD_LIBRARY_PATH".into(),
|
"LD_LIBRARY_PATH".into(),
|
||||||
format!("{pfx}/lib:{pfx}/lib64", pfx = prefix),
|
format!("{pfx}/lib:{pfx}/lib64", pfx = prefix),
|
||||||
|
|
|
@ -19,6 +19,7 @@ pub fn survive_profile() -> Profile {
|
||||||
environment.insert("SURVIVE_TIMECODE_OFFSET_MS".into(), "-6.94".into());
|
environment.insert("SURVIVE_TIMECODE_OFFSET_MS".into(), "-6.94".into());
|
||||||
environment.insert("XRT_DEBUG_GUI".into(), "1".into());
|
environment.insert("XRT_DEBUG_GUI".into(), "1".into());
|
||||||
environment.insert("XRT_CURATED_GUI".into(), "1".into());
|
environment.insert("XRT_CURATED_GUI".into(), "1".into());
|
||||||
|
environment.insert("U_PACING_APP_USE_MIN_FRAME_PERIOD".into(), "1".into());
|
||||||
environment.insert(
|
environment.insert(
|
||||||
"LD_LIBRARY_PATH".into(),
|
"LD_LIBRARY_PATH".into(),
|
||||||
format!("{pfx}/lib:{pfx}/lib64", pfx = prefix),
|
format!("{pfx}/lib:{pfx}/lib64", pfx = prefix),
|
||||||
|
|
|
@ -15,6 +15,7 @@ pub fn wivrn_profile() -> Profile {
|
||||||
);
|
);
|
||||||
environment.insert("XRT_DEBUG_GUI".into(), "1".into());
|
environment.insert("XRT_DEBUG_GUI".into(), "1".into());
|
||||||
environment.insert("XRT_CURATED_GUI".into(), "1".into());
|
environment.insert("XRT_CURATED_GUI".into(), "1".into());
|
||||||
|
environment.insert("U_PACING_APP_USE_MIN_FRAME_PERIOD".into(), "1".into());
|
||||||
Profile {
|
Profile {
|
||||||
uuid: "wivrn-default".into(),
|
uuid: "wivrn-default".into(),
|
||||||
name: format!("WiVRn - {name} Default", name = APP_NAME),
|
name: format!("WiVRn - {name} Default", name = APP_NAME),
|
||||||
|
|
|
@ -17,6 +17,7 @@ pub fn wmr_profile() -> Profile {
|
||||||
environment.insert("XRT_COMPOSITOR_COMPUTE".into(), "1".into());
|
environment.insert("XRT_COMPOSITOR_COMPUTE".into(), "1".into());
|
||||||
environment.insert("XRT_DEBUG_GUI".into(), "1".into());
|
environment.insert("XRT_DEBUG_GUI".into(), "1".into());
|
||||||
environment.insert("XRT_CURATED_GUI".into(), "1".into());
|
environment.insert("XRT_CURATED_GUI".into(), "1".into());
|
||||||
|
environment.insert("U_PACING_APP_USE_MIN_FRAME_PERIOD".into(), "1".into());
|
||||||
environment.insert(
|
environment.insert(
|
||||||
"LD_LIBRARY_PATH".into(),
|
"LD_LIBRARY_PATH".into(),
|
||||||
format!("{pfx}/lib:{pfx}/lib64", pfx = prefix),
|
format!("{pfx}/lib:{pfx}/lib64", pfx = prefix),
|
||||||
|
|
131
src/ui/app.rs
131
src/ui/app.rs
|
@ -8,6 +8,7 @@ use super::job_worker::job::WorkerJob;
|
||||||
use super::job_worker::JobWorker;
|
use super::job_worker::JobWorker;
|
||||||
use super::libsurvive_setup_window::LibsurviveSetupWindow;
|
use super::libsurvive_setup_window::LibsurviveSetupWindow;
|
||||||
use super::main_view::MainViewMsg;
|
use super::main_view::MainViewMsg;
|
||||||
|
use super::util::open_with_default_handler;
|
||||||
use crate::builders::build_basalt::get_build_basalt_jobs;
|
use crate::builders::build_basalt::get_build_basalt_jobs;
|
||||||
use crate::builders::build_libsurvive::get_build_libsurvive_jobs;
|
use crate::builders::build_libsurvive::get_build_libsurvive_jobs;
|
||||||
use crate::builders::build_mercury::get_build_mercury_job;
|
use crate::builders::build_mercury::get_build_mercury_job;
|
||||||
|
@ -34,19 +35,20 @@ use crate::file_builders::openvrpaths_vrpath::{
|
||||||
use crate::file_utils::setcap_cap_sys_nice_eip;
|
use crate::file_utils::setcap_cap_sys_nice_eip;
|
||||||
use crate::linux_distro::get_distro;
|
use crate::linux_distro::get_distro;
|
||||||
use crate::log_parser::MonadoLog;
|
use crate::log_parser::MonadoLog;
|
||||||
use crate::paths::get_ipc_file_path;
|
use crate::paths::{get_data_dir, get_ipc_file_path};
|
||||||
use crate::profile::{Profile, XRServiceType};
|
use crate::profile::{Profile, XRServiceType};
|
||||||
use crate::profiles::lighthouse::lighthouse_profile;
|
use crate::profiles::lighthouse::lighthouse_profile;
|
||||||
use crate::profiles::openhmd::openhmd_profile;
|
use crate::profiles::openhmd::openhmd_profile;
|
||||||
use crate::profiles::survive::survive_profile;
|
use crate::profiles::survive::survive_profile;
|
||||||
use crate::profiles::wivrn::wivrn_profile;
|
use crate::profiles::wivrn::wivrn_profile;
|
||||||
use crate::profiles::wmr::wmr_profile;
|
use crate::profiles::wmr::wmr_profile;
|
||||||
|
use crate::stateless_action;
|
||||||
use crate::ui::build_window::{BuildWindowMsg, BuildWindowOutMsg};
|
use crate::ui::build_window::{BuildWindowMsg, BuildWindowOutMsg};
|
||||||
use crate::ui::debug_view::DebugViewInit;
|
use crate::ui::debug_view::DebugViewInit;
|
||||||
use crate::ui::libsurvive_setup_window::LibsurviveSetupMsg;
|
use crate::ui::libsurvive_setup_window::LibsurviveSetupMsg;
|
||||||
use crate::ui::main_view::{MainView, MainViewInit, MainViewOutMsg};
|
use crate::ui::main_view::{MainView, MainViewInit, MainViewOutMsg};
|
||||||
use crate::xr_devices::XRDevice;
|
use crate::xr_devices::XRDevice;
|
||||||
use crate::{stateless_action, withclones};
|
use gtk::glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use relm4::actions::{AccelsPlus, ActionGroupName, RelmAction, RelmActionGroup};
|
use relm4::actions::{AccelsPlus, ActionGroupName, RelmAction, RelmActionGroup};
|
||||||
use relm4::adw::traits::MessageDialogExt;
|
use relm4::adw::traits::MessageDialogExt;
|
||||||
|
@ -124,6 +126,8 @@ pub enum Msg {
|
||||||
Quit,
|
Quit,
|
||||||
ParseLog(Vec<String>),
|
ParseLog(Vec<String>),
|
||||||
ConfigFbt,
|
ConfigFbt,
|
||||||
|
DebugOpenPrefix,
|
||||||
|
DebugOpenData,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
@ -619,6 +623,15 @@ impl SimpleComponent for App {
|
||||||
));
|
));
|
||||||
self.application.quit();
|
self.application.quit();
|
||||||
}
|
}
|
||||||
|
Msg::DebugOpenData => {
|
||||||
|
open_with_default_handler(&format!("file://{}", get_data_dir()));
|
||||||
|
}
|
||||||
|
Msg::DebugOpenPrefix => {
|
||||||
|
open_with_default_handler(&format!(
|
||||||
|
"file://{}",
|
||||||
|
self.get_selected_profile().prefix
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -712,59 +725,76 @@ impl SimpleComponent for App {
|
||||||
|
|
||||||
let mut actions = RelmActionGroup::<AppActionGroup>::new();
|
let mut actions = RelmActionGroup::<AppActionGroup>::new();
|
||||||
|
|
||||||
{
|
stateless_action!(
|
||||||
withclones![sender];
|
actions,
|
||||||
stateless_action!(actions, BuildProfileAction, {
|
BuildProfileAction,
|
||||||
|
clone!(@strong sender => move |_| {
|
||||||
sender.input_sender().emit(Msg::BuildProfile(false, false));
|
sender.input_sender().emit(Msg::BuildProfile(false, false));
|
||||||
});
|
})
|
||||||
}
|
);
|
||||||
{
|
stateless_action!(
|
||||||
withclones![sender];
|
actions,
|
||||||
stateless_action!(actions, BuildProfileCleanAction, {
|
BuildProfileCleanAction,
|
||||||
|
clone!(@strong sender => move |_| {
|
||||||
sender.input_sender().emit(Msg::BuildProfile(true, false));
|
sender.input_sender().emit(Msg::BuildProfile(true, false));
|
||||||
});
|
})
|
||||||
}
|
);
|
||||||
{
|
stateless_action!(
|
||||||
withclones![sender];
|
actions,
|
||||||
stateless_action!(actions, BuildProfileDebugAction, {
|
BuildProfileDebugAction,
|
||||||
|
clone!(@strong sender => move |_| {
|
||||||
sender.input_sender().emit(Msg::BuildProfile(false, true));
|
sender.input_sender().emit(Msg::BuildProfile(false, true));
|
||||||
});
|
})
|
||||||
}
|
);
|
||||||
{
|
stateless_action!(
|
||||||
withclones![sender];
|
actions,
|
||||||
stateless_action!(actions, BuildProfileCleanDebugAction, {
|
BuildProfileCleanDebugAction,
|
||||||
|
clone!(@strong sender => move |_| {
|
||||||
sender.input_sender().emit(Msg::BuildProfile(true, true));
|
sender.input_sender().emit(Msg::BuildProfile(true, true));
|
||||||
});
|
})
|
||||||
}
|
);
|
||||||
{
|
stateless_action!(
|
||||||
withclones![sender];
|
actions,
|
||||||
stateless_action!(actions, ConfigFbtAction, {
|
ConfigFbtAction,
|
||||||
|
clone!(@strong sender => move |_| {
|
||||||
sender.input_sender().emit(Msg::ConfigFbt);
|
sender.input_sender().emit(Msg::ConfigFbt);
|
||||||
});
|
})
|
||||||
}
|
);
|
||||||
{
|
{
|
||||||
let abd_sender = model.about_dialog.sender().clone();
|
let abd_sender = model.about_dialog.sender().clone();
|
||||||
stateless_action!(actions, AboutAction, {
|
stateless_action!(actions, AboutAction, move |_| {
|
||||||
abd_sender.send(()).unwrap();
|
abd_sender.send(()).unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
{
|
stateless_action!(
|
||||||
withclones![sender];
|
actions,
|
||||||
stateless_action!(actions, QuitAction, {
|
QuitAction,
|
||||||
|
clone!(@strong sender => move |_| {
|
||||||
sender.input(Msg::Quit);
|
sender.input(Msg::Quit);
|
||||||
});
|
})
|
||||||
}
|
);
|
||||||
{
|
stateless_action!(
|
||||||
withclones![sender];
|
actions,
|
||||||
actions.add_action(RelmAction::<DebugViewToggleAction>::new_stateful(
|
DebugOpenDataAction,
|
||||||
&model.enable_debug_view,
|
clone!(@strong sender => move |_| {
|
||||||
move |_, state| {
|
sender.input(Msg::DebugOpenData);
|
||||||
let s = *state;
|
})
|
||||||
*state = !s;
|
);
|
||||||
sender.input(Msg::EnableDebugViewChanged(*state));
|
stateless_action!(
|
||||||
},
|
actions,
|
||||||
))
|
DebugOpenPrefixAction,
|
||||||
}
|
clone!(@strong sender => move |_| {
|
||||||
|
sender.input(Msg::DebugOpenPrefix);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
actions.add_action(RelmAction::<DebugViewToggleAction>::new_stateful(
|
||||||
|
&model.enable_debug_view,
|
||||||
|
clone!(@strong sender => move |_, state| {
|
||||||
|
let s = *state;
|
||||||
|
*state = !s;
|
||||||
|
sender.input(Msg::EnableDebugViewChanged(*state));
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
|
||||||
root.insert_action_group(AppActionGroup::NAME, Some(&actions.into_action_group()));
|
root.insert_action_group(AppActionGroup::NAME, Some(&actions.into_action_group()));
|
||||||
|
|
||||||
|
@ -780,13 +810,13 @@ impl SimpleComponent for App {
|
||||||
model.config.clone(),
|
model.config.clone(),
|
||||||
));
|
));
|
||||||
|
|
||||||
{
|
glib::timeout_add_local(
|
||||||
withclones![sender];
|
Duration::from_millis(1000),
|
||||||
glib::timeout_add_local(Duration::from_millis(1000), move || {
|
clone!(@strong sender => move || {
|
||||||
sender.input(Msg::ClockTicking);
|
sender.input(Msg::ClockTicking);
|
||||||
glib::ControlFlow::Continue
|
glib::ControlFlow::Continue
|
||||||
});
|
}),
|
||||||
}
|
);
|
||||||
|
|
||||||
model.split_view = Some(widgets.split_view.clone());
|
model.split_view = Some(widgets.split_view.clone());
|
||||||
|
|
||||||
|
@ -803,3 +833,6 @@ new_stateless_action!(pub BuildProfileCleanDebugAction, AppActionGroup, "buildpr
|
||||||
new_stateless_action!(pub ConfigFbtAction, AppActionGroup, "configfbt");
|
new_stateless_action!(pub ConfigFbtAction, AppActionGroup, "configfbt");
|
||||||
new_stateless_action!(pub QuitAction, AppActionGroup, "quit");
|
new_stateless_action!(pub QuitAction, AppActionGroup, "quit");
|
||||||
new_stateful_action!(pub DebugViewToggleAction, AppActionGroup, "debugviewtoggle", (), bool);
|
new_stateful_action!(pub DebugViewToggleAction, AppActionGroup, "debugviewtoggle", (), bool);
|
||||||
|
|
||||||
|
new_stateless_action!(pub DebugOpenDataAction, AppActionGroup, "debugopendata");
|
||||||
|
new_stateless_action!(pub DebugOpenPrefixAction, AppActionGroup, "debugopenprefix");
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::log_level::LogLevel;
|
use crate::log_level::LogLevel;
|
||||||
use crate::log_parser::MonadoLog;
|
use crate::log_parser::MonadoLog;
|
||||||
use crate::withclones;
|
use crate::ui::app::{DebugOpenDataAction, DebugOpenPrefixAction};
|
||||||
|
use gtk::glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use relm4::prelude::*;
|
use relm4::prelude::*;
|
||||||
use relm4::{ComponentSender, SimpleComponent};
|
use relm4::{ComponentSender, SimpleComponent};
|
||||||
|
@ -57,6 +58,15 @@ impl SimpleComponent for DebugView {
|
||||||
type Input = DebugViewMsg;
|
type Input = DebugViewMsg;
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
|
menu! {
|
||||||
|
debug_menu: {
|
||||||
|
section! {
|
||||||
|
"Open Envision _Data Folder" => DebugOpenDataAction,
|
||||||
|
"Open _Prefix Folder" => DebugOpenPrefixAction,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
gtk::Box {
|
gtk::Box {
|
||||||
set_orientation: gtk::Orientation::Vertical,
|
set_orientation: gtk::Orientation::Vertical,
|
||||||
|
@ -66,6 +76,11 @@ impl SimpleComponent for DebugView {
|
||||||
set_hexpand: true,
|
set_hexpand: true,
|
||||||
set_vexpand: false,
|
set_vexpand: false,
|
||||||
add_css_class: "flat",
|
add_css_class: "flat",
|
||||||
|
pack_end: debug_menu_btn = >k::MenuButton {
|
||||||
|
set_icon_name: "view-more-symbolic",
|
||||||
|
set_tooltip_text: Some("Debug Actions..."),
|
||||||
|
set_menu_model: Some(&debug_menu),
|
||||||
|
},
|
||||||
pack_end: search_toggle = >k::ToggleButton {
|
pack_end: search_toggle = >k::ToggleButton {
|
||||||
set_icon_name: "edit-find-symbolic",
|
set_icon_name: "edit-find-symbolic",
|
||||||
set_tooltip_text: Some("Filter Log"),
|
set_tooltip_text: Some("Filter Log"),
|
||||||
|
@ -255,24 +270,18 @@ impl SimpleComponent for DebugView {
|
||||||
if let Some(btn) = log_level_dropdown.first_child() {
|
if let Some(btn) = log_level_dropdown.first_child() {
|
||||||
btn.add_css_class("flat");
|
btn.add_css_class("flat");
|
||||||
}
|
}
|
||||||
{
|
log_level_dropdown.connect_selected_notify(clone!(@strong sender => move |dd| {
|
||||||
withclones![sender];
|
sender.input(Self::Input::LogLevelChanged(
|
||||||
log_level_dropdown.connect_selected_notify(move |dd| {
|
*LogLevel::iter()
|
||||||
sender.input(Self::Input::LogLevelChanged(
|
.as_slice()
|
||||||
*LogLevel::iter()
|
.get(dd.selected() as usize)
|
||||||
.as_slice()
|
.unwrap(),
|
||||||
.get(dd.selected() as usize)
|
));
|
||||||
.unwrap(),
|
}));
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
adw::StyleManager::default().connect_dark_notify(clone!(@strong sender => move |_| {
|
||||||
withclones![sender];
|
sender.input(Self::Input::SetColorScheme);
|
||||||
adw::StyleManager::default().connect_dark_notify(move |_| {
|
}));
|
||||||
sender.input(Self::Input::SetColorScheme);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Self::set_color_scheme(&textbuf);
|
Self::set_color_scheme(&textbuf);
|
||||||
|
|
||||||
let mut model = Self {
|
let mut model = Self {
|
||||||
|
|
|
@ -2,9 +2,9 @@ use crate::{
|
||||||
file_builders::monado_config_v0::{TrackerRole, XrtTrackerRole},
|
file_builders::monado_config_v0::{TrackerRole, XrtTrackerRole},
|
||||||
ui::fbt_config_editor::FbtConfigEditorMsg,
|
ui::fbt_config_editor::FbtConfigEditorMsg,
|
||||||
ui::preference_rows::{combo_row, entry_row},
|
ui::preference_rows::{combo_row, entry_row},
|
||||||
withclones,
|
|
||||||
};
|
};
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
|
use gtk::glib::clone;
|
||||||
use relm4::{factory::AsyncFactoryComponent, prelude::*, AsyncFactorySender};
|
use relm4::{factory::AsyncFactoryComponent, prelude::*, AsyncFactorySender};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -54,27 +54,29 @@ impl AsyncFactoryComponent for TrackerRoleModel {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
add: {
|
add: {
|
||||||
withclones![sender];
|
|
||||||
let tr = self.tracker_role.clone();
|
let tr = self.tracker_role.clone();
|
||||||
&entry_row("Device serial", self.tracker_role.device_serial.as_str(), move |row| {
|
&entry_row(
|
||||||
let mut ntr = tr.clone();
|
"Device serial",
|
||||||
ntr.device_serial = row.text().to_string();
|
self.tracker_role.device_serial.as_str(),
|
||||||
sender.input(Self::Input::Changed(ntr));
|
clone!(@strong sender => move |row| {
|
||||||
})
|
let mut ntr = tr.clone();
|
||||||
|
ntr.device_serial = row.text().to_string();
|
||||||
|
sender.input(Self::Input::Changed(ntr));
|
||||||
|
})
|
||||||
|
)
|
||||||
},
|
},
|
||||||
add: {
|
add: {
|
||||||
withclones![sender];
|
|
||||||
let tr = self.tracker_role.clone();
|
let tr = self.tracker_role.clone();
|
||||||
&combo_row("Tracker role", None, &tr.role.clone().to_picker_string(),
|
&combo_row("Tracker role", None, &tr.role.clone().to_picker_string(),
|
||||||
XrtTrackerRole::iter()
|
XrtTrackerRole::iter()
|
||||||
.map(XrtTrackerRole::to_picker_string)
|
.map(XrtTrackerRole::to_picker_string)
|
||||||
.map(String::from)
|
.map(String::from)
|
||||||
.collect::<Vec<String>>(),
|
.collect::<Vec<String>>(),
|
||||||
move |row| {
|
clone!(@strong sender => move |row| {
|
||||||
let mut ntr = tr.clone();
|
let mut ntr = tr.clone();
|
||||||
ntr.role = XrtTrackerRole::from_number(&row.selected());
|
ntr.role = XrtTrackerRole::from_number(&row.selected());
|
||||||
sender.input(Self::Input::Changed(ntr));
|
sender.input(Self::Input::Changed(ntr));
|
||||||
}
|
})
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,9 @@ use crate::{
|
||||||
dump_monado_config_v0, get_monado_config_v0, MonadoConfigV0, TrackerRole,
|
dump_monado_config_v0, get_monado_config_v0, MonadoConfigV0, TrackerRole,
|
||||||
},
|
},
|
||||||
ui::factories::tracker_role_group_factory::TrackerRoleModelInit,
|
ui::factories::tracker_role_group_factory::TrackerRoleModelInit,
|
||||||
withclones,
|
|
||||||
};
|
};
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
|
use gtk::glib::clone;
|
||||||
use relm4::{factory::AsyncFactoryVecDeque, prelude::*};
|
use relm4::{factory::AsyncFactoryVecDeque, prelude::*};
|
||||||
|
|
||||||
#[tracker::track]
|
#[tracker::track]
|
||||||
|
@ -113,18 +113,12 @@ impl SimpleComponent for FbtConfigEditor {
|
||||||
.label("Save")
|
.label("Save")
|
||||||
.css_classes(["suggested-action"])
|
.css_classes(["suggested-action"])
|
||||||
.build();
|
.build();
|
||||||
{
|
add_btn.connect_clicked(clone!(@strong sender => move |_| {
|
||||||
withclones![sender];
|
sender.input(Self::Input::TrackerRoleNew);
|
||||||
add_btn.connect_clicked(move |_| {
|
}));
|
||||||
sender.input(Self::Input::TrackerRoleNew);
|
save_btn.connect_clicked(clone!(@strong sender => move |_| {
|
||||||
});
|
sender.input(Self::Input::Save);
|
||||||
};
|
}));
|
||||||
{
|
|
||||||
withclones![sender];
|
|
||||||
save_btn.connect_clicked(move |_| {
|
|
||||||
sender.input(Self::Input::Save);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut model = Self {
|
let mut model = Self {
|
||||||
win: None,
|
win: None,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{profile::Profile, withclones};
|
use crate::profile::{LighthouseDriver, Profile};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
job::{CmdWorkerData, FuncWorkerOut, WorkerJob},
|
job::{CmdWorkerData, FuncWorkerOut, WorkerJob},
|
||||||
|
@ -89,7 +89,7 @@ impl Worker for InternalJobWorker {
|
||||||
let mut job = self.jobs.pop_front().unwrap();
|
let mut job = self.jobs.pop_front().unwrap();
|
||||||
match &mut job {
|
match &mut job {
|
||||||
WorkerJob::Cmd(data) => {
|
WorkerJob::Cmd(data) => {
|
||||||
withclones![data];
|
let data = data.clone();
|
||||||
if let Ok(mut cmd) = Command::new(data.command)
|
if let Ok(mut cmd) = Command::new(data.command)
|
||||||
.args(data.args)
|
.args(data.args)
|
||||||
.envs(data.environment)
|
.envs(data.environment)
|
||||||
|
@ -161,10 +161,15 @@ impl InternalJobWorker {
|
||||||
) -> relm4::WorkerHandle<InternalJobWorker> {
|
) -> relm4::WorkerHandle<InternalJobWorker> {
|
||||||
let mut env = prof.environment.clone();
|
let mut env = prof.environment.clone();
|
||||||
if !env.contains_key("LH_DRIVER") {
|
if !env.contains_key("LH_DRIVER") {
|
||||||
env.insert(
|
match prof.lighthouse_driver {
|
||||||
"LH_DRIVER".into(),
|
// don't set LH_DRIVER for steamvr driver, set this instead
|
||||||
prof.lighthouse_driver.to_string().to_lowercase(),
|
LighthouseDriver::SteamVR => {
|
||||||
);
|
env.insert("STEAMVR_LH_ENABLE".into(), "true".into());
|
||||||
|
}
|
||||||
|
d => {
|
||||||
|
env.insert("LH_DRIVER".into(), d.to_string().to_lowercase());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let mut launch_opts = prof.xrservice_launch_options.trim();
|
let mut launch_opts = prof.xrservice_launch_options.trim();
|
||||||
let debug_launch_opts = if debug {
|
let debug_launch_opts = if debug {
|
||||||
|
|
|
@ -1,13 +1,6 @@
|
||||||
#[macro_export]
|
|
||||||
macro_rules! withclones {
|
|
||||||
($($var:ident),+) => {
|
|
||||||
$(let $var = $var.clone();)+
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! stateless_action {
|
macro_rules! stateless_action {
|
||||||
($group:ident, $name:ident, $ex:expr) => {
|
($group:ident, $name:ident, $fun:expr) => {
|
||||||
$group.add_action(RelmAction::<$name>::new_stateless(move |_| $ex));
|
$group.add_action(RelmAction::<$name>::new_stateless($fun));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ use super::install_wivrn_box::{InstallWivrnBox, InstallWivrnBoxInit, InstallWivr
|
||||||
use super::profile_editor::{ProfileEditor, ProfileEditorMsg, ProfileEditorOutMsg};
|
use super::profile_editor::{ProfileEditor, ProfileEditorMsg, ProfileEditorOutMsg};
|
||||||
use super::stardust::stardust_view::StardustView;
|
use super::stardust::stardust_view::StardustView;
|
||||||
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::file_utils::mount_has_nosuid;
|
use crate::file_utils::mount_has_nosuid;
|
||||||
use crate::gpu_profile::{
|
use crate::gpu_profile::{
|
||||||
|
@ -18,6 +19,7 @@ use crate::ui::app::{
|
||||||
};
|
};
|
||||||
use crate::ui::profile_editor::ProfileEditorInit;
|
use crate::ui::profile_editor::ProfileEditorInit;
|
||||||
use crate::ui::stardust::stardust_view::StardustViewInit;
|
use crate::ui::stardust::stardust_view::StardustViewInit;
|
||||||
|
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::*;
|
||||||
|
@ -48,6 +50,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,
|
||||||
#[tracker::do_not_track]
|
#[tracker::do_not_track]
|
||||||
stardust_view: Controller<StardustView>,
|
stardust_view: Controller<StardustView>,
|
||||||
|
@ -338,6 +342,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,
|
||||||
|
@ -347,6 +352,13 @@ impl SimpleComponent for MainView {
|
||||||
add_css_class: "padded",
|
add_css_class: "padded",
|
||||||
#[track = "model.changed(Self::selected_profile())"]
|
#[track = "model.changed(Self::selected_profile())"]
|
||||||
set_visible: model.selected_profile.lighthouse_driver == LighthouseDriver::Survive,
|
set_visible: model.selected_profile.lighthouse_driver == LighthouseDriver::Survive,
|
||||||
|
set_hexpand: true,
|
||||||
|
set_vexpand: false,
|
||||||
|
set_spacing: 12,
|
||||||
|
add_css_class: "card",
|
||||||
|
add_css_class: "padded",
|
||||||
|
#[track = "model.changed(Self::selected_profile())"]
|
||||||
|
set_visible: model.selected_profile.lighthouse_driver == LighthouseDriver::Survive,
|
||||||
gtk::Label {
|
gtk::Label {
|
||||||
add_css_class: "heading",
|
add_css_class: "heading",
|
||||||
set_hexpand: true,
|
set_hexpand: true,
|
||||||
|
@ -376,7 +388,27 @@ impl SimpleComponent for MainView {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
gtk::Label {
|
||||||
|
add_css_class: "dim-label",
|
||||||
|
set_hexpand: true,
|
||||||
|
set_label: concat!(
|
||||||
|
"Libsurvive needs to import your SteamVR calibration to work ",
|
||||||
|
"properly. You need to have used SteamVR with this setup ",
|
||||||
|
"before to be able to import its calibration."
|
||||||
|
),
|
||||||
|
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,
|
||||||
|
connect_clicked[sender] => move |_| {
|
||||||
|
sender.output(Self::Output::OpenLibsurviveSetup).expect("Sender output failed");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
} -> {
|
} -> {
|
||||||
set_name: Some("main_view"),
|
set_name: Some("main_view"),
|
||||||
|
@ -479,6 +511,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![]));
|
||||||
}
|
}
|
||||||
|
@ -506,6 +541,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()));
|
||||||
|
@ -644,6 +684,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,
|
||||||
|
@ -661,6 +708,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,
|
||||||
stardust_view: StardustView::builder()
|
stardust_view: StardustView::builder()
|
||||||
.launch(StardustViewInit {
|
.launch(StardustViewInit {
|
||||||
|
|
|
@ -16,5 +16,6 @@ pub mod preference_rows;
|
||||||
pub mod profile_editor;
|
pub mod profile_editor;
|
||||||
pub mod stardust;
|
pub mod stardust;
|
||||||
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;
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use crate::withclones;
|
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
gio,
|
gio,
|
||||||
glib::{self, GString},
|
glib::{self, clone, GString},
|
||||||
};
|
};
|
||||||
use relm4::prelude::*;
|
use relm4::prelude::*;
|
||||||
|
|
||||||
|
@ -94,33 +93,26 @@ pub fn path_row<F: Fn(Option<String>) + 'static + Clone>(
|
||||||
.valign(gtk::Align::Center)
|
.valign(gtk::Align::Center)
|
||||||
.build();
|
.build();
|
||||||
row.add_suffix(&clear_btn);
|
row.add_suffix(&clear_btn);
|
||||||
{
|
clear_btn.connect_clicked(clone!(@strong path_label, @strong cb => move |_| {
|
||||||
withclones![path_label, cb];
|
path_label.set_label("(None)");
|
||||||
clear_btn.connect_clicked(move |_| {
|
cb(None)
|
||||||
path_label.set_label("(None)");
|
}));
|
||||||
cb(None)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let filedialog = gtk::FileDialog::builder()
|
let filedialog = gtk::FileDialog::builder()
|
||||||
.modal(true)
|
.modal(true)
|
||||||
.title(format!("Select Path for {}", title))
|
.title(format!("Select Path for {}", title))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
{
|
row.connect_activated(clone!(@strong path_label => move |_| {
|
||||||
withclones![path_label];
|
filedialog.select_folder(root_win.as_ref(), gio::Cancellable::NONE, clone!(@strong path_label, @strong cb => move |res| {
|
||||||
row.connect_activated(move |_| {
|
if let Ok(file) = res {
|
||||||
withclones![path_label, cb];
|
if let Some(path) = file.path() {
|
||||||
filedialog.select_folder(root_win.as_ref(), gio::Cancellable::NONE, move |res| {
|
let path_s = path.to_str().unwrap().to_string();
|
||||||
if let Ok(file) = res {
|
path_label.set_text(path_s.as_str());
|
||||||
if let Some(path) = file.path() {
|
cb(Some(path_s))
|
||||||
let path_s = path.to_str().unwrap().to_string();
|
|
||||||
path_label.set_text(path_s.as_str());
|
|
||||||
cb(Some(path_s))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
});
|
}))
|
||||||
}
|
}));
|
||||||
|
|
||||||
row
|
row
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,9 @@ use crate::{
|
||||||
env_var_descriptions::env_var_descriptions_as_paragraph,
|
env_var_descriptions::env_var_descriptions_as_paragraph,
|
||||||
profile::{LighthouseDriver, Profile, XRServiceType},
|
profile::{LighthouseDriver, Profile, XRServiceType},
|
||||||
ui::preference_rows::{combo_row, entry_row, path_row, switch_row},
|
ui::preference_rows::{combo_row, entry_row, path_row, switch_row},
|
||||||
withclones,
|
|
||||||
};
|
};
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
|
use gtk::glib::clone;
|
||||||
use relm4::{factory::AsyncFactoryVecDeque, prelude::*};
|
use relm4::{factory::AsyncFactoryVecDeque, prelude::*};
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
@ -80,35 +80,30 @@ impl SimpleComponent for ProfileEditor {
|
||||||
set_vexpand: true,
|
set_vexpand: true,
|
||||||
add: maingrp = &adw::PreferencesGroup {
|
add: maingrp = &adw::PreferencesGroup {
|
||||||
set_title: "General",
|
set_title: "General",
|
||||||
add: {
|
add: &entry_row(
|
||||||
withclones![prof];
|
"Profile Name",
|
||||||
&entry_row("Profile Name", model.profile.borrow().name.as_str(), move |row| {
|
model.profile.borrow().name.as_str(),
|
||||||
|
clone!(@strong prof => move |row| {
|
||||||
prof.borrow_mut().name = row.text().to_string();
|
prof.borrow_mut().name = row.text().to_string();
|
||||||
})
|
})
|
||||||
},
|
),
|
||||||
add: {
|
add: &switch_row(
|
||||||
withclones![prof];
|
"Update on Build", None,
|
||||||
&switch_row(
|
model.profile.borrow().pull_on_build,
|
||||||
"Update on Build", None,
|
clone!(@strong prof => move |_, state| {
|
||||||
model.profile.borrow().pull_on_build,
|
prof.borrow_mut().pull_on_build = state;
|
||||||
move |_, state| {
|
gtk::glib::Propagation::Proceed
|
||||||
prof.borrow_mut().pull_on_build = state;
|
})
|
||||||
gtk::glib::Propagation::Proceed
|
),
|
||||||
}
|
add: &path_row(
|
||||||
)
|
"Install Prefix",
|
||||||
},
|
None,
|
||||||
add: {
|
Some(model.profile.borrow().prefix.clone()),
|
||||||
withclones![prof];
|
Some(init.root_win.clone()),
|
||||||
&path_row(
|
clone!(@strong prof => move |n_path| {
|
||||||
"Install Prefix",
|
prof.borrow_mut().prefix = n_path.unwrap_or_default()
|
||||||
None,
|
}),
|
||||||
Some(model.profile.borrow().prefix.clone()),
|
),
|
||||||
Some(init.root_win.clone()),
|
|
||||||
move |n_path| {
|
|
||||||
prof.borrow_mut().prefix = n_path.unwrap_or_default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
add: xrservicegrp = &adw::PreferencesGroup {
|
add: xrservicegrp = &adw::PreferencesGroup {
|
||||||
set_title: "XR Service",
|
set_title: "XR Service",
|
||||||
|
@ -119,227 +114,176 @@ impl SimpleComponent for ProfileEditor {
|
||||||
"For launch options, you can insert %command% as ",
|
"For launch options, you can insert %command% as ",
|
||||||
"a placeholder for the actual XR Service command.",
|
"a placeholder for the actual XR Service command.",
|
||||||
)),
|
)),
|
||||||
add: {
|
add: &combo_row(
|
||||||
withclones![prof];
|
"XR Service Type",
|
||||||
&combo_row(
|
Some("Monado is for PCVR headsets, while WiVRn is for Andorid standalone headsets"),
|
||||||
"XR Service Type",
|
model.profile.borrow().xrservice_type.to_string().as_str(),
|
||||||
Some("Monado is for PCVR headsets, while WiVRn is for Andorid standalone headsets"),
|
XRServiceType::iter()
|
||||||
model.profile.borrow().xrservice_type.to_string().as_str(),
|
.map(XRServiceType::to_string)
|
||||||
XRServiceType::iter()
|
.collect::<Vec<String>>(),
|
||||||
.map(XRServiceType::to_string)
|
clone!(@strong prof => move |row| {
|
||||||
.collect::<Vec<String>>(),
|
prof.borrow_mut().xrservice_type =
|
||||||
move |row| {
|
XRServiceType::from_number(row.selected());
|
||||||
prof.borrow_mut().xrservice_type =
|
}),
|
||||||
XRServiceType::from_number(row.selected());
|
),
|
||||||
},
|
add: &entry_row(
|
||||||
)
|
"XR Service Launch Options",
|
||||||
},
|
model.profile.borrow().xrservice_launch_options.as_str(),
|
||||||
add: {
|
clone!(@strong prof => move |row| {
|
||||||
withclones![prof];
|
prof.borrow_mut().xrservice_launch_options = row.text().trim().to_string();
|
||||||
&entry_row(
|
})
|
||||||
"XR Service Launch Options",
|
),
|
||||||
model.profile.borrow().xrservice_launch_options.as_str(),
|
add: &combo_row(
|
||||||
move |row| {
|
"Lighthouse Driver",
|
||||||
prof.borrow_mut().xrservice_launch_options = row.text().trim().to_string();
|
Some(concat!(
|
||||||
}
|
"Driver for lighhouse tracked XR devices (ie: Valve Index, HTC Vive...). Only applicable for Monado.\n\n",
|
||||||
)
|
"Vive: 3DOF tracking\n\n",
|
||||||
},
|
"Survive: 6DOF reverse engineered lighthouse tracking provided by Libsurvive\n\n",
|
||||||
add: {
|
"SteamVR: 6DOF lighthouse tracking using the proprietary SteamVR driver",
|
||||||
withclones![prof];
|
)),
|
||||||
&combo_row(
|
model.profile.borrow().lighthouse_driver.to_string().as_str(),
|
||||||
"Lighthouse Driver",
|
LighthouseDriver::iter()
|
||||||
Some(concat!(
|
.map(LighthouseDriver::to_string)
|
||||||
"Driver for lighhouse tracked XR devices (ie: Valve Index, HTC Vive...). Only applicable for Monado.\n\n",
|
.collect::<Vec<String>>(),
|
||||||
"Vive: 3DOF tracking\n\n",
|
clone!(@strong prof => move |row| {
|
||||||
"Survive: 6DOF reverse engineered lighthouse tracking provided by Libsurvive\n\n",
|
prof.borrow_mut().lighthouse_driver =
|
||||||
"SteamVR: 6DOF lighthouse tracking using the proprietary SteamVR driver",
|
LighthouseDriver::from_number(row.selected());
|
||||||
)),
|
})
|
||||||
model.profile.borrow().lighthouse_driver.to_string().as_str(),
|
),
|
||||||
LighthouseDriver::iter()
|
add: &path_row(
|
||||||
.map(LighthouseDriver::to_string)
|
"XR Service Path",
|
||||||
.collect::<Vec<String>>(),
|
None,
|
||||||
move |row| {
|
Some(model.profile.borrow().xrservice_path.clone()),
|
||||||
prof.borrow_mut().lighthouse_driver =
|
Some(init.root_win.clone()),
|
||||||
LighthouseDriver::from_number(row.selected());
|
clone!(@strong prof => move |n_path| {
|
||||||
}
|
prof.borrow_mut().xrservice_path = n_path.unwrap_or_default()
|
||||||
)
|
}),
|
||||||
},
|
),
|
||||||
add: {
|
add: &entry_row(
|
||||||
withclones![prof];
|
"XR Service Repo",
|
||||||
&path_row(
|
model.profile.borrow().xrservice_repo.clone().unwrap_or_default().as_str(),
|
||||||
"XR Service Path",
|
clone!(@strong prof => move |row| {
|
||||||
None,
|
let n_val = row.text().to_string();
|
||||||
Some(model.profile.borrow().xrservice_path.clone()),
|
prof.borrow_mut().xrservice_repo = (!n_val.is_empty()).then_some(n_val);
|
||||||
Some(init.root_win.clone()),
|
})
|
||||||
move |n_path| {
|
),
|
||||||
prof.borrow_mut().xrservice_path = n_path.unwrap_or_default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
add: {
|
|
||||||
withclones![prof];
|
|
||||||
&entry_row(
|
|
||||||
"XR Service Repo",
|
|
||||||
model.profile.borrow().xrservice_repo.clone().unwrap_or_default().as_str(),
|
|
||||||
move |row| {
|
|
||||||
let n_val = row.text().to_string();
|
|
||||||
prof.borrow_mut().xrservice_repo = (!n_val.is_empty()).then_some(n_val);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
add: model.xrservice_cmake_flags_rows.widget(),
|
add: model.xrservice_cmake_flags_rows.widget(),
|
||||||
add: opencompgrp = &adw::PreferencesGroup {
|
add: opencompgrp = &adw::PreferencesGroup {
|
||||||
set_title: "OpenComposite",
|
set_title: "OpenComposite",
|
||||||
set_description: Some("OpenVR driver built on top of OpenXR\n\nWhen specifying a repository, you can set a specific git ref (branch, tag, commit...) by appending a '#' followed by the ref"),
|
set_description: Some("OpenVR driver built on top of OpenXR\n\nWhen specifying a repository, you can set a specific git ref (branch, tag, commit...) by appending a '#' followed by the ref"),
|
||||||
add: {
|
add: &path_row(
|
||||||
withclones![prof];
|
"OpenComposite Path", None,
|
||||||
&path_row(
|
Some(model.profile.borrow().opencomposite_path.clone()),
|
||||||
"OpenComposite Path", None,
|
Some(init.root_win.clone()),
|
||||||
Some(model.profile.borrow().opencomposite_path.clone()),
|
clone!(@strong prof => move |n_path| {
|
||||||
Some(init.root_win.clone()),
|
prof.borrow_mut().opencomposite_path = n_path.unwrap_or_default();
|
||||||
move |n_path| {
|
})
|
||||||
prof.borrow_mut().opencomposite_path = n_path.unwrap_or_default();
|
),
|
||||||
}
|
add: &entry_row(
|
||||||
)
|
"OpenComposite Repo",
|
||||||
},
|
model.profile.borrow().opencomposite_repo.clone().unwrap_or_default().as_str(),
|
||||||
add: {
|
clone!(@strong prof => move |row| {
|
||||||
withclones![prof];
|
let n_val = row.text().to_string();
|
||||||
&entry_row(
|
prof.borrow_mut().opencomposite_repo = (!n_val.is_empty()).then_some(n_val);
|
||||||
"OpenComposite Repo",
|
})
|
||||||
model.profile.borrow().opencomposite_repo.clone().unwrap_or_default().as_str(),
|
),
|
||||||
move |row| {
|
|
||||||
let n_val = row.text().to_string();
|
|
||||||
prof.borrow_mut().opencomposite_repo = (!n_val.is_empty()).then_some(n_val);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
add: libsurvivegrp = &adw::PreferencesGroup {
|
add: libsurvivegrp = &adw::PreferencesGroup {
|
||||||
set_title: "Libsurvive",
|
set_title: "Libsurvive",
|
||||||
set_description: Some("Lighthouse tracking driver\n\nWhen specifying a repository, you can set a specific git ref (branch, tag, commit...) by appending a '#' followed by the ref"),
|
set_description: Some("Lighthouse tracking driver\n\nWhen specifying a repository, you can set a specific git ref (branch, tag, commit...) by appending a '#' followed by the ref"),
|
||||||
add: {
|
add: &switch_row(
|
||||||
withclones![prof];
|
"Enable Libsurvive", None,
|
||||||
&switch_row(
|
model.profile.borrow().features.libsurvive.enabled,
|
||||||
"Enable Libsurvive", None,
|
clone!(@strong prof => move |_, state| {
|
||||||
model.profile.borrow().features.libsurvive.enabled,
|
prof.borrow_mut().features.libsurvive.enabled = state;
|
||||||
move |_, state| {
|
gtk::glib::Propagation::Proceed
|
||||||
prof.borrow_mut().features.libsurvive.enabled = state;
|
})
|
||||||
gtk::glib::Propagation::Proceed
|
),
|
||||||
}
|
add: &path_row(
|
||||||
)
|
"Libsurvive Path", None,
|
||||||
},
|
model.profile.borrow().features.libsurvive.path.clone(),
|
||||||
add: {
|
Some(init.root_win.clone()),
|
||||||
withclones![prof];
|
clone!(@strong prof => move |n_path| {
|
||||||
&path_row(
|
prof.borrow_mut().features.libsurvive.path = n_path;
|
||||||
"Libsurvive Path", None,
|
})
|
||||||
model.profile.borrow().features.libsurvive.path.clone(),
|
),
|
||||||
Some(init.root_win.clone()),
|
add: &entry_row(
|
||||||
move |n_path| {
|
"Libsurvive Repo",
|
||||||
prof.borrow_mut().features.libsurvive.path = n_path;
|
model.profile.borrow().features.libsurvive.repo.clone().unwrap_or_default().as_str(),
|
||||||
}
|
clone!(@strong prof => move |row| {
|
||||||
)
|
let n_val = row.text().to_string();
|
||||||
},
|
prof.borrow_mut().features.libsurvive.repo = (!n_val.is_empty()).then_some(n_val);
|
||||||
add: {
|
})
|
||||||
withclones![prof];
|
),
|
||||||
&entry_row(
|
|
||||||
"Libsurvive Repo",
|
|
||||||
model.profile.borrow().features.libsurvive.repo.clone().unwrap_or_default().as_str(),
|
|
||||||
move |row| {
|
|
||||||
let n_val = row.text().to_string();
|
|
||||||
prof.borrow_mut().features.libsurvive.repo = (!n_val.is_empty()).then_some(n_val);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
add: openhmdgrp = &adw::PreferencesGroup {
|
add: openhmdgrp = &adw::PreferencesGroup {
|
||||||
set_title: "OpenHMD",
|
set_title: "OpenHMD",
|
||||||
set_description: Some("Legacy driver for older Oculus HMDs\n\nWhen specifying a repository, you can set a specific git ref (branch, tag, commit...) by appending a '#' followed by the ref"),
|
set_description: Some("Legacy driver for older Oculus HMDs\n\nWhen specifying a repository, you can set a specific git ref (branch, tag, commit...) by appending a '#' followed by the ref"),
|
||||||
add: {
|
add: &switch_row(
|
||||||
withclones![prof];
|
"Enable OpenHMD", None,
|
||||||
&switch_row(
|
model.profile.borrow().features.openhmd.enabled,
|
||||||
"Enable OpenHMD", None,
|
clone!(@strong prof => move |_, state| {
|
||||||
model.profile.borrow().features.openhmd.enabled,
|
prof.borrow_mut().features.openhmd.enabled = state;
|
||||||
move |_, state| {
|
gtk::glib::Propagation::Proceed
|
||||||
prof.borrow_mut().features.openhmd.enabled = state;
|
})
|
||||||
gtk::glib::Propagation::Proceed
|
),
|
||||||
}
|
add: &path_row(
|
||||||
)
|
"OpenHMD Path", None,
|
||||||
},
|
model.profile.borrow().features.openhmd.path.clone(),
|
||||||
add: {
|
Some(init.root_win.clone()),
|
||||||
withclones![prof];
|
clone!(@strong prof => move |n_path| {
|
||||||
&path_row(
|
prof.borrow_mut().features.openhmd.path = n_path;
|
||||||
"OpenHMD Path", None,
|
})
|
||||||
model.profile.borrow().features.openhmd.path.clone(),
|
),
|
||||||
Some(init.root_win.clone()),
|
add: &entry_row(
|
||||||
move |n_path| {
|
"OpenHMD Repo",
|
||||||
prof.borrow_mut().features.openhmd.path = n_path;
|
model.profile.borrow().features.openhmd.repo.clone().unwrap_or_default().as_str(),
|
||||||
}
|
clone!(@strong prof => move |row| {
|
||||||
)
|
let n_val = row.text().to_string();
|
||||||
},
|
prof.borrow_mut().features.openhmd.repo = (!n_val.is_empty()).then_some(n_val);
|
||||||
add: {
|
})
|
||||||
withclones![prof];
|
),
|
||||||
&entry_row(
|
|
||||||
"OpenHMD Repo",
|
|
||||||
model.profile.borrow().features.openhmd.repo.clone().unwrap_or_default().as_str(),
|
|
||||||
move |row| {
|
|
||||||
let n_val = row.text().to_string();
|
|
||||||
prof.borrow_mut().features.openhmd.repo = (!n_val.is_empty()).then_some(n_val);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
add: basaltgrp = &adw::PreferencesGroup {
|
add: basaltgrp = &adw::PreferencesGroup {
|
||||||
set_title: "Basalt",
|
set_title: "Basalt",
|
||||||
set_description: Some("Camera based SLAM tracking driver\n\nWhen specifying a repository, you can set a specific git ref (branch, tag, commit...) by appending a '#' followed by the ref"),
|
set_description: Some("Camera based SLAM tracking driver\n\nWhen specifying a repository, you can set a specific git ref (branch, tag, commit...) by appending a '#' followed by the ref"),
|
||||||
add: {
|
add: &switch_row(
|
||||||
withclones![prof];
|
"Enable Basalt", None,
|
||||||
&switch_row(
|
model.profile.borrow().features.basalt.enabled,
|
||||||
"Enable Basalt", None,
|
clone!(@strong prof => move |_, state| {
|
||||||
model.profile.borrow().features.basalt.enabled,
|
prof.borrow_mut().features.basalt.enabled = state;
|
||||||
move |_, state| {
|
gtk::glib::Propagation::Proceed
|
||||||
prof.borrow_mut().features.basalt.enabled = state;
|
})
|
||||||
gtk::glib::Propagation::Proceed
|
),
|
||||||
}
|
add: &path_row(
|
||||||
)
|
"Basalt Path", None,
|
||||||
},
|
model.profile.borrow().features.basalt.path.clone(),
|
||||||
add: {
|
Some(init.root_win.clone()),
|
||||||
withclones![prof];
|
clone!(@strong prof => move |n_path| {
|
||||||
&path_row(
|
prof.borrow_mut().features.basalt.path = n_path;
|
||||||
"Basalt Path", None,
|
})
|
||||||
model.profile.borrow().features.basalt.path.clone(),
|
),
|
||||||
Some(init.root_win.clone()),
|
add: &entry_row(
|
||||||
move |n_path| {
|
"Basalt Repo",
|
||||||
prof.borrow_mut().features.basalt.path = n_path;
|
model.profile.borrow().features.basalt.repo.clone().unwrap_or_default().as_str(),
|
||||||
}
|
clone!(@strong prof => move |row| {
|
||||||
)
|
let n_val = row.text().to_string();
|
||||||
},
|
prof.borrow_mut().features.basalt.repo = n_val.is_empty().then_some(n_val);
|
||||||
add: {
|
})
|
||||||
withclones![prof];
|
),
|
||||||
&entry_row(
|
|
||||||
"Basalt Repo",
|
|
||||||
model.profile.borrow().features.basalt.repo.clone().unwrap_or_default().as_str(),
|
|
||||||
move |row| {
|
|
||||||
let n_val = row.text().to_string();
|
|
||||||
prof.borrow_mut().features.basalt.repo = n_val.is_empty().then_some(n_val);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
add: mercurygrp = &adw::PreferencesGroup {
|
add: mercurygrp = &adw::PreferencesGroup {
|
||||||
set_title: "Mercury",
|
set_title: "Mercury",
|
||||||
set_description: Some("Camera and OpenCV based hand tracking driver"),
|
set_description: Some("Camera and OpenCV based hand tracking driver"),
|
||||||
add: {
|
add: &switch_row(
|
||||||
withclones![prof];
|
"Enable Mercury", None,
|
||||||
&switch_row(
|
model.profile.borrow().features.mercury_enabled,
|
||||||
"Enable Mercury", None,
|
clone!(@strong prof => move |_, state| {
|
||||||
model.profile.borrow().features.mercury_enabled,
|
prof.borrow_mut().features.mercury_enabled = state;
|
||||||
move |_, state| {
|
gtk::glib::Propagation::Proceed
|
||||||
prof.borrow_mut().features.mercury_enabled = state;
|
})
|
||||||
gtk::glib::Propagation::Proceed
|
),
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
add: model.env_rows.widget(),
|
add: model.env_rows.widget(),
|
||||||
}
|
}
|
||||||
|
@ -465,16 +409,17 @@ impl SimpleComponent for ProfileEditor {
|
||||||
.halign(gtk::Align::End)
|
.halign(gtk::Align::End)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
withclones![sender, name_entry, popover];
|
add_btn.connect_clicked(
|
||||||
add_btn.connect_clicked(move |_| {
|
clone!(@strong sender, @strong name_entry, @strong popover => move |_| {
|
||||||
let key_gstr = name_entry.text();
|
let key_gstr = name_entry.text();
|
||||||
let key = key_gstr.trim();
|
let key = key_gstr.trim();
|
||||||
if !key.is_empty() {
|
if !key.is_empty() {
|
||||||
popover.popdown();
|
popover.popdown();
|
||||||
name_entry.set_text("");
|
name_entry.set_text("");
|
||||||
sender.input($event(key.to_string()));
|
sender.input($event(key.to_string()));
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
);
|
||||||
btn
|
btn
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,10 +12,9 @@ use crate::{
|
||||||
build_window::{BuildStatus, BuildWindow, BuildWindowMsg, BuildWindowOutMsg},
|
build_window::{BuildStatus, BuildWindow, BuildWindowMsg, BuildWindowOutMsg},
|
||||||
job_worker::{internal_worker::JobWorkerOut, job::WorkerJob, JobWorker},
|
job_worker::{internal_worker::JobWorkerOut, job::WorkerJob, JobWorker},
|
||||||
},
|
},
|
||||||
withclones,
|
|
||||||
};
|
};
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
use gtk::prelude::*;
|
use gtk::{prelude::*, glib::clone};
|
||||||
use relm4::{
|
use relm4::{
|
||||||
actions::{ActionGroupName, RelmAction, RelmActionGroup},
|
actions::{ActionGroupName, RelmAction, RelmActionGroup},
|
||||||
new_action_group, new_stateless_action,
|
new_action_group, new_stateless_action,
|
||||||
|
@ -468,22 +467,16 @@ impl SimpleComponent for StardustView {
|
||||||
|
|
||||||
let mut actions = RelmActionGroup::<StardustActionGroup>::new();
|
let mut actions = RelmActionGroup::<StardustActionGroup>::new();
|
||||||
|
|
||||||
{
|
stateless_action!(actions, BuildStardustAction, clone!(@strong sender => move |_| {
|
||||||
withclones![sender];
|
sender
|
||||||
stateless_action!(actions, BuildStardustAction, {
|
.input_sender()
|
||||||
sender
|
.emit(Self::Input::BuildStardust { update: false });
|
||||||
.input_sender()
|
}));
|
||||||
.emit(Self::Input::BuildStardust { update: false });
|
stateless_action!(actions, UpdateStardustAction, clone!(@strong sender => move |_| {
|
||||||
});
|
sender
|
||||||
}
|
.input_sender()
|
||||||
{
|
.emit(Self::Input::BuildStardust { update: true });
|
||||||
withclones![sender];
|
}));
|
||||||
stateless_action!(actions, UpdateStardustAction, {
|
|
||||||
sender
|
|
||||||
.input_sender()
|
|
||||||
.emit(Self::Input::BuildStardust { update: true });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
root.insert_action_group(
|
root.insert_action_group(
|
||||||
StardustActionGroup::NAME,
|
StardustActionGroup::NAME,
|
||||||
|
|
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 }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use gtk4::prelude::*;
|
use gtk4::{gio, prelude::*};
|
||||||
|
|
||||||
pub fn limit_dropdown_width(dd: >k4::DropDown, chars: i32) {
|
pub fn limit_dropdown_width(dd: >k4::DropDown, chars: i32) {
|
||||||
let mut dd_child = dd
|
let mut dd_child = dd
|
||||||
|
@ -44,3 +44,9 @@ pub fn warning_heading() -> gtk4::Box {
|
||||||
|
|
||||||
b
|
b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn open_with_default_handler(uri: &str) {
|
||||||
|
if let Err(e) = gio::AppInfo::launch_default_for_uri(uri, gio::AppLaunchContext::NONE) {
|
||||||
|
eprintln!("Error opening uri {}: {}", uri, e)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue