cirque_pinnacle/mode.rs
1use crate::*;
2use embedded_hal::spi::SpiDevice;
3
4#[expect(clippy::struct_excessive_bools)]
5pub struct Relative {
6 /// Set to false to disable X.
7 ///
8 /// Disabling the X-axis will not allow regular tracking and is not recommended
9 /// for typical applications.
10 pub x: bool,
11
12 /// Set to false to disable Y.
13 ///
14 /// Disabling the Y-axis will not allow regular tracking and is not recommended
15 /// for typical applications.
16 pub y: bool,
17
18 /// Set to false to disable filter.
19 ///
20 /// The Filter disable bit controls whether the filtering algorithm is applied
21 /// to generated data. By default the hardware filters are enabled.
22 /// Cirque does not recommend disabling hardware filtering.
23 pub filter: bool,
24
25 /// Swap X & Y
26 pub swap_x_y: bool,
27
28 /// Enable GlideExtend®.
29 ///
30 /// GlideExtend is Cirque's patented motion extender technology that allows
31 /// the user to continue the drag function when an edge is reached, by lifting
32 /// and repositioning the finger.
33 pub glide_extend: bool,
34
35 /// Enable scroll.
36 pub scroll: bool,
37
38 /// Enable secondary tap.
39 ///
40 /// Secondary Taps allows a tap in the upper right corner (standard orientation)
41 /// to simulate an activation of the secondary button.
42 pub secondary_tap: bool,
43
44 /// If false, disables all taps.
45 ///
46 /// Disabling all taps disables secondary taps, even if secondary tap
47 /// is explicitly enabled.
48 pub taps: bool,
49
50 /// Enable Intellimouse.
51 ///
52 /// Intellimouse enabled will change Pinnacle's relative data packet to four bytes
53 /// rather than three. The fourth byte (PacketByte_3) will report scroll data
54 /// (referred to as wheel count).
55 pub intellimouse: bool,
56}
57
58impl Default for Relative {
59 fn default() -> Self {
60 Self {
61 x: true,
62 y: true,
63 filter: true,
64 swap_x_y: false,
65 glide_extend: false,
66 scroll: false,
67 secondary_tap: false,
68 taps: true,
69 intellimouse: false,
70 }
71 }
72}
73
74impl Relative {
75 pub fn init<S: SpiDevice<u8>>(&self, spi: S) -> Result<Touchpad<S, Self>, S::Error> {
76 let config1 =
77 1 | u8::from(!self.y) << 4 | u8::from(!self.x) << 3 | u8::from(!self.filter) << 2;
78 let config2 = u8::from(self.swap_x_y) << 7
79 | u8::from(!self.glide_extend) << 4
80 | u8::from(!self.scroll) << 3
81 | u8::from(!self.secondary_tap) << 2
82 | u8::from(!self.taps) << 1
83 | u8::from(self.intellimouse);
84 init(spi, config1, config2)
85 }
86}
87
88#[expect(clippy::struct_excessive_bools)]
89pub struct Absolute {
90 /// Set to false to disable X.
91 ///
92 /// Disabling the X-axis will not allow regular tracking and is not recommended
93 /// for typical applications.
94 pub x: bool,
95
96 /// Set to false to disable Y.
97 ///
98 /// Disabling the Y-axis will not allow regular tracking and is not recommended
99 /// for typical applications.
100 pub y: bool,
101
102 /// Set to false to disable filter.
103 ///
104 /// The Filter disable bit controls whether the filtering algorithm is applied
105 /// to generated data. By default the hardware filters are enabled.
106 /// Cirque does not recommend disabling hardware filtering.
107 pub filter: bool,
108
109 /// X data Invert.
110 ///
111 /// Y and X data count invert is only available when in absolute mode.
112 invert_x: bool,
113
114 /// Y data Invert.
115 ///
116 /// Y and X data count invert is only available when in absolute mode.
117 invert_y: bool,
118}
119
120impl Default for Absolute {
121 fn default() -> Self {
122 Self {
123 x: true,
124 y: true,
125 filter: true,
126 invert_x: false,
127 invert_y: false,
128 }
129 }
130}
131
132impl Absolute {
133 pub fn init<S: SpiDevice<u8>>(&self, spi: S) -> Result<Touchpad<S, Self>, S::Error> {
134 let config1 = 1
135 | u8::from(self.invert_y) << 7
136 | u8::from(self.invert_x) << 6
137 | u8::from(!self.y) << 4
138 | u8::from(!self.x) << 3
139 | u8::from(!self.filter) << 2
140 | 1 << 1;
141 let config2 = 0b11111; // disable all relative mode features;
142 init(spi, config1, config2)
143 }
144}
145
146pub trait Mode {}
147impl Mode for Relative {}
148impl Mode for Absolute {}
149
150fn init<M, S>(spi: S, config1: u8, config2: u8) -> Result<Touchpad<S, M>, S::Error>
151where
152 S: SpiDevice<u8>,
153 M: Mode,
154{
155 let mut pinnacle = Touchpad::new(spi);
156 pinnacle.clear_flags()?;
157 pinnacle.set_power_mode(PowerMode::Active)?;
158 pinnacle.write(FEED_CONFIG2_ADDR, config2)?;
159 pinnacle.write(FEED_CONFIG1_ADDR, config1)?;
160 Ok(pinnacle)
161}
162
163#[derive(Copy, Clone, Debug)]
164pub struct AbsoluteData {
165 pub x: u16,
166 pub y: u16,
167 pub z: u8,
168 pub button_flags: u8,
169}
170
171impl AbsoluteData {
172 pub const X_MIN: u16 = 0;
173 pub const Y_MIN: u16 = 0;
174 pub const X_MAX: u16 = 2047;
175 pub const Y_MAX: u16 = 1535;
176
177 #[must_use]
178 pub const fn touched(&self) -> bool {
179 self.z != 0
180 }
181
182 /// Represent X as f32 on the range from -1.0 to +1.0.
183 #[must_use]
184 pub fn x_f32(&self) -> f32 {
185 f32::from(self.x * 2) / f32::from(Self::X_MAX) - 1.0
186 }
187
188 /// Represent Y as f32 on the range from -1.0 to +1.0.
189 #[must_use]
190 pub fn y_f32(&self) -> f32 {
191 f32::from(self.y * 2) / f32::from(Self::Y_MAX) - 1.0
192 }
193}
194
195#[derive(Copy, Clone, Debug)]
196pub struct RelativeData {
197 pub x: i16,
198 pub y: i16,
199 pub wheel: i8,
200 pub buttons: Buttons,
201}
202
203#[derive(Copy, Clone, Debug)]
204pub struct Buttons {
205 /// BTN Primary (or tap).
206 pub primary: bool,
207 /// BTN Secondary (or top right corner tap).
208 pub secondary: bool,
209 /// BTN Auxiliary.
210 pub auxiliary: bool,
211}