azul_layout/managers/
sensors.rs1use alloc::vec::Vec;
19
20use azul_core::dom::DomNodeId;
21use azul_core::events::{
22 EventData, EventProvider, EventSource as CoreEventSource, EventType, SyntheticEvent,
23};
24use azul_core::task::Instant;
25pub use azul_core::sensors::{SensorKind, SensorReading};
26
27#[derive(Copy, Debug, Clone, PartialEq, Default)]
30pub struct SensorManager {
31 pub accelerometer: Option<SensorReading>,
33 pub gyroscope: Option<SensorReading>,
35 pub magnetometer: Option<SensorReading>,
37 pub pending_event: bool,
41 pub has_listeners: bool,
47}
48
49impl SensorManager {
50 #[must_use] pub fn new() -> Self {
51 Self::default()
52 }
53
54 #[must_use] pub const fn reading(&self, kind: SensorKind) -> Option<SensorReading> {
56 match kind {
57 SensorKind::Accelerometer => self.accelerometer,
58 SensorKind::Gyroscope => self.gyroscope,
59 SensorKind::Magnetometer => self.magnetometer,
60 }
61 }
62
63 pub fn set_reading(&mut self, reading: SensorReading) -> bool {
67 let slot = match reading.kind {
68 SensorKind::Accelerometer => &mut self.accelerometer,
69 SensorKind::Gyroscope => &mut self.gyroscope,
70 SensorKind::Magnetometer => &mut self.magnetometer,
71 };
72 let changed = slot.as_mut().is_none_or(|prev| !reading_bitwise_eq(prev, &reading));
73 *slot = Some(reading);
74 if changed {
75 self.pending_event = true;
76 }
77 changed
78 }
79
80 pub const fn clear_pending_event(&mut self) {
83 self.pending_event = false;
84 }
85
86 pub const fn set_has_listeners(&mut self, has: bool) {
88 self.has_listeners = has;
89 }
90
91 #[must_use] pub const fn has_listeners(&self) -> bool {
93 self.has_listeners
94 }
95}
96
97impl EventProvider for SensorManager {
98 fn get_pending_events(&self, timestamp: Instant) -> Vec<SyntheticEvent> {
102 if self.pending_event {
103 alloc::vec![SyntheticEvent::new(
104 EventType::SensorChanged,
105 CoreEventSource::User,
106 DomNodeId::ROOT,
107 timestamp,
108 EventData::None,
109 )]
110 } else {
111 Vec::new()
112 }
113 }
114}
115
116fn reading_bitwise_eq(a: &SensorReading, b: &SensorReading) -> bool {
117 a.kind == b.kind
118 && a.x.to_bits() == b.x.to_bits()
119 && a.y.to_bits() == b.y.to_bits()
120 && a.z.to_bits() == b.z.to_bits()
121 && a.timestamp_ms == b.timestamp_ms
122}
123
124static PENDING_READINGS: std::sync::Mutex<Vec<SensorReading>> =
133 std::sync::Mutex::new(Vec::new());
134
135pub fn push_sensor_reading(reading: SensorReading) {
138 let mut q = PENDING_READINGS.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
139 q.push(reading);
140}
141
142pub fn drain_sensor_readings() -> Vec<SensorReading> {
146 let mut q = PENDING_READINGS.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
147 core::mem::take(&mut *q)
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn listener_flag_gates_polling_decision() {
156 let mut mgr = SensorManager::new();
157 assert!(!mgr.has_listeners(), "no listeners until the relayout walk reports some");
158 mgr.set_has_listeners(true);
159 assert!(mgr.has_listeners());
160 mgr.set_has_listeners(false);
161 assert!(!mgr.has_listeners());
162 }
163
164 fn r(kind: SensorKind, x: f32, y: f32, z: f32) -> SensorReading {
165 SensorReading {
166 kind,
167 x,
168 y,
169 z,
170 timestamp_ms: 0,
171 }
172 }
173
174 #[test]
175 fn manager_defaults_to_no_readings() {
176 let mgr = SensorManager::new();
177 assert_eq!(mgr.reading(SensorKind::Accelerometer), None);
178 assert_eq!(mgr.reading(SensorKind::Gyroscope), None);
179 assert_eq!(mgr.reading(SensorKind::Magnetometer), None);
180 }
181
182 #[test]
183 fn set_reading_routes_by_kind_and_flags_change() {
184 let mut mgr = SensorManager::new();
185 assert!(mgr.set_reading(r(SensorKind::Accelerometer, 0.0, 0.0, 9.81)));
186 assert!(mgr.reading(SensorKind::Accelerometer).is_some());
188 assert_eq!(mgr.reading(SensorKind::Gyroscope), None);
189 assert!(!mgr.set_reading(r(SensorKind::Accelerometer, 0.0, 0.0, 9.81)));
191 assert!(mgr.set_reading(r(SensorKind::Accelerometer, 1.0, 0.0, 9.81)));
193 assert!(mgr.set_reading(r(SensorKind::Gyroscope, 0.1, 0.0, 0.0)));
195 assert_eq!(
196 mgr.reading(SensorKind::Gyroscope).map(|r| r.x),
197 Some(0.1)
198 );
199 }
200
201 #[test]
202 fn magnitude_of_resting_accelerometer() {
203 let g = r(SensorKind::Accelerometer, 0.0, 0.0, 9.81);
204 assert!((g.magnitude() - 9.81).abs() < 1e-4);
205 }
206
207 #[test]
208 fn readings_round_trip_through_manager() {
209 drop(drain_sensor_readings());
210
211 push_sensor_reading(r(SensorKind::Accelerometer, 1.0, 2.0, 3.0));
212 push_sensor_reading(r(SensorKind::Accelerometer, 4.0, 5.0, 6.0)); push_sensor_reading(r(SensorKind::Magnetometer, 20.0, 0.0, 40.0));
214 let drained = drain_sensor_readings();
215 assert_eq!(drained.len(), 3, "all parked readings drain in order");
216
217 let mut mgr = SensorManager::new();
218 for reading in &drained {
219 mgr.set_reading(*reading);
220 }
221 assert_eq!(
222 mgr.reading(SensorKind::Accelerometer).map(|r| r.x),
223 Some(4.0),
224 "the last accelerometer reading wins"
225 );
226 assert_eq!(
227 mgr.reading(SensorKind::Magnetometer).map(|r| r.z),
228 Some(40.0)
229 );
230
231 assert!(drain_sensor_readings().is_empty());
232 }
233}