1use 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 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
24 pub struct HapticProperty: u32 {
25 const CONSTANT = 1 << 0;
27 const SINE = 1 << 1;
29 const LEFT_RIGHT = 1 << 2;
31 const TRIANGLE = 1 << 3;
33 const SAW_TOOTH_UP = 1 << 4;
35 const SAW_TOOTH_DOWN = 1 << 5;
37 const RAMP = 1 << 6;
39 const CUSTOM = 1 << 11;
41 const GAIN = 1 << 12;
43 const AUTO_CENTER = 1 << 13;
45 const STATUS = 1 << 14;
47 const PAUSE = 1 << 15;
49 }
50}
51
52pub 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 #[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 #[must_use]
76 pub fn num_axes(&self) -> u32 {
77 unsafe { bind::SDL_HapticNumAxes(self.ptr.as_ptr()) as u32 }
78 }
79
80 #[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 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 #[must_use]
110 pub fn effects_creation_capacity(&self) -> usize {
111 unsafe { bind::SDL_HapticNumEffects(self.ptr.as_ptr()) as usize }
112 }
113
114 #[must_use]
116 pub fn effects_playing_capacity(&self) -> usize {
117 unsafe { bind::SDL_HapticNumEffectsPlaying(self.ptr.as_ptr()) as usize }
118 }
119
120 pub fn stop_all_effect(&self) {
122 unsafe {
123 bind::SDL_HapticStopAll(self.ptr.as_ptr());
124 }
125 }
126
127 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 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 #[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 #[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
168pub struct PausedHaptic {
170 haptic: Haptic,
171}
172
173impl PausedHaptic {
174 #[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
184pub struct HapticSet(Vec<Haptic>);
186
187impl HapticSet {
188 #[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 #[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}