rich_sdl2_rust/haptic/effect.rs
1//! Controls the effect on the haptic device.
2
3use crate::bind;
4
5use super::direction::Direction;
6
7/// An effect on the haptic device.
8#[derive(Debug, Clone, PartialEq, Eq)]
9#[non_exhaustive]
10pub enum HapticEffect {
11 /// Applies the constant force in the direction.
12 Constant(Direction, Play, Trigger, Level, Envelope),
13 /// Applies the force of the periodic waveform.
14 Periodic(Direction, Play, Trigger, Wave, Envelope),
15 /// Applies the force on fulfilled the condition.
16 Condition(Play, Trigger, Condition),
17 /// Applies the force by linear ramp.
18 Ramp(Direction, Play, Trigger, Ramp, Envelope),
19 /// Applies the left/right effect with two motors magnitude. One motor is high frequency, the other is low frequency.
20 LeftRight {
21 /// The length of the playing in milliseconds.
22 length: u32,
23 /// The magnitude for the large motor.
24 large_magnitude: u16,
25 /// The magnitude for the small motor.
26 small_magnitude: u16,
27 },
28 /// Applies the force of the periodic waveform from your sampled data.
29 Custom(Direction, Play, Trigger, Custom, Envelope),
30}
31
32impl HapticEffect {
33 pub(super) fn into_raw(self) -> bind::SDL_HapticEffect {
34 match self {
35 HapticEffect::Constant(dir, play, trigger, level, env) => bind::SDL_HapticEffect {
36 constant: bind::SDL_HapticConstant {
37 type_: bind::SDL_HAPTIC_CONSTANT as u16,
38 direction: dir.into_raw(),
39 length: play.length,
40 delay: play.delay,
41 button: trigger.button,
42 interval: trigger.interval,
43 level: level.0,
44 attack_length: env.attack_length,
45 attack_level: env.attack_level,
46 fade_length: env.fade_length,
47 fade_level: env.fade_level,
48 },
49 },
50 HapticEffect::Periodic(dir, play, trigger, wave, env) => bind::SDL_HapticEffect {
51 periodic: bind::SDL_HapticPeriodic {
52 type_: wave.kind.to_raw(),
53 direction: dir.into_raw(),
54 length: play.length,
55 delay: play.delay,
56 button: trigger.button,
57 interval: trigger.interval,
58 period: wave.period,
59 magnitude: wave.magnitude,
60 offset: wave.offset,
61 phase: wave.phase,
62 attack_length: env.attack_length,
63 attack_level: env.attack_level,
64 fade_length: env.fade_length,
65 fade_level: env.fade_level,
66 },
67 },
68 HapticEffect::Condition(play, trigger, condition) => bind::SDL_HapticEffect {
69 condition: bind::SDL_HapticCondition {
70 type_: 0,
71 direction: bind::SDL_HapticDirection {
72 type_: 0,
73 dir: [0; 3],
74 },
75 length: play.length,
76 delay: play.delay,
77 button: trigger.button,
78 interval: trigger.interval,
79 right_sat: condition.positive_level.0,
80 left_sat: condition.negative_level.0,
81 right_coeff: condition.positive_coefficient.0,
82 left_coeff: condition.negative_coefficient.0,
83 deadband: condition.dead_band.0,
84 center: condition.center.0,
85 },
86 },
87 HapticEffect::Ramp(dir, play, trigger, ramp, env) => bind::SDL_HapticEffect {
88 ramp: bind::SDL_HapticRamp {
89 type_: bind::SDL_HAPTIC_RAMP as u16,
90 direction: dir.into_raw(),
91 length: play.length,
92 delay: play.delay,
93 button: trigger.button,
94 interval: trigger.interval,
95 start: ramp.start.0,
96 end: ramp.end.0,
97 attack_length: env.attack_length,
98 attack_level: env.attack_level,
99 fade_length: env.fade_length,
100 fade_level: env.fade_level,
101 },
102 },
103 HapticEffect::LeftRight {
104 length,
105 large_magnitude,
106 small_magnitude,
107 } => bind::SDL_HapticEffect {
108 leftright: bind::SDL_HapticLeftRight {
109 type_: bind::SDL_HAPTIC_LEFTRIGHT as u16,
110 length,
111 large_magnitude,
112 small_magnitude,
113 },
114 },
115 HapticEffect::Custom(dir, play, trigger, mut custom, env) => bind::SDL_HapticEffect {
116 custom: bind::SDL_HapticCustom {
117 type_: bind::SDL_HAPTIC_CUSTOM as u16,
118 direction: dir.into_raw(),
119 length: play.length,
120 delay: play.delay,
121 button: trigger.button,
122 interval: trigger.interval,
123 channels: custom.channels,
124 period: custom.period,
125 samples: custom.samples,
126 data: custom.data.as_mut_ptr(),
127 attack_length: env.attack_length,
128 attack_level: env.attack_level,
129 fade_length: env.fade_length,
130 fade_level: env.fade_level,
131 },
132 },
133 }
134 }
135}
136
137impl From<bind::SDL_HapticEffect> for HapticEffect {
138 fn from(raw: bind::SDL_HapticEffect) -> Self {
139 match unsafe { raw.type_ } as u32 {
140 bind::SDL_HAPTIC_CONSTANT => {
141 let bind::SDL_HapticConstant {
142 direction,
143 length,
144 delay,
145 button,
146 interval,
147 level,
148 attack_length,
149 attack_level,
150 fade_length,
151 fade_level,
152 ..
153 } = unsafe { raw.constant };
154 Self::Constant(
155 direction.into(),
156 Play { length, delay },
157 Trigger { button, interval },
158 Level(level),
159 Envelope {
160 attack_length,
161 attack_level,
162 fade_length,
163 fade_level,
164 },
165 )
166 }
167 bind::SDL_HAPTIC_SINE
168 | bind::SDL_HAPTIC_TRIANGLE
169 | bind::SDL_HAPTIC_SAWTOOTHUP
170 | bind::SDL_HAPTIC_SAWTOOTHDOWN => {
171 let bind::SDL_HapticPeriodic {
172 type_,
173 direction,
174 length,
175 delay,
176 button,
177 interval,
178 period,
179 magnitude,
180 offset,
181 phase,
182 attack_length,
183 attack_level,
184 fade_length,
185 fade_level,
186 ..
187 } = unsafe { raw.periodic };
188 Self::Periodic(
189 direction.into(),
190 Play { length, delay },
191 Trigger { button, interval },
192 Wave {
193 kind: type_.into(),
194 period,
195 magnitude,
196 offset,
197 phase,
198 },
199 Envelope {
200 attack_length,
201 attack_level,
202 fade_length,
203 fade_level,
204 },
205 )
206 }
207 bind::SDL_HAPTIC_SPRING
208 | bind::SDL_HAPTIC_DAMPER
209 | bind::SDL_HAPTIC_INERTIA
210 | bind::SDL_HAPTIC_FRICTION => {
211 let bind::SDL_HapticCondition {
212 type_,
213 length,
214 delay,
215 button,
216 interval,
217 right_sat,
218 left_sat,
219 right_coeff,
220 left_coeff,
221 deadband,
222 center,
223 ..
224 } = unsafe { raw.condition };
225 Self::Condition(
226 Play { length, delay },
227 Trigger { button, interval },
228 Condition {
229 positive_level: Vector3(right_sat),
230 negative_level: Vector3(left_sat),
231 positive_coefficient: Vector3(right_coeff),
232 negative_coefficient: Vector3(left_coeff),
233 dead_band: Vector3(deadband),
234 center: Vector3(center),
235 },
236 )
237 }
238 bind::SDL_HAPTIC_RAMP => {
239 let bind::SDL_HapticRamp {
240 direction,
241 length,
242 delay,
243 button,
244 interval,
245 start,
246 end,
247 attack_length,
248 attack_level,
249 fade_length,
250 fade_level,
251 ..
252 } = unsafe { raw.ramp };
253 Self::Ramp(
254 direction.into(),
255 Play { length, delay },
256 Trigger { button, interval },
257 Ramp {
258 start: Level(start),
259 end: Level(end),
260 },
261 Envelope {
262 attack_length,
263 attack_level,
264 fade_length,
265 fade_level,
266 },
267 )
268 }
269 bind::SDL_HAPTIC_LEFTRIGHT => {
270 let bind::SDL_HapticLeftRight {
271 length,
272 large_magnitude,
273 small_magnitude,
274 ..
275 } = unsafe { raw.leftright };
276 Self::LeftRight {
277 length,
278 large_magnitude,
279 small_magnitude,
280 }
281 }
282 bind::SDL_HAPTIC_CUSTOM => {
283 let bind::SDL_HapticCustom {
284 direction,
285 length,
286 delay,
287 button,
288 interval,
289 channels,
290 period,
291 samples,
292 data,
293 attack_length,
294 attack_level,
295 fade_length,
296 fade_level,
297 ..
298 } = unsafe { raw.custom };
299 Self::Custom(
300 direction.into(),
301 Play { length, delay },
302 Trigger { button, interval },
303 Custom {
304 channels,
305 period,
306 samples,
307 data: unsafe {
308 std::slice::from_raw_parts_mut(
309 data,
310 channels as usize * samples as usize,
311 )
312 }
313 .to_vec(),
314 },
315 Envelope {
316 attack_length,
317 attack_level,
318 fade_length,
319 fade_level,
320 },
321 )
322 }
323 _ => unreachable!(),
324 }
325 }
326}
327
328/// Length and delay of the playing.
329#[derive(Debug, Clone, PartialEq, Eq)]
330pub struct Play {
331 /// The length of the playing in milliseconds.
332 pub length: u32,
333 /// The delay of the playing.
334 pub delay: u16,
335}
336
337/// A trigger button to start the effect, and an interval between the effect.
338#[derive(Debug, Clone, PartialEq, Eq)]
339pub struct Trigger {
340 /// The trigger button to start the effect
341 pub button: u16,
342 /// The interval between the effect in milliseconds.
343 pub interval: u16,
344}
345
346/// A magnitude level of the force.
347#[derive(Debug, Clone, PartialEq, Eq)]
348pub struct Level(pub i16);
349
350/// An envelope to fade in/out the effect. If both `attack_length` and `fade_level` are `0`, the envelope is not used. An example of a constant effect evolution in time:
351///
352/// ```text
353/// Strength
354/// ^
355/// |
356/// | effect level --> _________________
357/// | / \
358/// | / \
359/// | / \
360/// | / \
361/// | attack_level --> | \
362/// | | | <--- fade_level
363/// |
364/// +--------------------------------------------------> Time
365///
366/// [--] [---]
367/// attack_length fade_length
368///
369/// [------------------][-----------------------]
370/// delay length
371/// ```
372#[derive(Debug, Clone, PartialEq, Eq)]
373pub struct Envelope {
374 /// The length of the level from `attack_level` to the effect level in milliseconds.
375 pub attack_length: u16,
376 /// The initial force level on applying.
377 pub attack_level: u16,
378 /// The length of the level from the effect level to `attack_level`in milliseconds.
379 pub fade_length: u16,
380 /// The end force level on applying.
381 pub fade_level: u16,
382}
383
384/// A periodic waveform specification.
385#[derive(Debug, Clone, PartialEq, Eq)]
386pub struct Wave {
387 /// The kind of the waveform.
388 pub kind: WaveKind,
389 /// The period of the waveform.
390 pub period: u16,
391 /// The magnitude by peak-to-peak level of the waveform.
392 pub magnitude: i16,
393 /// The amplifier offset, mean value of the waveform.
394 pub offset: i16,
395 /// The phase shift in degrees times 100.
396 pub phase: u16,
397}
398
399/// A kind of the waveform.
400#[derive(Debug, Clone, PartialEq, Eq)]
401#[non_exhaustive]
402pub enum WaveKind {
403 /// A sine wave like:
404 ///
405 /// ```text
406 /// __ __ __ __
407 /// / \ / \ / \ /
408 /// / \__/ \__/ \__/
409 /// ```
410 Sine,
411 /// A triangle wave like:
412 ///
413 /// ```text
414 /// /\ /\ /\ /\ /\
415 /// / \ / \ / \ / \ /
416 /// / \/ \/ \/ \/
417 /// ```
418 Triangle,
419 /// An upwards sawtooth wave like:
420 ///
421 /// ```text
422 /// /| /| /| /| /| /| /|
423 /// / | / | / | / | / | / | / |
424 /// / |/ |/ |/ |/ |/ |/ |
425 /// ```
426 SawToothUp,
427 /// A downwards sawtooth wave like:
428 ///
429 /// ```text
430 /// \ |\ |\ |\ |\ |\ |\ |
431 /// \ | \ | \ | \ | \ | \ | \ |
432 /// \| \| \| \| \| \| \|
433 /// ```
434 SwaToothDown,
435}
436
437impl WaveKind {
438 fn to_raw(&self) -> u16 {
439 (match *self {
440 WaveKind::Sine => bind::SDL_HAPTIC_SINE,
441 WaveKind::Triangle => bind::SDL_HAPTIC_TRIANGLE,
442 WaveKind::SawToothUp => bind::SDL_HAPTIC_SAWTOOTHUP,
443 WaveKind::SwaToothDown => bind::SDL_HAPTIC_SAWTOOTHDOWN,
444 }) as u16
445 }
446}
447
448impl From<u16> for WaveKind {
449 fn from(raw: u16) -> Self {
450 match raw as u32 {
451 bind::SDL_HAPTIC_SINE => Self::Sine,
452 bind::SDL_HAPTIC_TRIANGLE => Self::Triangle,
453 bind::SDL_HAPTIC_SAWTOOTHUP => Self::SawToothUp,
454 bind::SDL_HAPTIC_SAWTOOTHDOWN => Self::SwaToothDown,
455 _ => unreachable!(),
456 }
457 }
458}
459
460/// A vector to represent the XYZ component for [`Condition`].
461#[derive(Debug, Clone, PartialEq, Eq)]
462pub struct Vector3<T>(pub [T; 3]);
463
464/// A condition to trigger the effect. Refer to [`Direction`] for which side is the positive/negative on the joystick.
465#[derive(Debug, Clone, PartialEq, Eq)]
466pub struct Condition {
467 /// The level when the joystick in the positive side.
468 pub positive_level: Vector3<u16>,
469 /// The level when the joystick in the negative side.
470 pub negative_level: Vector3<u16>,
471 /// How fast to increase the force towards the positive side.
472 pub positive_coefficient: Vector3<i16>,
473 /// How fast to increase the force towards the negative side.
474 pub negative_coefficient: Vector3<i16>,
475 /// The size of the dead zone.
476 pub dead_band: Vector3<u16>,
477 /// The position of the dead zone.
478 pub center: Vector3<i16>,
479}
480
481/// A linear ramp to interpolate the force of the effect.
482#[derive(Debug, Clone, PartialEq, Eq)]
483pub struct Ramp {
484 /// The level at the start to play.
485 pub start: Level,
486 /// The level at the end to play.
487 pub end: Level,
488}
489
490/// A custom periodic waveform by your sampled data.
491#[derive(Debug, Clone, PartialEq, Eq)]
492pub struct Custom {
493 /// The numbers of using axes.
494 pub channels: u8,
495 /// The period of your sampled data.
496 pub period: u16,
497 /// The numbers of the samples in your data.
498 pub samples: u16,
499 /// The sampled data. Its length must be equal to `channels * samples`.
500 pub data: Vec<u16>,
501}