feat: xr devices can have multiple roles and they will be displayed as only one row

This commit is contained in:
Gabriele Musco 2024-08-10 11:31:27 +02:00
commit 58ceb44984
3 changed files with 81 additions and 113 deletions

View file

@ -51,28 +51,21 @@ impl SimpleComponent for DevicesBox {
let mut has_right = false; let mut has_right = false;
let mut models: Vec<DeviceRowModelInit> = Vec::new(); let mut models: Vec<DeviceRowModelInit> = Vec::new();
for dev in &self.devices { for dev in &self.devices {
match dev.dev_type { let mut row_model = DeviceRowModelInit::from(dev);
XRDeviceRole::Head => { if !has_head && dev.roles.contains(&XRDeviceRole::Head) {
has_head = true; has_head = true;
let mut init = DeviceRowModelInit::from(dev); if ["Simulated HMD", "Qwerty HMD"].contains(&dev.name.as_str()) {
if dev.name == "Simulated HMD" || dev.name == "Qwerty HMD" { row_model.state = Some(DeviceRowState::Warning);
init.state = Some(DeviceRowState::Warning); row_model.subtitle = Some(format!("No HMD detected ({})", dev.name));
init.subtitle = Some(format!("No HMD detected ({})", dev.name));
} }
models.push(init);
} }
XRDeviceRole::Left => { if !has_left && dev.roles.contains(&XRDeviceRole::Left) {
has_left = true; has_left = true;
models.push(DeviceRowModelInit::from(dev));
} }
XRDeviceRole::Right => { if !has_right && dev.roles.contains(&XRDeviceRole::Right) {
has_right = true; has_right = true;
models.push(DeviceRowModelInit::from(dev));
} }
_ => { models.push(row_model);
models.push(DeviceRowModelInit::from(dev));
}
};
} }
if !has_right { if !has_right {
models.push(DeviceRowModelInit::new_missing(XRDeviceRole::Right)); models.push(DeviceRowModelInit::new_missing(XRDeviceRole::Right));
@ -84,13 +77,7 @@ impl SimpleComponent for DevicesBox {
models.push(DeviceRowModelInit::new_missing(XRDeviceRole::Head)); models.push(DeviceRowModelInit::new_missing(XRDeviceRole::Head));
} }
models.sort_by(|m1, m2| { models.sort_by(|m1, m2| m1.sort_index.cmp(&m2.sort_index));
let dt1 =
XRDeviceRole::from_display_str(m1.title.as_deref().unwrap_or_default());
let dt2 =
XRDeviceRole::from_display_str(m2.title.as_deref().unwrap_or_default());
dt1.cmp(&dt2)
});
for model in models { for model in models {
guard.push_back(model); guard.push_back(model);

View file

@ -52,14 +52,29 @@ pub struct DeviceRowModelInit {
pub state: Option<DeviceRowState>, pub state: Option<DeviceRowState>,
pub suffix: Option<gtk::Widget>, pub suffix: Option<gtk::Widget>,
pub battery_status: Option<EnvisionBatteryStatus>, pub battery_status: Option<EnvisionBatteryStatus>,
pub sort_index: u32,
} }
impl From<&XRDevice> for DeviceRowModelInit { impl From<&XRDevice> for DeviceRowModelInit {
fn from(d: &XRDevice) -> Self { fn from(d: &XRDevice) -> Self {
Self { Self {
title: Some(d.dev_type.to_string()), title: Some(if d.roles.is_empty() {
XRDeviceRole::GenericTracker.to_string()
} else {
d.roles
.iter()
.map(XRDeviceRole::to_string)
.collect::<Vec<String>>()
.join("\n")
}),
subtitle: Some(d.name.clone()), subtitle: Some(d.name.clone()),
battery_status: d.battery.map(EnvisionBatteryStatus::from), battery_status: d.battery.map(EnvisionBatteryStatus::from),
sort_index: d
.roles
.iter()
.min()
.unwrap_or(&XRDeviceRole::GenericTracker)
.as_number(),
..Default::default() ..Default::default()
} }
} }

View file

@ -1,7 +1,5 @@
use libmonado_rs::{self, BatteryStatus, DeviceRole}; use libmonado_rs::{self, BatteryStatus, DeviceRole};
use std::{collections::HashSet, fmt::Display, slice::Iter}; use std::{collections::HashMap, fmt::Display, slice::Iter};
const GENERIC_TRACKER_PREFIX: &str = "Found generic tracker device: ";
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum XRDeviceRole { pub enum XRDeviceRole {
@ -132,7 +130,7 @@ impl XRDeviceRole {
Self::Camera => "camera", Self::Camera => "camera",
Self::Keyboard => "keyboard", Self::Keyboard => "keyboard",
Self::GenericTracker => "generic-tracker", // not actually in monado Self::GenericTracker => "generic-tracker",
} }
} }
@ -235,45 +233,40 @@ impl From<DeviceRole> for XRDeviceRole {
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Default)]
pub struct XRDevice { pub struct XRDevice {
pub dev_type: XRDeviceRole, pub roles: Vec<XRDeviceRole>,
pub name: String, pub name: String,
pub index: String, pub id: String,
pub serial: Option<String>, pub serial: Option<String>,
pub battery: Option<BatteryStatus>, pub battery: Option<BatteryStatus>,
} }
impl Default for XRDevice {
fn default() -> Self {
Self {
dev_type: XRDeviceRole::GenericTracker,
name: String::default(),
index: String::default(),
serial: None,
battery: Option::default(),
}
}
}
impl XRDevice { impl XRDevice {
#[deprecated] pub fn from_libmonado(monado: &libmonado_rs::Monado) -> Vec<Self> {
pub fn generic_tracker_from_log_row(s: &str) -> Option<Self> { if let Ok(monado_devs) = monado.devices() {
if s.starts_with(GENERIC_TRACKER_PREFIX) { let mut devs: HashMap<u32, XRDevice> = monado_devs
let n_tracker = s.trim_start_matches(GENERIC_TRACKER_PREFIX); .into_iter()
return Some(Self { .map(|dev| {
dev_type: XRDeviceRole::GenericTracker, (
index: n_tracker.into(), dev.index,
..Default::default() Self {
}); id: dev.id.to_string(),
} serial: dev.serial().ok(),
battery: dev.battery_status().ok().and_then(|bs| {
if bs.present {
Some(bs)
} else {
None None
} }
}),
pub fn from_libmonado(monado: &libmonado_rs::Monado) -> Vec<Self> { name: dev.name,
let mut devs_with_role = HashSet::new(); roles: Vec::default(),
// only roles in src/xrt/targets/libmonado/monado.c:role_enum },
let mut res = [ )
})
.collect();
[
DeviceRole::Head, DeviceRole::Head,
DeviceRole::Eyes, DeviceRole::Eyes,
DeviceRole::Left, DeviceRole::Left,
@ -283,48 +276,21 @@ impl XRDevice {
DeviceRole::HandTrackingRight, DeviceRole::HandTrackingRight,
] ]
.into_iter() .into_iter()
.filter_map(|role| { .for_each(|role| {
if let Ok(dev) = monado.device_from_role(role) { if let Ok(index) = monado.device_index_from_role(role) {
devs_with_role.insert(dev.id); if let Some(target) = devs.get_mut(&index) {
Some(Self { target.roles.push(role.into());
index: dev.id.to_string(),
serial: dev.serial().ok(),
battery: dev.battery_status().ok().and_then(|bs| {
if bs.present {
Some(bs)
} else { } else {
None eprintln!(
"Could not find device index {index} for role {}",
XRDeviceRole::from(role)
)
} }
}), }
name: dev.name, });
dev_type: XRDeviceRole::from(role), devs.drain().map(|(_, d)| d).collect()
})
} else { } else {
None Vec::default()
} }
})
.collect::<Vec<Self>>();
if let Ok(all_devs) = monado.devices() {
res.extend(all_devs.into_iter().filter_map(|dev| {
if devs_with_role.contains(&dev.id) {
None
} else {
Some(Self {
index: dev.id.to_string(),
serial: dev.serial().ok(),
battery: dev.battery_status().ok().and_then(|bs| {
if bs.present {
Some(bs)
} else {
None
}
}),
name: dev.name,
dev_type: XRDeviceRole::GenericTracker,
})
}
}));
}
res
} }
} }