1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4pub enum HapticPattern {
5 None,
6 Click,
7 DoubleClick,
8 LongPress,
9 Success,
10 Alert,
11 Custom(&'static [(u32, u8)]),
12}
13
14static CLICK_STEPS: &[(u32, u8)] = &[(30, 255)];
15static DOUBLE_CLICK_STEPS: &[(u32, u8)] = &[(40, 255), (40, 0), (40, 255)];
16static LONG_PRESS_STEPS: &[(u32, u8)] = &[(150, 200)];
17static SUCCESS_STEPS: &[(u32, u8)] = &[(80, 150), (40, 0), (120, 255)];
18static ALERT_STEPS: &[(u32, u8)] = &[(200, 255), (80, 0), (200, 255)];
19
20impl HapticPattern {
21 pub fn steps(&self) -> &'static [(u32, u8)] {
22 match self {
23 HapticPattern::None => &[],
24 HapticPattern::Click => CLICK_STEPS,
25 HapticPattern::DoubleClick => DOUBLE_CLICK_STEPS,
26 HapticPattern::LongPress => LONG_PRESS_STEPS,
27 HapticPattern::Success => SUCCESS_STEPS,
28 HapticPattern::Alert => ALERT_STEPS,
29 HapticPattern::Custom(steps) => steps,
30 }
31 }
32}
33
34#[derive(Clone, Copy, Debug)]
35pub struct HapticSequencer {
36 steps: &'static [(u32, u8)],
37 current_step: usize,
38 step_elapsed_ms: u32,
39 current_intensity: u8,
40}
41
42#[allow(clippy::derivable_impls)]
43impl Default for HapticSequencer {
44 fn default() -> Self {
45 Self {
46 steps: &[],
47 current_step: 0,
48 step_elapsed_ms: 0,
49 current_intensity: 0,
50 }
51 }
52}
53
54impl HapticSequencer {
55 pub fn new() -> Self {
56 Self::default()
57 }
58
59 pub fn play(&mut self, pattern: HapticPattern) {
60 let steps = pattern.steps();
61 if steps.is_empty() {
62 self.steps = &[];
63 self.current_step = 0;
64 self.step_elapsed_ms = 0;
65 self.current_intensity = 0;
66 } else {
67 self.steps = steps;
68 self.current_step = 0;
69 self.step_elapsed_ms = 0;
70 self.current_intensity = steps[0].1;
71 }
72 }
73
74 pub fn stop(&mut self) {
75 self.play(HapticPattern::None);
76 }
77
78 pub fn tick(&mut self, dt_ms: u32) {
79 if self.steps.is_empty() {
80 return;
81 }
82
83 self.step_elapsed_ms = self.step_elapsed_ms.saturating_add(dt_ms);
84
85 while self.current_step < self.steps.len()
86 && self.step_elapsed_ms >= self.steps[self.current_step].0
87 {
88 self.step_elapsed_ms -= self.steps[self.current_step].0;
89 self.current_step += 1;
90 }
91
92 if self.current_step >= self.steps.len() {
93 self.steps = &[];
95 self.current_step = 0;
96 self.step_elapsed_ms = 0;
97 self.current_intensity = 0;
98 } else {
99 self.current_intensity = self.steps[self.current_step].1;
100 }
101 }
102
103 pub fn current_intensity(&self) -> u8 {
104 self.current_intensity
105 }
106
107 pub fn is_playing(&self) -> bool {
108 !self.steps.is_empty()
109 }
110}