1pub fn apply_deadzone_i16(v: i16, deadzone: i16) -> i16 {
9 let dz = deadzone.clamp(0, 32767);
10 let vi = v as i32;
11 let sign = vi.signum();
12 let mag = vi.abs();
13 if mag <= (dz as i32) {
14 0
15 } else {
16 let rem = 32767 - (dz as i32);
17 let adj = ((mag - (dz as i32)) * 32767) / rem.max(1);
18 (adj * sign).clamp(-32768, 32767) as i16
19 }
20}
21
22pub fn apply_anti_deadzone_i16(v: i16, anti: i16) -> i16 {
25 let anti = anti.clamp(0, 32767) as i32;
26 let vi = v as i32;
27 if vi == 0 || anti == 0 {
28 return v;
29 }
30 let sign = vi.signum();
31 let mag = vi.abs().clamp(0, 32767);
32 let boosted = mag.max(anti);
33 (boosted * sign).clamp(-32768, 32767) as i16
34}
35
36pub fn square_to_circle(lx: i16, ly: i16) -> (i16, i16) {
38 let nx = (lx as f32) / 32767.0;
39 let ny = (ly as f32) / 32767.0;
40 let sx = (nx * (1.0 - 0.5 * ny * ny).sqrt()).clamp(-1.0, 1.0);
41 let sy = (ny * (1.0 - 0.5 * nx * nx).sqrt()).clamp(-1.0, 1.0);
42 ((sx * 32767.0).round() as i16, (sy * 32767.0).round() as i16)
43}
44
45pub fn apply_radial_deadzone(lx: i16, ly: i16, deadzone: i16) -> (i16, i16) {
47 let dz = deadzone.clamp(0, 32767) as f32;
48 let x = lx as f32;
49 let y = ly as f32;
50 let r = (x * x + y * y).sqrt();
51 if r <= dz {
52 return (0, 0);
53 }
54 let r2 = ((r - dz) * 32767.0) / (32767.0 - dz).max(1.0);
55 if r == 0.0 {
56 return (0, 0);
57 }
58 let scale = r2 / r;
59 let nx = (x * scale).clamp(-32767.0, 32767.0);
60 let ny = (y * scale).clamp(-32767.0, 32767.0);
61 (nx.round() as i16, ny.round() as i16)
62}
63
64pub fn postprocess_stick(
66 lx: i16,
67 ly: i16,
68 use_circle: bool,
69 radial_deadzone_v: i16,
70 anti_deadzone_v: i16
71) -> (i16, i16) {
72 let (mut x, mut y) = if use_circle { square_to_circle(lx, ly) } else { (lx, ly) };
73 (x, y) = apply_radial_deadzone(x, y, radial_deadzone_v);
74 x = apply_anti_deadzone_i16(x, anti_deadzone_v);
75 y = apply_anti_deadzone_i16(y, anti_deadzone_v);
76 (x, y)
77}
78
79pub fn trigger_pressed(v: u8, thresh: u8) -> bool {
81 v >= thresh
82}
83
84pub fn button_edges(prev: u16, curr: u16) -> (u16, u16) {
88 let changed = prev ^ curr;
89 let pressed = changed & curr; let released = changed & !curr; (pressed, released)
92}
93
94pub fn debounce_bit(prev_out: bool, curr_in: bool, counter: u8, hold_count: u8) -> (bool, u8) {
96 if curr_in == prev_out {
97 (prev_out, 0)
98 } else {
99 let c = counter.saturating_add(1);
100 if c >= hold_count {
101 (curr_in, 0)
102 } else {
103 (prev_out, c)
104 }
105 }
106}
107
108pub fn autorepeat_fire(
110 is_down: bool,
111 elapsed_ms_since_change: u32,
112 first_delay_ms: u32,
113 repeat_rate_ms: u32
114) -> bool {
115 if !is_down {
116 return false;
117 }
118 if elapsed_ms_since_change == 0 {
119 return true;
120 }
121 if elapsed_ms_since_change < first_delay_ms {
122 return false;
123 }
124 let after = elapsed_ms_since_change - first_delay_ms;
125 repeat_rate_ms != 0 && after % repeat_rate_ms == 0
126}
127
128pub fn remap_buttons(mask: u16, remap: &[(u16, u16)]) -> u16 {
131 let mut out = 0u16;
132 for &(src, dst) in remap {
133 if (mask & src) != 0 {
134 out |= dst;
135 }
136 }
137 out
138}
139
140pub fn merge_masks(masks: &[u16]) -> u16 {
142 masks.iter().fold(0u16, |acc, &m| acc | m)
143}
144
145pub fn set_bit(mask: u16, bit: u16, on: bool) -> u16 {
147 if on { mask | bit } else { mask & !bit }
148}
149pub fn toggle_bit(mask: u16, bit: u16) -> u16 {
150 mask ^ bit
151}