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 => { }
has_left = true; if !has_left && dev.roles.contains(&XRDeviceRole::Left) {
models.push(DeviceRowModelInit::from(dev)); has_left = true;
} }
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,96 +233,64 @@ 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 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: XRDeviceRole::GenericTracker,
index: n_tracker.into(),
..Default::default()
});
}
None
}
pub fn from_libmonado(monado: &libmonado_rs::Monado) -> Vec<Self> { pub fn from_libmonado(monado: &libmonado_rs::Monado) -> Vec<Self> {
let mut devs_with_role = HashSet::new(); if let Ok(monado_devs) = monado.devices() {
// only roles in src/xrt/targets/libmonado/monado.c:role_enum let mut devs: HashMap<u32, XRDevice> = monado_devs
let mut res = [ .into_iter()
DeviceRole::Head, .map(|dev| {
DeviceRole::Eyes, (
DeviceRole::Left, dev.index,
DeviceRole::Right, Self {
DeviceRole::Gamepad, id: dev.id.to_string(),
DeviceRole::HandTrackingLeft, serial: dev.serial().ok(),
DeviceRole::HandTrackingRight, battery: dev.battery_status().ok().and_then(|bs| {
] if bs.present {
.into_iter() Some(bs)
.filter_map(|role| { } else {
if let Ok(dev) = monado.device_from_role(role) { None
devs_with_role.insert(dev.id); }
Some(Self { }),
index: dev.id.to_string(), name: dev.name,
serial: dev.serial().ok(), roles: Vec::default(),
battery: dev.battery_status().ok().and_then(|bs| { },
if bs.present { )
Some(bs)
} else {
None
}
}),
name: dev.name,
dev_type: XRDeviceRole::from(role),
}) })
} else { .collect();
None [
} DeviceRole::Head,
}) DeviceRole::Eyes,
.collect::<Vec<Self>>(); DeviceRole::Left,
if let Ok(all_devs) = monado.devices() { DeviceRole::Right,
res.extend(all_devs.into_iter().filter_map(|dev| { DeviceRole::Gamepad,
if devs_with_role.contains(&dev.id) { DeviceRole::HandTrackingLeft,
None DeviceRole::HandTrackingRight,
} else { ]
Some(Self { .into_iter()
index: dev.id.to_string(), .for_each(|role| {
serial: dev.serial().ok(), if let Ok(index) = monado.device_index_from_role(role) {
battery: dev.battery_status().ok().and_then(|bs| { if let Some(target) = devs.get_mut(&index) {
if bs.present { target.roles.push(role.into());
Some(bs) } else {
} else { eprintln!(
None "Could not find device index {index} for role {}",
} XRDeviceRole::from(role)
}), )
name: dev.name, }
dev_type: XRDeviceRole::GenericTracker,
})
} }
})); });
devs.drain().map(|(_, d)| d).collect()
} else {
Vec::default()
} }
res
} }
} }