mirror of
https://gitlab.com/gabmus/envision.git
synced 2025-04-21 03:54:49 +00:00
feat!: refactor xr devices around vec
This commit is contained in:
parent
0d65e3a99b
commit
cd7fe1ac7b
6 changed files with 451 additions and 276 deletions
|
@ -43,7 +43,7 @@ use crate::ui::build_window::{BuildWindowMsg, BuildWindowOutMsg};
|
|||
use crate::ui::debug_view::DebugViewInit;
|
||||
use crate::ui::libsurvive_setup_window::LibsurviveSetupMsg;
|
||||
use crate::ui::main_view::{MainView, MainViewInit, MainViewOutMsg};
|
||||
use crate::xr_devices::XRDevices;
|
||||
use crate::xr_devices::XRDevice;
|
||||
use crate::{stateless_action, withclones};
|
||||
use gtk::prelude::*;
|
||||
use relm4::actions::{AccelsPlus, ActionGroupName, RelmAction, RelmActionGroup};
|
||||
|
@ -91,7 +91,7 @@ pub struct App {
|
|||
#[tracker::do_not_track]
|
||||
profiles: Vec<Profile>,
|
||||
#[tracker::do_not_track]
|
||||
xr_devices: XRDevices,
|
||||
xr_devices: Vec<XRDevice>,
|
||||
#[tracker::do_not_track]
|
||||
fbt_config_editor: Option<Controller<FbtConfigEditor>>,
|
||||
#[tracker::do_not_track]
|
||||
|
@ -164,7 +164,7 @@ impl App {
|
|||
return;
|
||||
};
|
||||
self.debug_view.sender().emit(DebugViewMsg::ClearLog);
|
||||
self.xr_devices = XRDevices::default();
|
||||
self.xr_devices = vec![];
|
||||
if prof.can_start() {
|
||||
remove_file(&get_ipc_file_path(&prof.xrservice_type))
|
||||
.is_err()
|
||||
|
@ -225,7 +225,7 @@ impl App {
|
|||
self.main_view
|
||||
.sender()
|
||||
.emit(MainViewMsg::XRServiceActiveChanged(false, None));
|
||||
self.xr_devices = XRDevices::default();
|
||||
self.xr_devices = vec![];
|
||||
}
|
||||
|
||||
pub fn profiles_list(config: &Config) -> Vec<Profile> {
|
||||
|
@ -318,10 +318,13 @@ impl SimpleComponent for App {
|
|||
state.exit_status.is_none() && !state.stop_requested
|
||||
} {
|
||||
if let Some(monado) = self.libmonado.as_ref() {
|
||||
self.xr_devices.merge(XRDevices::from_libmonado(monado));
|
||||
self.xr_devices = XRDevice::merge(
|
||||
&self.xr_devices,
|
||||
&XRDevice::from_libmonado(monado),
|
||||
);
|
||||
self.main_view
|
||||
.sender()
|
||||
.emit(MainViewMsg::UpdateDevices(Some(self.xr_devices.clone())));
|
||||
.emit(MainViewMsg::UpdateDevices(self.xr_devices.clone()));
|
||||
} else {
|
||||
if let Some(so) = self.get_selected_profile().libmonado_so() {
|
||||
self.libmonado = libmonado_rs::Monado::create(so).ok();
|
||||
|
@ -338,23 +341,11 @@ impl SimpleComponent for App {
|
|||
match MonadoLog::new_from_str(row.as_str()) {
|
||||
None => {}
|
||||
Some(parsed) => {
|
||||
// if parsed.func == "p_create_system" {
|
||||
// match XRDevices::from_log_message(parsed.message.as_str()) {
|
||||
// None => {}
|
||||
// Some(devices) => {
|
||||
// self.xr_devices.merge(devices.clone());
|
||||
// self.main_view.sender().emit(MainViewMsg::UpdateDevices(
|
||||
// Some(self.xr_devices.clone()),
|
||||
// ));
|
||||
// break;
|
||||
// }
|
||||
// };
|
||||
// } else {
|
||||
// self.xr_devices
|
||||
// .search_log_for_generic_trackers(parsed.message.as_str());
|
||||
// }
|
||||
self.xr_devices
|
||||
.search_log_for_generic_trackers(parsed.message.as_str());
|
||||
if let Some(tracker) =
|
||||
XRDevice::generic_tracker_from_log_row(parsed.message.as_str())
|
||||
{
|
||||
self.xr_devices.push(tracker);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -389,7 +380,7 @@ impl SimpleComponent for App {
|
|||
self.start_xrservice(sender);
|
||||
}
|
||||
None => {
|
||||
worker.stop();
|
||||
self.shutdown_xrservice();
|
||||
self.restart_xrservice = true;
|
||||
}
|
||||
}
|
||||
|
@ -668,7 +659,7 @@ impl SimpleComponent for App {
|
|||
profiles,
|
||||
xrservice_worker: None,
|
||||
build_worker: None,
|
||||
xr_devices: XRDevices::default(),
|
||||
xr_devices: vec![],
|
||||
fbt_config_editor: None,
|
||||
restart_xrservice: false,
|
||||
libmonado: None,
|
||||
|
|
|
@ -1,48 +1,43 @@
|
|||
use super::{
|
||||
alert::alert,
|
||||
factories::device_row_factory::{DeviceRowModel, DeviceRowModelInit, DeviceRowState},
|
||||
};
|
||||
use crate::{
|
||||
file_builders::monado_config_v0::dump_generic_trackers,
|
||||
xr_devices::{XRDevice, XRDevices},
|
||||
xr_devices::{XRDevice, XRDeviceType},
|
||||
};
|
||||
use adw::prelude::*;
|
||||
use relm4::prelude::*;
|
||||
|
||||
use super::alert::alert;
|
||||
use relm4::{factory::FactoryVecDeque, prelude::*, Sender};
|
||||
|
||||
#[tracker::track]
|
||||
pub struct DevicesBox {
|
||||
devices: Option<XRDevices>,
|
||||
devices: Vec<XRDevice>,
|
||||
|
||||
#[tracker::do_not_track]
|
||||
device_rows: FactoryVecDeque<DeviceRowModel>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DevicesBoxMsg {
|
||||
UpdateDevices(Option<XRDevices>),
|
||||
UpdateDevices(Vec<XRDevice>),
|
||||
DumpGenericTrackers,
|
||||
}
|
||||
|
||||
impl DevicesBox {
|
||||
fn get_dev(&self, key: XRDevice) -> Option<String> {
|
||||
match &self.devices {
|
||||
None => None,
|
||||
Some(devs) => match key {
|
||||
XRDevice::Head => devs.head.clone(),
|
||||
XRDevice::Left => devs.left.clone(),
|
||||
XRDevice::Right => devs.right.clone(),
|
||||
XRDevice::Gamepad => devs.gamepad.clone(),
|
||||
XRDevice::Eyes => devs.eyes.clone(),
|
||||
XRDevice::HandTrackingLeft => devs.hand_tracking_left.clone(),
|
||||
XRDevice::HandTrackingRight => devs.hand_tracking_right.clone(),
|
||||
XRDevice::GenericTracker => {
|
||||
if devs.generic_trackers.is_empty() {
|
||||
return None;
|
||||
} else {
|
||||
return Some(devs.generic_trackers.join(", "));
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
fn create_save_trackers_btn(sender: Sender<DevicesBoxMsg>) -> gtk::Button {
|
||||
let btn = gtk::Button::builder()
|
||||
.halign(gtk::Align::Center)
|
||||
.valign(gtk::Align::Center)
|
||||
.icon_name("document-save-symbolic")
|
||||
.tooltip_text("Save current trackers")
|
||||
.css_classes(["circular", "flat"])
|
||||
.build();
|
||||
|
||||
fn get_dev_or_none(&self, key: XRDevice) -> String {
|
||||
self.get_dev(key).unwrap_or("None".into())
|
||||
btn.connect_clicked(move |_| {
|
||||
sender.emit(DevicesBoxMsg::DumpGenericTrackers);
|
||||
});
|
||||
|
||||
btn
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,147 +55,129 @@ impl SimpleComponent for DevicesBox {
|
|||
set_spacing: 12,
|
||||
set_margin_top: 12,
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_visible: model.devices.is_some(),
|
||||
gtk::ListBox {
|
||||
add_css_class: "boxed-list",
|
||||
set_selection_mode: gtk::SelectionMode::None,
|
||||
set_margin_all: 12,
|
||||
// Head
|
||||
adw::ActionRow {
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_icon_name: Some(match model.get_dev(XRDevice::Head) {
|
||||
Some(name) => match name.as_str() {
|
||||
"Simulated HMD" => "dialog-warning-symbolic",
|
||||
_ => "emblem-ok-symbolic",
|
||||
},
|
||||
None => "dialog-question-symbolic",
|
||||
}),
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_class_active: ("error", model.get_dev(XRDevice::Head).is_none()),
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_class_active: ("warning", model.get_dev_or_none(XRDevice::Head) == "Simulated HMD"),
|
||||
set_title: "Head",
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_subtitle: match model.get_dev_or_none(XRDevice::Head).as_str() {
|
||||
"Simulated HMD" => "No HMD detected (Simulated HMD)",
|
||||
s => s,
|
||||
},
|
||||
// TODO: status icon with popover
|
||||
},
|
||||
// Left
|
||||
adw::ActionRow {
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_icon_name: Some(match model.get_dev(XRDevice::Left) {
|
||||
Some(_) => "emblem-ok-symbolic",
|
||||
None => "dialog-question-symbolic",
|
||||
}),
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_class_active: ("error", model.get_dev(XRDevice::Left).is_none()),
|
||||
set_title: "Left",
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_subtitle: model.get_dev_or_none(XRDevice::Left).as_str(),
|
||||
// TODO: status icon with popover
|
||||
},
|
||||
// Right
|
||||
adw::ActionRow {
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_icon_name: Some(match model.get_dev(XRDevice::Right) {
|
||||
Some(_) => "emblem-ok-symbolic",
|
||||
None => "dialog-question-symbolic",
|
||||
}),
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_class_active: ("error", model.get_dev(XRDevice::Right).is_none()),
|
||||
set_title: "Right",
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_subtitle: model.get_dev_or_none(XRDevice::Right).as_str(),
|
||||
// TODO: status icon with popover
|
||||
},
|
||||
// Gamepad
|
||||
adw::ActionRow {
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_visible: model.get_dev(XRDevice::Gamepad).is_some(),
|
||||
set_icon_name: Some("emblem-ok-symbolic"),
|
||||
set_title: "Gamepad",
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_subtitle: model.get_dev_or_none(XRDevice::Gamepad).as_str(),
|
||||
},
|
||||
// Eyes
|
||||
adw::ActionRow {
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_visible: model.get_dev(XRDevice::Eyes).is_some(),
|
||||
set_icon_name: Some("emblem-ok-symbolic"),
|
||||
set_title: "Eye Tracking",
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_subtitle: model.get_dev_or_none(XRDevice::Eyes).as_str(),
|
||||
},
|
||||
// Hand Tracking Left
|
||||
adw::ActionRow {
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_visible: model.get_dev(XRDevice::HandTrackingLeft).is_some(),
|
||||
set_icon_name: Some("emblem-ok-symbolic"),
|
||||
set_title: "Hand Tracking Left",
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_subtitle: model.get_dev_or_none(XRDevice::HandTrackingLeft).as_str(),
|
||||
},
|
||||
// Hand Tracking Right
|
||||
adw::ActionRow {
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_visible: model.get_dev(XRDevice::HandTrackingRight).is_some(),
|
||||
set_icon_name: Some("emblem-ok-symbolic"),
|
||||
set_title: "Hand Tracking Right",
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_subtitle: model.get_dev_or_none(XRDevice::HandTrackingRight).as_str(),
|
||||
},
|
||||
// Generic Trackers
|
||||
adw::ActionRow {
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_visible: model.get_dev(XRDevice::GenericTracker).is_some(),
|
||||
set_icon_name: Some("emblem-ok-symbolic"),
|
||||
set_title: "Generic Trackers",
|
||||
#[track = "model.changed(Self::devices())"]
|
||||
set_subtitle: model.get_dev_or_none(XRDevice::GenericTracker).as_str(),
|
||||
add_suffix: save_trackers_btn = >k::Button {
|
||||
set_halign: gtk::Align::Center,
|
||||
set_valign: gtk::Align::Center,
|
||||
set_icon_name: "document-save-symbolic",
|
||||
set_tooltip_text: Some("Save current trackers"),
|
||||
set_css_classes: &["circular", "flat"],
|
||||
connect_clicked => move |_| {
|
||||
sender.input(Self::Input::DumpGenericTrackers);
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
set_visible: !model.devices.is_empty(),
|
||||
|
||||
append: &devices_listbox,
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>) {
|
||||
fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>) {
|
||||
self.reset();
|
||||
|
||||
match message {
|
||||
Self::Input::UpdateDevices(devs) => {
|
||||
self.set_devices(devs);
|
||||
let mut guard = self.device_rows.guard();
|
||||
guard.clear();
|
||||
if !self.devices.is_empty() {
|
||||
let mut has_head = false;
|
||||
let mut has_left = false;
|
||||
let mut has_right = false;
|
||||
let mut models: Vec<DeviceRowModelInit> = vec![];
|
||||
let mut generic: Vec<&XRDevice> = vec![];
|
||||
for dev in &self.devices {
|
||||
match dev.dev_type {
|
||||
XRDeviceType::Head => {
|
||||
has_head = true;
|
||||
let mut init = DeviceRowModelInit::from_xr_device(&dev);
|
||||
if dev.name == "Simulated HMD" {
|
||||
init.state = Some(DeviceRowState::Warning);
|
||||
init.subtitle = Some("No HMD detected (Simulated HMD)".into());
|
||||
}
|
||||
models.push(init);
|
||||
}
|
||||
XRDeviceType::Left => {
|
||||
has_left = true;
|
||||
models.push(DeviceRowModelInit::from_xr_device(&dev));
|
||||
}
|
||||
XRDeviceType::Right => {
|
||||
has_right = true;
|
||||
models.push(DeviceRowModelInit::from_xr_device(&dev));
|
||||
}
|
||||
XRDeviceType::GenericTracker => {
|
||||
generic.push(dev);
|
||||
}
|
||||
_ => {
|
||||
models.push(DeviceRowModelInit::from_xr_device(&dev));
|
||||
}
|
||||
};
|
||||
}
|
||||
if !generic.is_empty() {
|
||||
models.push(DeviceRowModelInit {
|
||||
title: Some(XRDeviceType::GenericTracker.to_string()),
|
||||
subtitle: Some(
|
||||
generic
|
||||
.iter()
|
||||
.map(|d| d.id.as_str())
|
||||
.collect::<Vec<&str>>()
|
||||
.join(", "),
|
||||
),
|
||||
suffix: Some(
|
||||
Self::create_save_trackers_btn(sender.input_sender().clone())
|
||||
.upcast::<gtk::Widget>(),
|
||||
),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
if !has_right {
|
||||
models.push(DeviceRowModelInit::new_missing(XRDeviceType::Right));
|
||||
}
|
||||
if !has_left {
|
||||
models.push(DeviceRowModelInit::new_missing(XRDeviceType::Left));
|
||||
}
|
||||
if !has_head {
|
||||
models.push(DeviceRowModelInit::new_missing(XRDeviceType::Head));
|
||||
}
|
||||
|
||||
models.sort_by(|m1, m2| {
|
||||
let dt1 = XRDeviceType::from_display_str(
|
||||
m1.title.as_ref().unwrap_or(&String::new()),
|
||||
);
|
||||
let dt2 = XRDeviceType::from_display_str(
|
||||
m2.title.as_ref().unwrap_or(&String::new()),
|
||||
);
|
||||
dt1.cmp(&dt2)
|
||||
});
|
||||
|
||||
for model in models {
|
||||
guard.push_back(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
Self::Input::DumpGenericTrackers => {
|
||||
if let Some(devs) = self.devices.as_ref() {
|
||||
let added = dump_generic_trackers(&devs.generic_trackers);
|
||||
let multi_title = format!("Added {} new trackers", added);
|
||||
let (title, msg) = match added {
|
||||
0 => (
|
||||
"No new trackers found",
|
||||
"All the currently connected trackers are already present in your configuration"
|
||||
let added = dump_generic_trackers(
|
||||
&self
|
||||
.devices
|
||||
.iter()
|
||||
.filter(|d| d.dev_type == XRDeviceType::GenericTracker)
|
||||
.map(|d| d.id.clone())
|
||||
.collect::<Vec<String>>(),
|
||||
);
|
||||
let multi_title = format!("Added {} new trackers", added);
|
||||
let (title, msg) = match added {
|
||||
0 => (
|
||||
"No new trackers found",
|
||||
concat!(
|
||||
"All the currently connected trackers ",
|
||||
"are already present in your configuration"
|
||||
),
|
||||
1 => (
|
||||
"Added 1 new tracker",
|
||||
"Edit your configuration to make sure that all the trackers have the appropriate roles assigned"
|
||||
),
|
||||
1 => (
|
||||
"Added 1 new tracker",
|
||||
concat!(
|
||||
"Edit your configuration to make sure that ",
|
||||
"all the trackers have the appropriate roles assigned"
|
||||
),
|
||||
_ => (
|
||||
multi_title.as_str(),
|
||||
"Edit your configuration to make sure that all the trackers have the appropriate roles assigned"
|
||||
),
|
||||
_ => (
|
||||
multi_title.as_str(),
|
||||
concat!(
|
||||
"Edit your configuration to make sure that ",
|
||||
"all the trackers have the appropriate roles assigned"
|
||||
),
|
||||
};
|
||||
alert(title, Some(msg), None);
|
||||
}
|
||||
),
|
||||
};
|
||||
alert(title, Some(msg), None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -210,9 +187,19 @@ impl SimpleComponent for DevicesBox {
|
|||
root: &Self::Root,
|
||||
sender: ComponentSender<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
let devices_listbox = gtk::ListBox::builder()
|
||||
.css_classes(["boxed-list"])
|
||||
.selection_mode(gtk::SelectionMode::None)
|
||||
.margin_start(12)
|
||||
.margin_end(12)
|
||||
.margin_top(12)
|
||||
.margin_bottom(12)
|
||||
.build();
|
||||
|
||||
let model = Self {
|
||||
tracker: 0,
|
||||
devices: None,
|
||||
devices: vec![],
|
||||
device_rows: FactoryVecDeque::new(devices_listbox.clone(), sender.input_sender()),
|
||||
};
|
||||
|
||||
let widgets = view_output!();
|
||||
|
|
124
src/ui/factories/device_row_factory.rs
Normal file
124
src/ui/factories/device_row_factory.rs
Normal file
|
@ -0,0 +1,124 @@
|
|||
use adw::prelude::*;
|
||||
use relm4::prelude::*;
|
||||
|
||||
use crate::{
|
||||
ui::devices_box::DevicesBoxMsg,
|
||||
xr_devices::{XRDevice, XRDeviceType},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum DeviceRowState {
|
||||
Ok,
|
||||
Error,
|
||||
Warning,
|
||||
}
|
||||
|
||||
impl Default for DeviceRowState {
|
||||
fn default() -> Self {
|
||||
Self::Ok
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceRowState {
|
||||
pub fn icon(&self) -> &str {
|
||||
match &self {
|
||||
Self::Ok => "emblem-ok-symbolic",
|
||||
Self::Error => "dialog-question-symbolic",
|
||||
Self::Warning => "dialog-warning-symbolic",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn class_name(&self) -> Option<&str> {
|
||||
match &self {
|
||||
Self::Ok => None,
|
||||
Self::Error => Some("error"),
|
||||
Self::Warning => Some("warning"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DeviceRowModel {
|
||||
title: String,
|
||||
subtitle: String,
|
||||
state: DeviceRowState,
|
||||
suffix: Option<gtk::Widget>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct DeviceRowModelInit {
|
||||
pub title: Option<String>,
|
||||
pub subtitle: Option<String>,
|
||||
pub state: Option<DeviceRowState>,
|
||||
pub suffix: Option<gtk::Widget>,
|
||||
}
|
||||
|
||||
impl DeviceRowModelInit {
|
||||
pub fn from_xr_device(d: &XRDevice) -> Self {
|
||||
Self {
|
||||
title: Some(d.dev_type.to_string()),
|
||||
subtitle: Some(d.name.clone()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_missing(t: XRDeviceType) -> Self {
|
||||
DeviceRowModelInit {
|
||||
title: Some(t.to_string()),
|
||||
subtitle: Some("None".into()),
|
||||
state: Some(DeviceRowState::Error),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[relm4::factory(pub)]
|
||||
impl FactoryComponent for DeviceRowModel {
|
||||
type Init = DeviceRowModelInit;
|
||||
type Input = ();
|
||||
type Output = ();
|
||||
type CommandOutput = ();
|
||||
type Widgets = DeviceRowModelWidgets;
|
||||
type ParentInput = DevicesBoxMsg;
|
||||
type ParentWidget = gtk::ListBox;
|
||||
|
||||
view! {
|
||||
root = adw::ActionRow {
|
||||
// TODO: replace with flat button that spawns popover
|
||||
add_prefix: icon = >k::Image {
|
||||
set_icon_name: Some(self.state.icon()),
|
||||
},
|
||||
set_title: self.title.as_str(),
|
||||
set_subtitle: self.subtitle.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
fn init_widgets(
|
||||
&mut self,
|
||||
_index: &Self::Index,
|
||||
root: &Self::Root,
|
||||
_returned_widget: &<Self::ParentWidget as relm4::factory::FactoryView>::ReturnedWidget,
|
||||
_sender: FactorySender<Self>,
|
||||
) -> Self::Widgets {
|
||||
let widgets = view_output!();
|
||||
|
||||
if let Some(suffix) = self.suffix.as_ref() {
|
||||
widgets.root.add_suffix(suffix);
|
||||
}
|
||||
if let Some(cls) = self.state.class_name() {
|
||||
widgets.root.add_css_class(cls);
|
||||
widgets.icon.add_css_class(cls);
|
||||
}
|
||||
|
||||
widgets
|
||||
}
|
||||
|
||||
fn init_model(init: Self::Init, _index: &Self::Index, _sender: FactorySender<Self>) -> Self {
|
||||
Self {
|
||||
title: init.title.unwrap_or_default(),
|
||||
subtitle: init.subtitle.unwrap_or_default(),
|
||||
state: init.state.unwrap_or_default(),
|
||||
suffix: init.suffix,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,2 +1,3 @@
|
|||
pub mod env_var_row_factory;
|
||||
pub mod tracker_role_group_factory;
|
||||
pub mod device_row_factory;
|
||||
|
|
|
@ -18,7 +18,7 @@ use crate::ui::app::{
|
|||
};
|
||||
use crate::ui::profile_editor::ProfileEditorInit;
|
||||
use crate::ui::util::{limit_dropdown_width, warning_heading};
|
||||
use crate::xr_devices::XRDevices;
|
||||
use crate::xr_devices::XRDevice;
|
||||
use gtk::prelude::*;
|
||||
use relm4::adw::traits::MessageDialogExt;
|
||||
use relm4::adw::ResponseAppearance;
|
||||
|
@ -66,7 +66,7 @@ pub enum MainViewMsg {
|
|||
DeleteProfile,
|
||||
DuplicateProfile,
|
||||
SaveProfile(Profile),
|
||||
UpdateDevices(Option<XRDevices>),
|
||||
UpdateDevices(Vec<XRDevice>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -462,7 +462,7 @@ impl SimpleComponent for MainView {
|
|||
Self::Input::XRServiceActiveChanged(active, profile) => {
|
||||
self.set_xrservice_active(active);
|
||||
if !active {
|
||||
sender.input(Self::Input::UpdateDevices(None));
|
||||
sender.input(Self::Input::UpdateDevices(vec![]));
|
||||
}
|
||||
self.steam_launch_options_box
|
||||
.sender()
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use std::slice::Iter;
|
||||
use crate::profile::Profile;
|
||||
use std::{fmt::Display, slice::Iter};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum XRDevice {
|
||||
const GENERIC_TRACKER_PREFIX: &str = "Found generic tracker device: ";
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
pub enum XRDeviceType {
|
||||
Head,
|
||||
Left,
|
||||
Right,
|
||||
|
@ -13,7 +14,34 @@ pub enum XRDevice {
|
|||
GenericTracker,
|
||||
}
|
||||
|
||||
impl XRDevice {
|
||||
impl Display for XRDeviceType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
Self::Head => "Head",
|
||||
Self::Left => "Left",
|
||||
Self::Right => "Right",
|
||||
Self::Gamepad => "Gamepad",
|
||||
Self::Eyes => "Eye Tracking",
|
||||
Self::HandTrackingLeft => "Hand tracking left",
|
||||
Self::HandTrackingRight => "Hand tracking right",
|
||||
Self::GenericTracker => "Generic tracker",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for XRDeviceType {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.as_number().cmp(&other.as_number()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for XRDeviceType {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.partial_cmp(other).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl XRDeviceType {
|
||||
pub fn iter() -> Iter<'static, Self> {
|
||||
[
|
||||
Self::Head,
|
||||
|
@ -40,58 +68,108 @@ impl XRDevice {
|
|||
Self::GenericTracker => "generic_tracker",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub struct XRDevices {
|
||||
pub head: Option<String>,
|
||||
pub left: Option<String>,
|
||||
pub right: Option<String>,
|
||||
pub gamepad: Option<String>,
|
||||
pub eyes: Option<String>,
|
||||
pub hand_tracking_left: Option<String>,
|
||||
pub hand_tracking_right: Option<String>,
|
||||
pub generic_trackers: Vec<String>,
|
||||
}
|
||||
|
||||
const GENERIC_TRACKER_PREFIX: &str = "Found generic tracker device: ";
|
||||
|
||||
impl XRDevices {
|
||||
fn set_device(&mut self, dev: &XRDevice, name: String) {
|
||||
match dev {
|
||||
XRDevice::Head => self.head = Some(name),
|
||||
XRDevice::Left => self.left = Some(name),
|
||||
XRDevice::Right => self.right = Some(name),
|
||||
XRDevice::Gamepad => self.gamepad = Some(name),
|
||||
XRDevice::Eyes => self.eyes = Some(name),
|
||||
XRDevice::HandTrackingLeft => self.hand_tracking_left = Some(name),
|
||||
XRDevice::HandTrackingRight => self.hand_tracking_right = Some(name),
|
||||
_ => {}
|
||||
pub fn as_number(&self) -> u32 {
|
||||
match self {
|
||||
Self::Head => 0,
|
||||
Self::Left => 1,
|
||||
Self::Right => 2,
|
||||
Self::Gamepad => 3,
|
||||
Self::Eyes => 4,
|
||||
Self::HandTrackingLeft => 5,
|
||||
Self::HandTrackingRight => 6,
|
||||
Self::GenericTracker => 7,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_libmonado(monado: &libmonado_rs::Monado) -> Self {
|
||||
let mut res = Self::default();
|
||||
pub fn from_monado_str(s: &str) -> Option<Self> {
|
||||
match s {
|
||||
"head" => Some(Self::Head),
|
||||
"left" => Some(Self::Left),
|
||||
"right" => Some(Self::Right),
|
||||
"gamepad" => Some(Self::Gamepad),
|
||||
"eyes" => Some(Self::Eyes),
|
||||
"hand_tracking.left" => Some(Self::HandTrackingLeft),
|
||||
"hand_tracking.right" => Some(Self::HandTrackingRight),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_display_str(s: &str) -> Self {
|
||||
match s {
|
||||
"Head" => Self::Head,
|
||||
"Left" => Self::Left,
|
||||
"Right" => Self::Right,
|
||||
"Gamepad" => Self::Gamepad,
|
||||
"Eye Tracking" => Self::Eyes,
|
||||
"Hand tracking left" => Self::HandTrackingLeft,
|
||||
"Hand tracking right" => Self::HandTrackingRight,
|
||||
"Generic tracker" => Self::GenericTracker,
|
||||
_ => Self::GenericTracker,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct XRDevice {
|
||||
pub dev_type: XRDeviceType,
|
||||
pub name: String,
|
||||
pub id: String,
|
||||
pub battery: f32, // battery percentage, from 0 to 1 maybe
|
||||
// still need to implement it in monado & gui
|
||||
}
|
||||
|
||||
impl Default for XRDevice {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
dev_type: XRDeviceType::GenericTracker,
|
||||
name: String::default(),
|
||||
id: String::default(),
|
||||
battery: f32::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl XRDevice {
|
||||
pub fn generic_tracker_from_log_row(s: &str) -> Option<Self> {
|
||||
if s.starts_with(GENERIC_TRACKER_PREFIX) {
|
||||
let n_tracker = s.trim_start_matches(GENERIC_TRACKER_PREFIX);
|
||||
return Some(Self {
|
||||
dev_type: XRDeviceType::GenericTracker,
|
||||
id: n_tracker.into(),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn from_libmonado(monado: &libmonado_rs::Monado) -> Vec<Self> {
|
||||
let mut res = vec![];
|
||||
[
|
||||
XRDevice::Head,
|
||||
XRDevice::Left,
|
||||
XRDevice::Right,
|
||||
XRDevice::Gamepad,
|
||||
XRDevice::Eyes,
|
||||
XRDeviceType::Head,
|
||||
XRDeviceType::Left,
|
||||
XRDeviceType::Right,
|
||||
XRDeviceType::Gamepad,
|
||||
XRDeviceType::Eyes,
|
||||
]
|
||||
.iter()
|
||||
.for_each(|xrd| {
|
||||
if let Ok(dev) = monado.device_from_role(xrd.to_monado_str()) {
|
||||
res.set_device(&xrd, dev.name);
|
||||
res.push(Self {
|
||||
name: dev.name,
|
||||
id: dev.id.to_string(),
|
||||
dev_type: xrd.clone(),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
});
|
||||
res
|
||||
}
|
||||
|
||||
pub fn from_log_message(s: &str) -> Option<Self> {
|
||||
pub fn from_log_message(s: &str) -> Vec<Self> {
|
||||
let mut res = vec![];
|
||||
let rows = s.split('\n');
|
||||
let mut in_section = false;
|
||||
let mut devs = Self::default();
|
||||
for row in rows {
|
||||
if !in_section && row.starts_with("\tIn roles:") {
|
||||
in_section = true;
|
||||
|
@ -103,64 +181,58 @@ impl XRDevices {
|
|||
}
|
||||
match row.trim().split(": ").collect::<Vec<&str>>()[..] {
|
||||
[_, "<none>"] => {}
|
||||
["head", val] => devs.head = Some(val.to_string()),
|
||||
["left", val] => devs.left = Some(val.to_string()),
|
||||
["right", val] => devs.right = Some(val.to_string()),
|
||||
["gamepad", val] => devs.gamepad = Some(val.to_string()),
|
||||
["eyes", val] => devs.eyes = Some(val.to_string()),
|
||||
["hand_tracking.left", val] => devs.hand_tracking_left = Some(val.to_string()),
|
||||
["hand_tracking.right", val] => {
|
||||
devs.hand_tracking_right = Some(val.to_string())
|
||||
[dev_type_s, name] => {
|
||||
if let Some(xrdt) = XRDeviceType::from_monado_str(dev_type_s) {
|
||||
res.push(Self {
|
||||
dev_type: xrdt,
|
||||
name: name.to_string(),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
if in_section {
|
||||
return Some(devs);
|
||||
}
|
||||
None
|
||||
res
|
||||
}
|
||||
|
||||
pub fn merge(&mut self, new: Self) {
|
||||
if new.head.is_some() {
|
||||
self.head = new.head;
|
||||
pub fn merge(old: &[Self], new: &[Self]) -> Vec<Self> {
|
||||
if old.is_empty() {
|
||||
return Vec::from(new);
|
||||
}
|
||||
if new.left.is_some() {
|
||||
self.left = new.left;
|
||||
}
|
||||
if new.right.is_some() {
|
||||
self.right = new.right;
|
||||
}
|
||||
if new.gamepad.is_some() {
|
||||
self.gamepad = new.gamepad;
|
||||
}
|
||||
if new.eyes.is_some() {
|
||||
self.eyes = new.eyes;
|
||||
}
|
||||
if new.hand_tracking_left.is_some() {
|
||||
self.hand_tracking_left = new.hand_tracking_left;
|
||||
}
|
||||
if new.hand_tracking_right.is_some() {
|
||||
self.hand_tracking_right = new.hand_tracking_right;
|
||||
}
|
||||
if !new.generic_trackers.is_empty() {
|
||||
self.generic_trackers.extend(
|
||||
new.generic_trackers
|
||||
.iter()
|
||||
.filter(|t| !self.generic_trackers.contains(t))
|
||||
.cloned()
|
||||
.collect::<Vec<String>>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn search_log_for_generic_trackers(&mut self, s: &str) {
|
||||
if s.starts_with(GENERIC_TRACKER_PREFIX) {
|
||||
let n_tracker = s.trim_start_matches(GENERIC_TRACKER_PREFIX);
|
||||
if !self.generic_trackers.contains(&n_tracker.to_string()) {
|
||||
self.generic_trackers.push(n_tracker.into());
|
||||
let new_dev_types = new
|
||||
.iter()
|
||||
.filter_map(|d| {
|
||||
if d.dev_type == XRDeviceType::GenericTracker {
|
||||
return None;
|
||||
}
|
||||
Some(d.dev_type)
|
||||
})
|
||||
.collect::<Vec<XRDeviceType>>();
|
||||
let mut res = old
|
||||
.iter()
|
||||
.filter(|d| !new_dev_types.contains(&d.dev_type))
|
||||
.map(Self::clone)
|
||||
.collect::<Vec<Self>>();
|
||||
let old_tracker_ids = old
|
||||
.iter()
|
||||
.filter_map(|d| {
|
||||
if d.dev_type == XRDeviceType::GenericTracker {
|
||||
return Some(d.id.clone());
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect::<Vec<String>>();
|
||||
for n_dev in new {
|
||||
if n_dev.dev_type == XRDeviceType::GenericTracker {
|
||||
if !old_tracker_ids.contains(&n_dev.id) {
|
||||
res.push(n_dev.clone());
|
||||
}
|
||||
} else {
|
||||
res.push(n_dev.clone());
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue