dualsense_tools_bevy/
lib.rs1use std::sync::{Arc, Mutex};
2
3use bevy::{
4 input::gamepad::{GamepadConnection, GamepadConnectionEvent},
5 log,
6 prelude::*,
7};
8use dualsense_tools::*;
9use hidapi::HidApi;
10
11#[derive(Default, Debug)]
14pub struct DualsenseTiltPlugin<const SAMPLES: usize> {
15 estimator_config: TiltEstimatorConfig<SAMPLES>,
16}
17
18impl<const SAMPLES: usize> DualsenseTiltPlugin<SAMPLES> {
19 pub fn new(estimator_config: TiltEstimatorConfig<SAMPLES>) -> DualsenseTiltPlugin<SAMPLES> {
20 DualsenseTiltPlugin { estimator_config }
21 }
22}
23
24impl<const SAMPLES: usize> Plugin for DualsenseTiltPlugin<SAMPLES> {
25 fn build(&self, app: &mut bevy::app::App) {
26 app.insert_resource(DualsenseTilt::default())
27 .insert_resource(DualsenseResource::default())
28 .insert_resource(TiltEstimatorResource::new(self.estimator_config))
29 .add_systems(Update, handle_connection)
30 .add_systems(
31 Update,
32 update_tilt_tilt_system::<SAMPLES>.pipe(handle_results),
33 );
34 }
35}
36
37#[derive(Resource, Default, Clone, Copy, Debug)]
40pub struct DualsenseTilt(TiltEstimates);
41
42impl DualsenseTilt {
43 pub fn estimates(&self) -> TiltEstimates {
44 self.0
45 }
46}
47
48#[derive(Resource, Clone, Debug, Default)]
49struct DualsenseResource {
50 dualsense: Option<Arc<Mutex<Dualsense>>>,
51}
52
53#[derive(Resource, Clone, Debug)]
54struct TiltEstimatorResource<const SAMPLES: usize> {
55 state_buffer: DualsenseStatesBuffer<SAMPLES>,
56 tilt_estimator: TiltEstimator<SAMPLES>,
57}
58
59impl<const SAMPLES: usize> TiltEstimatorResource<SAMPLES> {
60 fn new(config: TiltEstimatorConfig<SAMPLES>) -> TiltEstimatorResource<SAMPLES> {
61 TiltEstimatorResource {
62 state_buffer: default(),
63 tilt_estimator: TiltEstimator::new(config),
64 }
65 }
66}
67
68fn update_tilt_tilt_system<const SAMPLES: usize>(
69 controller_res: Res<DualsenseResource>,
70 tilt: ResMut<DualsenseTilt>,
71 estimator_res: ResMut<TiltEstimatorResource<SAMPLES>>,
72) -> Result<(), BevyError> {
73 tilt.into_inner().0 = if let Some(controller) = &controller_res.into_inner().dualsense {
74 let estimator = estimator_res.into_inner();
75 let state = controller.lock().unwrap().read()?;
76 let event = estimator.state_buffer.push(state);
77
78 estimator.tilt_estimator.next_estimate(event)
79 } else {
80 TiltEstimates::default()
81 };
82
83 Ok(())
84}
85
86fn handle_connection(
87 mut connection_events: MessageReader<GamepadConnectionEvent>,
88 controller_res: ResMut<DualsenseResource>,
89) -> Result<(), BevyError> {
90 for ev in connection_events.read() {
91 match &ev.connection {
92 GamepadConnection::Connected {
93 name: _,
94 vendor_id,
95 product_id,
96 } => {
97 if *vendor_id == Some(VENDOR_ID) && *product_id == Some(PRODUCT_ID) {
98 log::info!("Connecting Dualsense Controller");
99 let mut hidapi = HidApi::new()?;
100 let dualsense = Dualsense::new(&mut hidapi)?;
101 controller_res.into_inner().dualsense = Some(Arc::new(Mutex::new(dualsense)));
102 break;
103 }
104 }
105
106 GamepadConnection::Disconnected => (),
107 }
108 }
109
110 Ok(())
111}
112
113fn handle_results(r: In<Result<(), BevyError>>, controller_res: ResMut<DualsenseResource>) {
114 match r.0 {
115 Ok(()) => (),
116 Err(err) => {
117 bevy::log::error!(
118 "Error in the dualsense-tilt plugin, disconnecting controller: {}",
119 err
120 );
121 controller_res.into_inner().dualsense = None;
122 }
123 }
124}