Skip to content

Commit 6a31d31

Browse files
authored
Merge pull request #1131 from Yamakaky/sensor-api
Add sensor API implementation
2 parents 7a821ac + c261a0d commit 6a31d31

File tree

8 files changed

+409
-7
lines changed

8 files changed

+409
-7
lines changed

.github/workflows/CI.yml

+4-6
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,7 @@ jobs:
5858
5959
build-linux:
6060
name: build linux pkg-config
61-
runs-on: ubuntu-20.04
62-
strategy:
63-
fail-fast: false
61+
runs-on: ubuntu-latest
6462
steps:
6563
- uses: actions/checkout@v2
6664
- name: Install dependencies
@@ -76,6 +74,6 @@ jobs:
7674
set -xeuo pipefail
7775
rustc --version
7876
cargo --version
79-
cargo build --features "${CI_BUILD_FEATURES}"
80-
cargo build --examples --features "${CI_BUILD_FEATURES}"
81-
cargo test --features "${CI_BUILD_FEATURES}"
77+
# SDL 2.0.10 so no hidapi
78+
cargo build --no-default-features --features "${CI_BUILD_FEATURES}"
79+
cargo test --no-default-features --features "${CI_BUILD_FEATURES}"

Cargo.toml

+7-1
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,13 @@ optional = true
4141

4242
[features]
4343
unsafe_textures = []
44-
default = []
44+
#default = ["hidapi"]
4545
gfx = ["c_vec", "sdl2-sys/gfx"]
4646
mixer = ["sdl2-sys/mixer"]
4747
image = ["sdl2-sys/image"]
4848
ttf = ["sdl2-sys/ttf"]
49+
# Use hidapi support in SDL. Only 2.0.12 and after
50+
hidapi = []
4951

5052
use-bindgen = ["sdl2-sys/use-bindgen"]
5153
use-pkgconfig = ["sdl2-sys/use-pkgconfig"]
@@ -141,6 +143,10 @@ name = "renderer-yuv"
141143
required-features = ["ttf", "image"]
142144
name = "resource-manager"
143145

146+
[[example]]
147+
required-features = ["hidapi"]
148+
name = "sensors"
149+
144150
[[example]]
145151
required-features = ["ttf"]
146152
name = "ttf-demo"

examples/sensors.rs

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use std::time::{Duration, Instant};
2+
3+
use sdl2::{event::Event, sensor::SensorType};
4+
5+
extern crate sdl2;
6+
7+
fn main() -> Result<(), String> {
8+
let sdl_context = sdl2::init()?;
9+
let game_controller_subsystem = sdl_context.game_controller()?;
10+
11+
let available = game_controller_subsystem
12+
.num_joysticks()
13+
.map_err(|e| format!("can't enumerate joysticks: {}", e))?;
14+
15+
println!("{} joysticks available", available);
16+
17+
// Iterate over all available joysticks and look for game controllers.
18+
let controller = (0..available)
19+
.find_map(|id| {
20+
if !game_controller_subsystem.is_game_controller(id) {
21+
println!("{} is not a game controller", id);
22+
return None;
23+
}
24+
25+
println!("Attempting to open controller {}", id);
26+
27+
match game_controller_subsystem.open(id) {
28+
Ok(c) => {
29+
// We managed to find and open a game controller,
30+
// exit the loop
31+
println!("Success: opened \"{}\"", c.name());
32+
Some(c)
33+
}
34+
Err(e) => {
35+
println!("failed: {:?}", e);
36+
None
37+
}
38+
}
39+
})
40+
.expect("Couldn't open any controller");
41+
42+
if !controller.has_sensor(SensorType::Accelerometer) {
43+
return Err(format!(
44+
"{} doesn't support the accelerometer",
45+
controller.name()
46+
));
47+
}
48+
if !controller.has_sensor(SensorType::Gyroscope) {
49+
return Err(format!(
50+
"{} doesn't support the gyroscope",
51+
controller.name()
52+
));
53+
}
54+
55+
controller
56+
.sensor_set_enabled(SensorType::Accelerometer, true)
57+
.map_err(|e| format!("error enabling accelerometer: {}", e))?;
58+
controller
59+
.sensor_set_enabled(SensorType::Gyroscope, true)
60+
.map_err(|e| format!("error enabling gyroscope: {}", e))?;
61+
let mut now = Instant::now();
62+
for event in sdl_context.event_pump()?.wait_iter() {
63+
if false && now.elapsed() > Duration::from_secs(1) {
64+
now = Instant::now();
65+
66+
let mut gyro_data = [0f32; 3];
67+
let mut accel_data = [0f32; 3];
68+
69+
controller
70+
.sensor_get_data(SensorType::Gyroscope, &mut gyro_data)
71+
.map_err(|e| format!("error getting gyro data: {}", e))?;
72+
controller
73+
.sensor_get_data(SensorType::Accelerometer, &mut accel_data)
74+
.map_err(|e| format!("error getting accel data: {}", e))?;
75+
76+
println!("gyro: {:?}, accel: {:?}", gyro_data, accel_data);
77+
}
78+
79+
if let Event::ControllerSensorUpdated { .. } = event {
80+
println!("{:?}", event);
81+
}
82+
83+
if let Event::Quit { .. } = event {
84+
break;
85+
}
86+
}
87+
88+
Ok(())
89+
}

src/sdl2/controller.rs

+80
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ use std::fmt;
66
use std::io;
77
use std::path::Path;
88

9+
#[cfg(feature = "hidapi")]
10+
use crate::sensor::SensorType;
11+
#[cfg(feature = "hidapi")]
12+
use std::convert::TryInto;
13+
914
use crate::common::{validate_int, IntegerOrSdlError};
1015
use crate::get_error;
1116
use crate::joystick;
@@ -504,6 +509,81 @@ impl GameController {
504509
}
505510
}
506511

512+
#[cfg(feature = "hidapi")]
513+
impl GameController {
514+
#[doc(alias = "SDL_GameControllerHasSensor")]
515+
pub fn has_sensor(&self, sensor_type: crate::sensor::SensorType) -> bool {
516+
let result = unsafe { sys::SDL_GameControllerHasSensor(self.raw, sensor_type.into()) };
517+
518+
match result {
519+
sys::SDL_bool::SDL_FALSE => false,
520+
sys::SDL_bool::SDL_TRUE => true,
521+
}
522+
}
523+
524+
#[doc(alias = "SDL_GameControllerIsSensorEnabled")]
525+
pub fn sensor_enabled(&self, sensor_type: crate::sensor::SensorType) -> bool {
526+
let result =
527+
unsafe { sys::SDL_GameControllerIsSensorEnabled(self.raw, sensor_type.into()) };
528+
529+
match result {
530+
sys::SDL_bool::SDL_FALSE => false,
531+
sys::SDL_bool::SDL_TRUE => true,
532+
}
533+
}
534+
535+
#[doc(alias = "SDL_GameControllerHasSensor")]
536+
pub fn sensor_set_enabled(
537+
&self,
538+
sensor_type: crate::sensor::SensorType,
539+
enabled: bool,
540+
) -> Result<(), IntegerOrSdlError> {
541+
let result = unsafe {
542+
sys::SDL_GameControllerSetSensorEnabled(
543+
self.raw,
544+
sensor_type.into(),
545+
if enabled {
546+
sys::SDL_bool::SDL_TRUE
547+
} else {
548+
sys::SDL_bool::SDL_FALSE
549+
},
550+
)
551+
};
552+
553+
if result != 0 {
554+
Err(IntegerOrSdlError::SdlError(get_error()))
555+
} else {
556+
Ok(())
557+
}
558+
}
559+
560+
/// Get data from a sensor.
561+
///
562+
/// The number of data points depends on the sensor. Both Gyroscope and
563+
/// Accelerometer return 3 values, one for each axis.
564+
#[doc(alias = "SDL_GameControllerGetSensorData")]
565+
pub fn sensor_get_data(
566+
&self,
567+
sensor_type: SensorType,
568+
data: &mut [f32],
569+
) -> Result<(), IntegerOrSdlError> {
570+
let result = unsafe {
571+
sys::SDL_GameControllerGetSensorData(
572+
self.raw,
573+
sensor_type.into(),
574+
data.as_mut_ptr(),
575+
data.len().try_into().unwrap(),
576+
)
577+
};
578+
579+
if result != 0 {
580+
Err(IntegerOrSdlError::SdlError(get_error()))
581+
} else {
582+
Ok(())
583+
}
584+
}
585+
}
586+
507587
impl Drop for GameController {
508588
#[doc(alias = "SDL_GameControllerClose")]
509589
fn drop(&mut self) {

src/sdl2/event.rs

+30
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,8 @@ pub enum EventType {
298298
ControllerDeviceAdded = SDL_EventType::SDL_CONTROLLERDEVICEADDED as u32,
299299
ControllerDeviceRemoved = SDL_EventType::SDL_CONTROLLERDEVICEREMOVED as u32,
300300
ControllerDeviceRemapped = SDL_EventType::SDL_CONTROLLERDEVICEREMAPPED as u32,
301+
#[cfg(feature = "hidapi")]
302+
ControllerSensorUpdated = SDL_EventType::SDL_CONTROLLERSENSORUPDATE as u32,
301303

302304
FingerDown = SDL_EventType::SDL_FINGERDOWN as u32,
303305
FingerUp = SDL_EventType::SDL_FINGERUP as u32,
@@ -366,6 +368,8 @@ impl TryFrom<u32> for EventType {
366368
SDL_CONTROLLERDEVICEADDED => ControllerDeviceAdded,
367369
SDL_CONTROLLERDEVICEREMOVED => ControllerDeviceRemoved,
368370
SDL_CONTROLLERDEVICEREMAPPED => ControllerDeviceRemapped,
371+
#[cfg(feature = "hidapi")]
372+
SDL_CONTROLLERSENSORUPDATE => ControllerSensorUpdated,
369373

370374
SDL_FINGERDOWN => FingerDown,
371375
SDL_FINGERUP => FingerUp,
@@ -674,6 +678,18 @@ pub enum Event {
674678
which: u32,
675679
},
676680

681+
/// Triggered when the gyroscope or accelerometer is updated
682+
#[cfg(feature = "hidapi")]
683+
ControllerSensorUpdated {
684+
timestamp: u32,
685+
which: u32,
686+
sensor: crate::sensor::SensorType,
687+
/// Data from the sensor.
688+
///
689+
/// See the `sensor` module for more information.
690+
data: [f32; 3],
691+
},
692+
677693
FingerDown {
678694
timestamp: u32,
679695
touch_id: i64,
@@ -1612,6 +1628,16 @@ impl Event {
16121628
which: event.which as u32,
16131629
}
16141630
}
1631+
#[cfg(feature = "hidapi")]
1632+
EventType::ControllerSensorUpdated => {
1633+
let event = raw.csensor;
1634+
Event::ControllerSensorUpdated {
1635+
timestamp: event.timestamp,
1636+
which: event.which as u32,
1637+
sensor: crate::sensor::SensorType::from_ll(event.sensor),
1638+
data: event.data,
1639+
}
1640+
}
16151641

16161642
EventType::FingerDown => {
16171643
let event = raw.tfinger;
@@ -1898,6 +1924,8 @@ impl Event {
18981924
| (Self::RenderDeviceReset { .. }, Self::RenderDeviceReset { .. })
18991925
| (Self::User { .. }, Self::User { .. })
19001926
| (Self::Unknown { .. }, Self::Unknown { .. }) => true,
1927+
#[cfg(feature = "hidapi")]
1928+
(Self::ControllerSensorUpdated { .. }, Self::ControllerSensorUpdated { .. }) => true,
19011929
_ => false,
19021930
}
19031931
}
@@ -1947,6 +1975,8 @@ impl Event {
19471975
Self::ControllerDeviceAdded { timestamp, .. } => timestamp,
19481976
Self::ControllerDeviceRemoved { timestamp, .. } => timestamp,
19491977
Self::ControllerDeviceRemapped { timestamp, .. } => timestamp,
1978+
#[cfg(feature = "hidapi")]
1979+
Self::ControllerSensorUpdated { timestamp, .. } => timestamp,
19501980
Self::FingerDown { timestamp, .. } => timestamp,
19511981
Self::FingerUp { timestamp, .. } => timestamp,
19521982
Self::FingerMotion { timestamp, .. } => timestamp,

src/sdl2/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ pub mod rect;
8383
pub mod render;
8484
pub mod rwops;
8585
mod sdl;
86+
#[cfg(feature = "hidapi")]
87+
pub mod sensor;
8688
pub mod surface;
8789
pub mod timer;
8890
pub mod touch;

src/sdl2/sdl.rs

+7
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ impl Sdl {
121121
GameControllerSubsystem::new(self)
122122
}
123123

124+
/// Initializes the game controller subsystem.
125+
#[inline]
126+
pub fn sensor(&self) -> Result<SensorSubsystem, String> {
127+
SensorSubsystem::new(self)
128+
}
129+
124130
/// Initializes the timer subsystem.
125131
#[inline]
126132
pub fn timer(&self) -> Result<TimerSubsystem, String> {
@@ -278,6 +284,7 @@ subsystem!(VideoSubsystem, sys::SDL_INIT_VIDEO, nosync);
278284
subsystem!(TimerSubsystem, sys::SDL_INIT_TIMER, sync);
279285
// The event queue can be read from other threads.
280286
subsystem!(EventSubsystem, sys::SDL_INIT_EVENTS, sync);
287+
subsystem!(SensorSubsystem, sys::SDL_INIT_SENSOR, sync);
281288

282289
static mut IS_EVENT_PUMP_ALIVE: bool = false;
283290

0 commit comments

Comments
 (0)