use alloc::vec::Vec;
use azul_core::dom::DomNodeId;
use azul_core::events::{
EventData, EventProvider, EventSource as CoreEventSource, EventType, SyntheticEvent,
};
use azul_core::task::Instant;
pub use azul_core::sensors::{SensorKind, SensorReading};
#[derive(Debug, Clone, PartialEq, Default)]
pub struct SensorManager {
pub accelerometer: Option<SensorReading>,
pub gyroscope: Option<SensorReading>,
pub magnetometer: Option<SensorReading>,
pub pending_event: bool,
}
impl SensorManager {
pub fn new() -> Self {
Self::default()
}
pub fn reading(&self, kind: SensorKind) -> Option<SensorReading> {
match kind {
SensorKind::Accelerometer => self.accelerometer,
SensorKind::Gyroscope => self.gyroscope,
SensorKind::Magnetometer => self.magnetometer,
}
}
pub fn set_reading(&mut self, reading: SensorReading) -> bool {
let slot = match reading.kind {
SensorKind::Accelerometer => &mut self.accelerometer,
SensorKind::Gyroscope => &mut self.gyroscope,
SensorKind::Magnetometer => &mut self.magnetometer,
};
let changed = match slot {
Some(prev) => !reading_bitwise_eq(prev, &reading),
None => true,
};
*slot = Some(reading);
if changed {
self.pending_event = true;
}
changed
}
pub fn clear_pending_event(&mut self) {
self.pending_event = false;
}
}
impl EventProvider for SensorManager {
fn get_pending_events(&self, timestamp: Instant) -> Vec<SyntheticEvent> {
if self.pending_event {
alloc::vec![SyntheticEvent::new(
EventType::SensorChanged,
CoreEventSource::User,
DomNodeId::ROOT,
timestamp,
EventData::None,
)]
} else {
Vec::new()
}
}
}
fn reading_bitwise_eq(a: &SensorReading, b: &SensorReading) -> bool {
a.kind == b.kind
&& a.x.to_bits() == b.x.to_bits()
&& a.y.to_bits() == b.y.to_bits()
&& a.z.to_bits() == b.z.to_bits()
&& a.timestamp_ms == b.timestamp_ms
}
static PENDING_READINGS: std::sync::Mutex<Vec<SensorReading>> =
std::sync::Mutex::new(Vec::new());
pub fn push_sensor_reading(reading: SensorReading) {
let mut q = PENDING_READINGS.lock().unwrap_or_else(|e| e.into_inner());
q.push(reading);
}
pub fn drain_sensor_readings() -> Vec<SensorReading> {
let mut q = PENDING_READINGS.lock().unwrap_or_else(|e| e.into_inner());
core::mem::take(&mut *q)
}
#[cfg(test)]
mod tests {
use super::*;
fn r(kind: SensorKind, x: f32, y: f32, z: f32) -> SensorReading {
SensorReading {
kind,
x,
y,
z,
timestamp_ms: 0,
}
}
#[test]
fn manager_defaults_to_no_readings() {
let mgr = SensorManager::new();
assert_eq!(mgr.reading(SensorKind::Accelerometer), None);
assert_eq!(mgr.reading(SensorKind::Gyroscope), None);
assert_eq!(mgr.reading(SensorKind::Magnetometer), None);
}
#[test]
fn set_reading_routes_by_kind_and_flags_change() {
let mut mgr = SensorManager::new();
assert!(mgr.set_reading(r(SensorKind::Accelerometer, 0.0, 0.0, 9.81)));
assert!(mgr.reading(SensorKind::Accelerometer).is_some());
assert_eq!(mgr.reading(SensorKind::Gyroscope), None);
assert!(!mgr.set_reading(r(SensorKind::Accelerometer, 0.0, 0.0, 9.81)));
assert!(mgr.set_reading(r(SensorKind::Accelerometer, 1.0, 0.0, 9.81)));
assert!(mgr.set_reading(r(SensorKind::Gyroscope, 0.1, 0.0, 0.0)));
assert_eq!(
mgr.reading(SensorKind::Gyroscope).map(|r| r.x),
Some(0.1)
);
}
#[test]
fn magnitude_of_resting_accelerometer() {
let g = r(SensorKind::Accelerometer, 0.0, 0.0, 9.81);
assert!((g.magnitude() - 9.81).abs() < 1e-4);
}
#[test]
fn readings_round_trip_through_manager() {
let _ = drain_sensor_readings();
push_sensor_reading(r(SensorKind::Accelerometer, 1.0, 2.0, 3.0));
push_sensor_reading(r(SensorKind::Accelerometer, 4.0, 5.0, 6.0)); push_sensor_reading(r(SensorKind::Magnetometer, 20.0, 0.0, 40.0));
let drained = drain_sensor_readings();
assert_eq!(drained.len(), 3, "all parked readings drain in order");
let mut mgr = SensorManager::new();
for reading in &drained {
mgr.set_reading(*reading);
}
assert_eq!(
mgr.reading(SensorKind::Accelerometer).map(|r| r.x),
Some(4.0),
"the last accelerometer reading wins"
);
assert_eq!(
mgr.reading(SensorKind::Magnetometer).map(|r| r.z),
Some(40.0)
);
assert!(drain_sensor_readings().is_empty());
}
}