1use crate::input::TouchProto;
2use lazy_static::lazy_static;
3use std::env;
4use std::fmt;
5
6#[derive(Debug, Copy, Clone, Eq, PartialEq)]
7pub enum Model {
8 LibraColour,
9 ClaraColour,
10 ClaraBW,
11 Elipsa2E,
12 Clara2E,
13 Libra2,
14 Sage,
15 Elipsa,
16 Nia,
17 LibraH2O,
18 Forma32GB,
19 Forma,
20 ClaraHD,
21 AuraH2OEd2V2,
22 AuraH2OEd2V1,
23 AuraEd2V2,
24 AuraEd2V1,
25 AuraONELimEd,
26 AuraONE,
27 Touch2,
28 GloHD,
29 AuraH2O,
30 Aura,
31 AuraHD,
32 Mini,
33 Glo,
34 TouchC,
35 TouchAB,
36}
37
38#[derive(Debug, Copy, Clone, Eq, PartialEq)]
39pub enum Orientation {
40 Portrait,
41 Landscape,
42}
43
44impl fmt::Display for Model {
45 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46 match *self {
47 Model::LibraColour => write!(f, "Libra Colour"),
48 Model::ClaraColour => write!(f, "Clara Colour"),
49 Model::ClaraBW => write!(f, "Clara BW"),
50 Model::Elipsa2E => write!(f, "Elipsa 2E"),
51 Model::Clara2E => write!(f, "Clara 2E"),
52 Model::Libra2 => write!(f, "Libra 2"),
53 Model::Sage => write!(f, "Sage"),
54 Model::Elipsa => write!(f, "Elipsa"),
55 Model::Nia => write!(f, "Nia"),
56 Model::LibraH2O => write!(f, "Libra H₂O"),
57 Model::Forma32GB => write!(f, "Forma 32GB"),
58 Model::Forma => write!(f, "Forma"),
59 Model::ClaraHD => write!(f, "Clara HD"),
60 Model::AuraH2OEd2V1 => write!(f, "Aura H₂O Edition 2 Version 1"),
61 Model::AuraH2OEd2V2 => write!(f, "Aura H₂O Edition 2 Version 2"),
62 Model::AuraEd2V1 => write!(f, "Aura Edition 2 Version 1"),
63 Model::AuraEd2V2 => write!(f, "Aura Edition 2 Version 2"),
64 Model::AuraONELimEd => write!(f, "Aura ONE Limited Edition"),
65 Model::AuraONE => write!(f, "Aura ONE"),
66 Model::Touch2 => write!(f, "Touch 2.0"),
67 Model::GloHD => write!(f, "Glo HD"),
68 Model::AuraH2O => write!(f, "Aura H₂O"),
69 Model::Aura => write!(f, "Aura"),
70 Model::AuraHD => write!(f, "Aura HD"),
71 Model::Mini => write!(f, "Mini"),
72 Model::Glo => write!(f, "Glo"),
73 Model::TouchC => write!(f, "Touch C"),
74 Model::TouchAB => write!(f, "Touch A/B"),
75 }
76 }
77}
78
79#[derive(Debug)]
80pub struct Device {
81 pub model: Model,
82 pub proto: TouchProto,
83 pub dims: (u32, u32),
84 pub dpi: u16,
85}
86
87#[derive(Debug, Copy, Clone, PartialEq)]
88pub enum FrontlightKind {
89 Standard,
90 Natural,
91 Premixed,
92}
93
94impl Device {
95 pub fn new(product: &str, model_number: &str) -> Device {
96 match product {
97 "kraken" => Device {
98 model: Model::Glo,
99 proto: TouchProto::Single,
100 dims: (758, 1024),
101 dpi: 212,
102 },
103 "pixie" => Device {
104 model: Model::Mini,
105 proto: TouchProto::Single,
106 dims: (600, 800),
107 dpi: 200,
108 },
109 "dragon" => Device {
110 model: Model::AuraHD,
111 proto: TouchProto::Single,
112 dims: (1080, 1440),
113 dpi: 265,
114 },
115 "phoenix" => Device {
116 model: Model::Aura,
117 proto: TouchProto::MultiA,
118 dims: (758, 1024),
119 dpi: 212,
120 },
121 "dahlia" => Device {
122 model: Model::AuraH2O,
123 proto: TouchProto::MultiA,
124 dims: (1080, 1440),
125 dpi: 265,
126 },
127 "alyssum" => Device {
128 model: Model::GloHD,
129 proto: TouchProto::MultiA,
130 dims: (1072, 1448),
131 dpi: 300,
132 },
133 "pika" => Device {
134 model: Model::Touch2,
135 proto: TouchProto::MultiA,
136 dims: (600, 800),
137 dpi: 167,
138 },
139 "daylight" => Device {
140 model: if model_number == "381" {
141 Model::AuraONELimEd
142 } else {
143 Model::AuraONE
144 },
145 proto: TouchProto::MultiA,
146 dims: (1404, 1872),
147 dpi: 300,
148 },
149 "star" => Device {
150 model: if model_number == "379" {
151 Model::AuraEd2V2
152 } else {
153 Model::AuraEd2V1
154 },
155 proto: TouchProto::MultiA,
156 dims: (758, 1024),
157 dpi: 212,
158 },
159 "snow" => Device {
160 model: if model_number == "378" {
161 Model::AuraH2OEd2V2
162 } else {
163 Model::AuraH2OEd2V1
164 },
165 proto: TouchProto::MultiB,
166 dims: (1080, 1440),
167 dpi: 265,
168 },
169 "nova" => Device {
170 model: Model::ClaraHD,
171 proto: TouchProto::MultiB,
172 dims: (1072, 1448),
173 dpi: 300,
174 },
175 "frost" => Device {
176 model: if model_number == "380" {
177 Model::Forma32GB
178 } else {
179 Model::Forma
180 },
181 proto: TouchProto::MultiB,
182 dims: (1440, 1920),
183 dpi: 300,
184 },
185 "storm" => Device {
186 model: Model::LibraH2O,
187 proto: TouchProto::MultiB,
188 dims: (1264, 1680),
189 dpi: 300,
190 },
191 "luna" => Device {
192 model: Model::Nia,
193 proto: TouchProto::MultiA,
194 dims: (758, 1024),
195 dpi: 212,
196 },
197 "europa" => Device {
198 model: Model::Elipsa,
199 proto: TouchProto::MultiC,
200 dims: (1404, 1872),
201 dpi: 227,
202 },
203 "cadmus" => Device {
204 model: Model::Sage,
205 proto: TouchProto::MultiC,
206 dims: (1440, 1920),
207 dpi: 300,
208 },
209 "io" => Device {
210 model: Model::Libra2,
211 proto: TouchProto::MultiC,
212 dims: (1264, 1680),
213 dpi: 300,
214 },
215 "goldfinch" => Device {
216 model: Model::Clara2E,
217 proto: TouchProto::MultiB,
218 dims: (1072, 1448),
219 dpi: 300,
220 },
221 "condor" => Device {
222 model: Model::Elipsa2E,
223 proto: TouchProto::MultiC,
224 dims: (1404, 1872),
225 dpi: 227,
226 },
227 "spaBW" | "spaBWTPV" => Device {
228 model: Model::ClaraBW,
229 proto: TouchProto::MultiB,
230 dims: (1072, 1448),
231 dpi: 300,
232 },
233 "spaColour" => Device {
234 model: Model::ClaraColour,
235 proto: TouchProto::MultiB,
236 dims: (1072, 1448),
237 dpi: 300,
238 },
239 "monza" => Device {
240 model: Model::LibraColour,
241 proto: TouchProto::MultiB,
242 dims: (1264, 1680),
243 dpi: 300,
244 },
245 _ => Device {
246 model: if model_number == "320" {
247 Model::TouchC
248 } else {
249 Model::TouchAB
250 },
251 proto: TouchProto::Single,
252 dims: (600, 800),
253 dpi: 167,
254 },
255 }
256 }
257
258 pub fn color_samples(&self) -> usize {
259 match self.model {
260 Model::ClaraColour | Model::LibraColour => 3,
261 _ => 1,
262 }
263 }
264
265 pub fn frontlight_kind(&self) -> FrontlightKind {
266 match self.model {
267 Model::ClaraHD
268 | Model::Forma
269 | Model::Forma32GB
270 | Model::LibraH2O
271 | Model::Sage
272 | Model::Libra2
273 | Model::Clara2E
274 | Model::Elipsa2E
275 | Model::ClaraBW
276 | Model::ClaraColour
277 | Model::LibraColour => FrontlightKind::Premixed,
278 Model::AuraONE | Model::AuraONELimEd | Model::AuraH2OEd2V1 | Model::AuraH2OEd2V2 => {
279 FrontlightKind::Natural
280 }
281 _ => FrontlightKind::Standard,
282 }
283 }
284
285 pub fn has_natural_light(&self) -> bool {
286 self.frontlight_kind() != FrontlightKind::Standard
287 }
288
289 pub fn has_lightsensor(&self) -> bool {
290 matches!(self.model, Model::AuraONE | Model::AuraONELimEd)
291 }
292
293 pub fn has_gyroscope(&self) -> bool {
294 matches!(
295 self.model,
296 Model::Forma
297 | Model::Forma32GB
298 | Model::LibraH2O
299 | Model::Elipsa
300 | Model::Sage
301 | Model::Libra2
302 | Model::Elipsa2E
303 | Model::LibraColour
304 )
305 }
306
307 pub fn has_page_turn_buttons(&self) -> bool {
308 matches!(
309 self.model,
310 Model::Forma
311 | Model::Forma32GB
312 | Model::LibraH2O
313 | Model::Sage
314 | Model::Libra2
315 | Model::LibraColour
316 )
317 }
318
319 pub fn has_power_cover(&self) -> bool {
320 matches!(self.model, Model::Sage)
321 }
322
323 pub fn has_removable_storage(&self) -> bool {
324 matches!(
325 self.model,
326 Model::AuraH2O
327 | Model::Aura
328 | Model::AuraHD
329 | Model::Glo
330 | Model::TouchAB
331 | Model::TouchC
332 )
333 }
334
335 pub fn should_invert_buttons(&self, rotation: i8) -> bool {
336 let sr = self.startup_rotation();
337 let (_, dir) = self.mirroring_scheme();
338
339 rotation == (4 + sr - dir) % 4 || rotation == (4 + sr - 2 * dir) % 4
340 }
341
342 pub fn orientation(&self, rotation: i8) -> Orientation {
343 if self.should_swap_axes(rotation) {
344 Orientation::Portrait
345 } else {
346 Orientation::Landscape
347 }
348 }
349
350 pub fn mark(&self) -> u8 {
351 match self.model {
352 Model::LibraColour => 13,
353 Model::ClaraBW | Model::ClaraColour => 12,
354 Model::Elipsa2E => 11,
355 Model::Clara2E => 10,
356 Model::Libra2 => 9,
357 Model::Sage | Model::Elipsa => 8,
358 Model::Nia
359 | Model::LibraH2O
360 | Model::Forma32GB
361 | Model::Forma
362 | Model::ClaraHD
363 | Model::AuraH2OEd2V2
364 | Model::AuraEd2V2 => 7,
365 Model::AuraH2OEd2V1
366 | Model::AuraEd2V1
367 | Model::AuraONELimEd
368 | Model::AuraONE
369 | Model::Touch2
370 | Model::GloHD => 6,
371 Model::AuraH2O | Model::Aura => 5,
372 Model::AuraHD | Model::Mini | Model::Glo | Model::TouchC => 4,
373 Model::TouchAB => 3,
374 }
375 }
376
377 pub fn should_mirror_axes(&self, rotation: i8) -> (bool, bool) {
378 let (mxy, dir) = self.mirroring_scheme();
379 let mx = (4 + (mxy + dir)) % 4;
380 let my = (4 + (mxy - dir)) % 4;
381 let mirror_x = mxy == rotation || mx == rotation;
382 let mirror_y = mxy == rotation || my == rotation;
383 (mirror_x, mirror_y)
384 }
385
386 pub fn mirroring_scheme(&self) -> (i8, i8) {
388 match self.model {
389 Model::AuraH2OEd2V1 | Model::LibraH2O | Model::Libra2 => (3, 1),
390 Model::Sage => (0, 1),
391 Model::AuraH2OEd2V2 => (0, -1),
392 Model::Forma | Model::Forma32GB => (2, -1),
393 _ => (2, 1),
394 }
395 }
396
397 pub fn should_swap_axes(&self, rotation: i8) -> bool {
398 rotation % 2 == self.swapping_scheme()
399 }
400
401 pub fn swapping_scheme(&self) -> i8 {
402 match self.model {
403 Model::LibraH2O => 0,
404 _ => 1,
405 }
406 }
407
408 pub fn startup_rotation(&self) -> i8 {
411 match self.model {
412 Model::LibraH2O => 0,
413 Model::AuraH2OEd2V1
414 | Model::Forma
415 | Model::Forma32GB
416 | Model::Sage
417 | Model::Libra2
418 | Model::Elipsa2E
419 | Model::LibraColour => 1,
420 _ => 3,
421 }
422 }
423
424 pub fn to_canonical(&self, n: i8) -> i8 {
427 let (_, dir) = self.mirroring_scheme();
428 (4 + dir * (n - self.startup_rotation())) % 4
429 }
430
431 pub fn from_canonical(&self, n: i8) -> i8 {
434 let (_, dir) = self.mirroring_scheme();
435 (self.startup_rotation() + (4 + dir * n) % 4) % 4
436 }
437
438 pub fn transformed_rotation(&self, n: i8) -> i8 {
441 match self.model {
442 Model::AuraHD | Model::AuraH2O => n ^ 2,
443 Model::AuraH2OEd2V2 | Model::Forma | Model::Forma32GB => (4 - n) % 4,
444 _ => n,
445 }
446 }
447
448 pub fn transformed_gyroscope_rotation(&self, n: i8) -> i8 {
449 match self.model {
450 Model::LibraH2O => n ^ 1,
451 Model::Libra2 | Model::Sage | Model::Elipsa2E | Model::LibraColour => (6 - n) % 4,
452 Model::Elipsa => (4 - n) % 4,
453 _ => n,
454 }
455 }
456}
457
458lazy_static! {
459 pub static ref CURRENT_DEVICE: Device = {
460 let product = env::var("PRODUCT").unwrap_or_default();
461 let model_number = env::var("MODEL_NUMBER").unwrap_or_default();
462
463 Device::new(&product, &model_number)
464 };
465}
466
467#[cfg(test)]
468mod tests {
469 use super::Device;
470
471 #[test]
472 fn test_device_canonical_rotation() {
473 let forma = Device::new("frost", "377");
474 let aura_one = Device::new("daylight", "373");
475 for n in 0..4 {
476 assert_eq!(forma.from_canonical(forma.to_canonical(n)), n);
477 }
478 assert_eq!(aura_one.from_canonical(0), aura_one.startup_rotation());
479 assert_eq!(
480 forma.from_canonical(1) - forma.from_canonical(0),
481 aura_one.from_canonical(2) - aura_one.from_canonical(3)
482 );
483 }
484}