Skip to main content

dualsense_tools/virtual_controller/
controller.rs

1use crate::virtual_controller::*;
2use crate::{DualsenseStatesBuffer, Tilt, TiltEstimator, state::DualsenseState};
3
4/// A controller that represents all the available inputs of a Dualsense device
5pub struct VirtualController<const N: usize> {
6    states_buffer: DualsenseStatesBuffer<N>,
7    tilt_estimator: TiltEstimator<N>,
8    tilt: Tilt,
9    tilt_enabled: bool,
10}
11
12impl<const N: usize> VirtualController<N> {
13    /// Creates a new controller using the provided estimator for mapping roll/pitch
14    pub fn new(tilt_estimator: TiltEstimator<N>) -> VirtualController<N> {
15        VirtualController {
16            states_buffer: DualsenseStatesBuffer::default(),
17            tilt_estimator,
18            tilt: Tilt::default(),
19            tilt_enabled: true,
20        }
21    }
22
23    /// Enables/disables tilt tracking
24    pub fn toggle_tilt_enabled(&mut self) {
25        self.tilt_enabled = !self.tilt_enabled
26    }
27
28    /// Updates the virtual controller state using the given Dualsense readings
29    pub fn handle_dualsense(&mut self, ds_state: DualsenseState) -> VirtualControllerState {
30        let state_event = self.states_buffer.push(ds_state);
31
32        let throttle: u8 = (((ds_state.axes.rz.as_u8() as i16) - (ds_state.axes.z.as_u8() as i16)
33            + (u8::MAX as i16))
34            / 2) as u8;
35
36        let pitch;
37        let roll;
38        if self.tilt_enabled {
39            self.tilt = self
40                .tilt_estimator
41                .next_estimate(state_event)
42                .accel_corrected_gyro;
43
44            pitch = self.tilt.get_pitch_radians().into();
45            roll = self.tilt.get_roll_radians().into();
46        } else {
47            pitch = AxisValue::default();
48            roll = AxisValue::default();
49        }
50
51        VirtualControllerState {
52            axes: Axes {
53                x: ds_state.axes.x.into(),
54                y: ds_state.axes.y.into(),
55                z: ds_state.axes.z.into(),
56                rx: ds_state.axes.rx.into(),
57                ry: ds_state.axes.ry.into(),
58                rz: ds_state.axes.rz.into(),
59                throttle: throttle.into(),
60                pitch,
61                roll,
62            },
63            hat: ds_state.hat.into(),
64            buttons: Buttons {
65                square: ds_state.square,
66                triangle: ds_state.triangle,
67                circle: ds_state.circle,
68                cross: ds_state.cross,
69                l1: ds_state.l1,
70                r1: ds_state.r1,
71                l2: ds_state.l2,
72                r2: ds_state.r2,
73                l3: ds_state.l3,
74                r3: ds_state.r3,
75                option: ds_state.option,
76                share: ds_state.share,
77                touch_click: ds_state.touch_click,
78                ps: ds_state.ps,
79                mic: ds_state.mic,
80            },
81            is_tilt_enabled: self.tilt_enabled,
82        }
83    }
84}