cadmus_core/
device.rs

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    // Returns the center and direction of the mirroring pattern.
387    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    // The written rotation that makes the screen be in portrait mode
409    // with the Kobo logo at the bottom.
410    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    // Return a device independent rotation value given
425    // the device dependent written rotation value *n*.
426    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    // Return a device dependent written rotation value given
432    // the device independent rotation value *n*.
433    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    // Return a device dependent written rotation value given
439    // the device dependent read rotation value *n*.
440    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}