1#![no_std]
4
5use core::fmt::Debug;
6
7use fixed::{traits::ToFixed, types::U17F15};
8use fixed_macro::types::{I17F15, U17F15};
9
10pub trait TouchScreen {
12 type Error;
14
15 fn touches(&mut self) -> Result<impl IntoIterator<Item = Touch>, Error<Self::Error>>;
20}
21
22pub trait AsyncTouchScreen {
24 type Error;
26
27 fn touches(
32 &mut self,
33 ) -> impl Future<Output = Result<impl IntoIterator<Item = Touch>, Error<Self::Error>>>;
34}
35
36#[derive(Debug, Clone, PartialEq)]
38pub struct Touch {
39 pub id: u8,
44
45 pub x: u16,
47
48 pub y: u16,
50
51 pub phase: Phase,
53
54 pub tool: Tool,
56
57 pub proximity: Option<u16>,
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub enum Phase {
64 Started,
66 Moved,
68 Ended,
70 Cancelled,
72}
73
74#[derive(Debug, Clone, Copy, PartialEq)]
76pub enum Tool {
77 Finger,
79 Stylus {
81 pressure: Option<u16>,
83 tilt: Option<UnitAngle>,
87 azimuth: Option<UnitAngle>,
91 },
92}
93
94#[derive(Debug, Clone, Copy, PartialEq)]
96pub enum Error<E> {
97 Interface(E),
99 DataCorruption,
101 DeviceError,
103}
104
105impl<E> From<E> for Error<E> {
106 fn from(error: E) -> Self {
107 Error::Interface(error)
108 }
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq)]
115pub struct UnitAngle(fixed::types::U1F15);
116
117impl UnitAngle {
118 #[must_use]
125 pub fn from_pi_radians(value: impl ToFixed) -> Self {
126 UnitAngle(value.wrapping_to_fixed())
127 }
128
129 #[must_use]
133 pub fn from_radians(value: impl ToFixed) -> Self {
134 let fixed_radians = value.to_fixed::<fixed::types::U17F15>();
135 let pi_radians = fixed_radians / U17F15!(3.14159265359);
136 UnitAngle(pi_radians.wrapping_to_fixed())
137 }
138
139 #[must_use]
143 pub fn from_degrees(value: impl ToFixed) -> Self {
144 let fixed_degrees = value.to_fixed::<fixed::types::I17F15>();
145 let radians = fixed_degrees / I17F15!(180);
146 UnitAngle(radians.wrapping_to_fixed())
147 }
148
149 #[must_use]
153 pub fn as_pi_radians(&self) -> fixed::types::U1F15 {
154 self.0
155 }
156
157 #[must_use]
158 pub fn as_radians_f32(&self) -> f32 {
159 (self.0.to_fixed::<U17F15>() * U17F15!(3.14159265359)).to_num::<f32>()
160 }
161
162 #[must_use]
163 pub fn as_degrees_f32(&self) -> f32 {
164 (self.0.to_fixed::<U17F15>() * U17F15!(180.0)).to_num::<f32>()
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use core::f32;
171
172 use super::*;
173
174 #[test]
175 #[expect(clippy::cast_precision_loss)]
176 fn angle_from_pi_radians() {
177 let angle = UnitAngle::from_pi_radians(0.0);
178 assert_eq!(angle.as_pi_radians(), fixed::types::U1F15::from_num(0.0));
179 assert!(angle.as_radians_f32().abs() < 0.00001);
180 assert!(angle.as_degrees_f32().abs() < 0.00001);
181
182 for i in -8..8 {
183 let offset = (i * 2) as f32;
184 let angle = UnitAngle::from_pi_radians(1.0 + offset);
185 assert_eq!(angle.as_pi_radians(), fixed::types::U1F15::from_num(1.0));
186 assert!((angle.as_radians_f32() - 1.0 * f32::consts::PI).abs() < 0.00001);
187 assert!((angle.as_degrees_f32() - 180.0).abs() < 0.00001);
188 }
189 }
190
191 #[test]
192 #[expect(clippy::cast_precision_loss)]
193 fn sweep_360_degrees() {
194 for i in -1080..1080 {
195 let angle = UnitAngle::from_degrees(i);
196
197 let unit_degrees = (i + 360 * 20) % 360;
198 let radians = unit_degrees as f32 * f32::consts::PI / 180.0;
199
200 assert!(
201 (angle.as_degrees_f32() - unit_degrees as f32).abs() < 0.01,
202 "Expected {} to be nearly {unit_degrees}",
203 angle.as_degrees_f32()
204 );
205 assert!(
206 (angle.as_radians_f32() - radians).abs() < 0.001,
207 "Expected {} to be nearly {radians}",
208 angle.as_radians_f32()
209 );
210 }
211 }
212}