rich_sdl2_rust/
haptic.rs

1//! Haptic devices, which give the players to feedback by some force.
2
3use bitflags::bitflags;
4use std::{ffi::CStr, marker::PhantomData, os::raw::c_int, ptr::NonNull};
5
6use crate::{bind, event::joystick::Joystick, Result, Sdl, SdlError};
7
8pub mod direction;
9pub mod effect;
10mod joystick;
11mod mouse;
12mod playing;
13pub mod rumble;
14
15pub use joystick::*;
16pub use mouse::*;
17pub use playing::*;
18
19use self::effect::HapticEffect;
20
21bitflags! {
22    /// A property of the haptic device.
23    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
24    pub struct HapticProperty: u32 {
25        /// Supported a constant effect.
26        const CONSTANT = 1 << 0;
27        /// Supported a sine wave periodic effect.
28        const SINE = 1 << 1;
29        /// Supported a left/right effect.
30        const LEFT_RIGHT = 1 << 2;
31        /// Supported a triangle wave periodic effect.
32        const TRIANGLE = 1 << 3;
33        /// Supported an upwards sawtooth wave periodic effect.
34        const SAW_TOOTH_UP = 1 << 4;
35        /// Supported a downwards sawtooth wave periodic effect.
36        const SAW_TOOTH_DOWN = 1 << 5;
37        /// Supported a ramp effect.
38        const RAMP = 1 << 6;
39        /// Supported a custom effect.
40        const CUSTOM = 1 << 11;
41        /// Supported setting the global gain.
42        const GAIN = 1 << 12;
43        /// Supported setting auto-center.
44        const AUTO_CENTER = 1 << 13;
45        /// Supported querying the status of the effect.
46        const STATUS = 1 << 14;
47        /// Supported pausing the effect.
48        const PAUSE = 1 << 15;
49    }
50}
51
52/// A haptic device.
53pub struct Haptic {
54    ptr: NonNull<bind::SDL_Haptic>,
55}
56
57impl std::fmt::Debug for Haptic {
58    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59        f.debug_struct("Haptic")
60            .field("name", &self.name())
61            .finish_non_exhaustive()
62    }
63}
64
65impl Haptic {
66    /// Returns the name of the haptic device.
67    #[must_use]
68    pub fn name(&self) -> String {
69        let index = unsafe { bind::SDL_HapticIndex(self.ptr.as_ptr()) };
70        let cstr = unsafe { CStr::from_ptr(bind::SDL_HapticName(index)) };
71        cstr.to_string_lossy().to_string()
72    }
73
74    /// Returns the numbers of the axes on the haptic device.
75    #[must_use]
76    pub fn num_axes(&self) -> u32 {
77        unsafe { bind::SDL_HapticNumAxes(self.ptr.as_ptr()) as u32 }
78    }
79
80    /// Returns whether the effect is supported on the haptic device.
81    #[must_use]
82    pub fn is_effect_supported(&self, effect: &HapticEffect) -> bool {
83        let mut raw = effect.clone().into_raw();
84        unsafe {
85            bind::SDL_HapticEffectSupported(self.ptr.as_ptr(), &mut raw) as bind::SDL_bool
86                == bind::SDL_TRUE
87        }
88    }
89
90    /// Constructs the [`PendingEffect`] from the effect specification.
91    ///
92    /// # Errors
93    ///
94    /// Returns `Err` if this feature is unsupported, or failed to create a new haptic effect on the device.
95    pub fn new_effect(&self, effect: &HapticEffect) -> Result<PendingEffect> {
96        if !self.is_effect_supported(effect) {
97            return Err(SdlError::UnsupportedFeature);
98        }
99        let mut raw = effect.clone().into_raw();
100        let ret = unsafe { bind::SDL_HapticNewEffect(self.ptr.as_ptr(), &mut raw) };
101        if ret < 0 {
102            Err(SdlError::Others { msg: Sdl::error() })
103        } else {
104            Ok(PendingEffect::new(ret, self))
105        }
106    }
107
108    /// Returns the capacity of the effects on the haptic device.
109    #[must_use]
110    pub fn effects_creation_capacity(&self) -> usize {
111        unsafe { bind::SDL_HapticNumEffects(self.ptr.as_ptr()) as usize }
112    }
113
114    /// Returns the maximum numbers of playing the effects at same time on the haptic device.
115    #[must_use]
116    pub fn effects_playing_capacity(&self) -> usize {
117        unsafe { bind::SDL_HapticNumEffectsPlaying(self.ptr.as_ptr()) as usize }
118    }
119
120    /// Stops all the playing effect.
121    pub fn stop_all_effect(&self) {
122        unsafe {
123            bind::SDL_HapticStopAll(self.ptr.as_ptr());
124        }
125    }
126
127    /// Sets the global gain. If not supported, this has no effects.
128    pub fn set_gain(&self, gain: u32) {
129        if !self.property().contains(HapticProperty::GAIN) {
130            return;
131        }
132        let ret = unsafe { bind::SDL_HapticSetGain(self.ptr.as_ptr(), gain.min(100) as c_int) };
133        if ret < 0 {
134            eprintln!("{}", Sdl::error());
135        }
136    }
137
138    /// Sets auto-center. If not supported, this has no effects.
139    pub fn set_auto_center(&self, auto_center: u32) {
140        if !self.property().contains(HapticProperty::AUTO_CENTER) {
141            return;
142        }
143        let ret = unsafe {
144            bind::SDL_HapticSetAutocenter(self.ptr.as_ptr(), auto_center.min(100) as c_int)
145        };
146        if ret < 0 {
147            eprintln!("{}", Sdl::error());
148        }
149    }
150
151    /// Queries a property on the haptic device.
152    #[must_use]
153    pub fn property(&self) -> HapticProperty {
154        let bits = unsafe { bind::SDL_HapticQuery(self.ptr.as_ptr()) };
155        HapticProperty::from_bits(bits).unwrap()
156    }
157
158    /// Pauses the haptic device and converts into [`PausedHaptic`].
159    #[must_use]
160    pub fn pause(self) -> PausedHaptic {
161        unsafe {
162            bind::SDL_HapticPause(self.ptr.as_ptr());
163        }
164        PausedHaptic { haptic: self }
165    }
166}
167
168/// A haptic device but frozen not to interact.
169pub struct PausedHaptic {
170    haptic: Haptic,
171}
172
173impl PausedHaptic {
174    /// Unpauses the haptic device and converts into [`Haptic`].
175    #[must_use]
176    pub fn unpause(self) -> Haptic {
177        unsafe {
178            bind::SDL_HapticUnpause(self.haptic.ptr.as_ptr());
179        }
180        self.haptic
181    }
182}
183
184/// All of recognized haptic devices at initialized.
185pub struct HapticSet(Vec<Haptic>);
186
187impl HapticSet {
188    /// Constructs and initializes the system and recognizes haptic devices.
189    #[must_use]
190    pub fn new() -> Self {
191        let num_haptics = unsafe {
192            bind::SDL_InitSubSystem(bind::SDL_INIT_HAPTIC);
193            bind::SDL_NumHaptics()
194        };
195        Self(
196            (0..num_haptics)
197                .filter_map(|index| {
198                    let ptr = unsafe { bind::SDL_HapticOpen(index) };
199                    NonNull::new(ptr).map(|ptr| Haptic { ptr })
200                })
201                .collect(),
202        )
203    }
204
205    /// Returns the haptic devices.
206    #[must_use]
207    pub fn haptics(&self) -> &[Haptic] {
208        &self.0
209    }
210}
211
212impl Default for HapticSet {
213    fn default() -> Self {
214        Self::new()
215    }
216}
217
218impl Drop for HapticSet {
219    fn drop(&mut self) {
220        for haptic in &self.0 {
221            unsafe { bind::SDL_HapticClose(haptic.ptr.as_ptr()) }
222        }
223    }
224}