Skip to main content

repose_platform/
sensor.rs

1//! Motion-sensor hardware backends (gyroscope / accelerometer).
2//!
3//! Separate from [`crate::gamepad`] on purpose: a sensor driver reads devices
4//! only for their IMU, leaving buttons, axes and rumble to the gamepad
5//! backend. Keeping the traits apart means no driver has to claim both.
6//!
7//! Linux reads `/dev/input/event*` directly (`evdev`, `sensors` feature),
8//! because motion never reaches gilrs. The input protocol gives it a precise
9//! shape: a node flagged `INPUT_PROP_ACCELEROMETER` reports accelerometer data
10//! on its directional axes (`ABS_X/Y/Z`) and gyroscope data on its rotational
11//! axes (`ABS_RX/RY/RZ`), and must not mix them with regular stick axes. Each
12//! axis `AbsInfo` resolution is counts per unit, so `value / resolution` is g
13//! and degrees per second.
14//!
15//! Every mainline driver (hid-steam, hid-playstation, hid-nintendo,
16//! hid-sony) puts motion on its own node named after the pad, so readings
17//! carry the pad's name for the runner to resolve to a
18//! [`GamepadId`](repose_core::input::GamepadId).
19
20use repose_core::input::SensorSample;
21
22#[cfg(all(feature = "sensors", target_os = "linux"))]
23mod evdev;
24#[cfg(all(feature = "sensors", target_os = "linux"))]
25pub use evdev::{EvdevSensorBackend, MotionScale};
26
27/// One reading tagged with the hardware device that produced it. The runner
28/// resolves `device` against the connected pads' names, so backends never
29/// assign pad ids themselves.
30#[derive(Clone, Debug, PartialEq)]
31pub struct SensorReading {
32    pub device: String,
33    pub sample: SensorSample,
34}
35
36/// Hardware poller for motion sensors. Default: no sensors.
37pub trait SensorBackend {
38    fn poll_sensors(&mut self) -> Vec<SensorReading> {
39        Vec::new()
40    }
41}
42
43/// Placeholder backend for targets without a motion driver yet.
44pub struct NoSensorBackend;
45
46impl SensorBackend for NoSensorBackend {}
47
48/// Create the platform sensor backend, or `None` when `sensors` is off or the
49/// target has no motion driver yet.
50pub fn create_backend() -> Option<impl SensorBackend> {
51    #[cfg(all(feature = "sensors", target_os = "linux"))]
52    {
53        Some(EvdevSensorBackend::new())
54    }
55    #[cfg(any(not(feature = "sensors"), not(target_os = "linux")))]
56    {
57        None::<NoSensorBackend>
58    }
59}