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

View file

@ -52,14 +52,29 @@ pub struct DeviceRowModelInit {
pub state: Option<DeviceRowState>,
pub suffix: Option<gtk::Widget>,
pub battery_status: Option<EnvisionBatteryStatus>,
pub sort_index: u32,
}
impl From<&XRDevice> for DeviceRowModelInit {
fn from(d: &XRDevice) -> 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()),
battery_status: d.battery.map(EnvisionBatteryStatus::from),
sort_index: d
.roles
.iter()
.min()
.unwrap_or(&XRDeviceRole::GenericTracker)
.as_number(),
..Default::default()
}
}

View file

@ -1,7 +1,5 @@
use libmonado_rs::{self, BatteryStatus, DeviceRole};
use std::{collections::HashSet, fmt::Display, slice::Iter};
const GENERIC_TRACKER_PREFIX: &str = "Found generic tracker device: ";
use std::{collections::HashMap, fmt::Display, slice::Iter};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum XRDeviceRole {
@ -132,7 +130,7 @@ impl XRDeviceRole {
Self::Camera => "camera",
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 dev_type: XRDeviceRole,
pub roles: Vec<XRDeviceRole>,
pub name: String,
pub index: String,
pub id: String,
pub serial: Option<String>,
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 {
#[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> {
let mut devs_with_role = HashSet::new();
// only roles in src/xrt/targets/libmonado/monado.c:role_enum
let mut res = [
DeviceRole::Head,
DeviceRole::Eyes,
DeviceRole::Left,
DeviceRole::Right,
DeviceRole::Gamepad,
DeviceRole::HandTrackingLeft,
DeviceRole::HandTrackingRight,
]
.into_iter()
.filter_map(|role| {
if let Ok(dev) = monado.device_from_role(role) {
devs_with_role.insert(dev.id);
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::from(role),
if let Ok(monado_devs) = monado.devices() {
let mut devs: HashMap<u32, XRDevice> = monado_devs
.into_iter()
.map(|dev| {
(
dev.index,
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
}
}),
name: dev.name,
roles: Vec::default(),
},
)
})
} else {
None
}
})
.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,
})
.collect();
[
DeviceRole::Head,
DeviceRole::Eyes,
DeviceRole::Left,
DeviceRole::Right,
DeviceRole::Gamepad,
DeviceRole::HandTrackingLeft,
DeviceRole::HandTrackingRight,
]
.into_iter()
.for_each(|role| {
if let Ok(index) = monado.device_index_from_role(role) {
if let Some(target) = devs.get_mut(&index) {
target.roles.push(role.into());
} else {
eprintln!(
"Could not find device index {index} for role {}",
XRDeviceRole::from(role)
)
}
}
}));
});
devs.drain().map(|(_, d)| d).collect()
} else {
Vec::default()
}
res
}
}