Skip to main content

display_types/
screen.rs

1/// Physical screen dimensions or aspect ratio, decoded from EDID base block bytes `0x15`–`0x16`.
2///
3/// The two bytes encode one of three things depending on which are zero:
4///
5/// | `0x15` | `0x16` | Meaning                           |
6/// |--------|--------|-----------------------------------|
7/// | non-0  | non-0  | Physical width × height in cm     |
8/// | non-0  | 0      | Landscape aspect ratio            |
9/// | 0      | non-0  | Portrait aspect ratio             |
10/// | 0      | 0      | Undefined — `None` on the field   |
11#[non_exhaustive]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum ScreenSize {
15    /// Physical screen dimensions. Values are in centimetres (1–255 cm per axis).
16    Physical {
17        /// Horizontal screen size in centimetres.
18        width_cm: u8,
19        /// Vertical screen size in centimetres.
20        height_cm: u8,
21    },
22    /// Landscape aspect ratio (width ÷ height > 1), encoded as a raw EDID byte.
23    ///
24    /// Call [`landscape_ratio`][Self::landscape_ratio] for the computed `f32` value.
25    Landscape(u8),
26    /// Portrait aspect ratio (width ÷ height < 1), encoded as a raw EDID byte.
27    ///
28    /// Call [`portrait_ratio`][Self::portrait_ratio] for the computed `f32` value.
29    Portrait(u8),
30}
31
32impl ScreenSize {
33    /// Returns the landscape aspect ratio (width ÷ height) for a `Landscape` variant.
34    ///
35    /// Formula: `(raw + 99) / 100`. Range: 1.00 → 3.54.
36    /// Returns `None` for other variants.
37    pub fn landscape_ratio(&self) -> Option<f32> {
38        if let Self::Landscape(v) = self {
39            Some((*v as f32 + 99.0) / 100.0)
40        } else {
41            None
42        }
43    }
44
45    /// Returns the portrait aspect ratio (width ÷ height) for a `Portrait` variant.
46    ///
47    /// Formula: `100 / (raw + 99)`. Range: 0.28 → 0.99.
48    /// Returns `None` for other variants.
49    pub fn portrait_ratio(&self) -> Option<f32> {
50        if let Self::Portrait(v) = self {
51            Some(100.0 / (*v as f32 + 99.0))
52        } else {
53            None
54        }
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn landscape_ratio_correct_formula() {
64        // raw=1: (1 + 99) / 100 = 1.00
65        assert_eq!(ScreenSize::Landscape(1).landscape_ratio(), Some(1.00));
66        // raw=100: (100 + 99) / 100 = 1.99
67        assert!((ScreenSize::Landscape(100).landscape_ratio().unwrap() - 1.99).abs() < 1e-5);
68    }
69
70    #[test]
71    fn landscape_ratio_none_for_other_variants() {
72        assert_eq!(
73            ScreenSize::Physical {
74                width_cm: 60,
75                height_cm: 34
76            }
77            .landscape_ratio(),
78            None
79        );
80        assert_eq!(ScreenSize::Portrait(1).landscape_ratio(), None);
81    }
82
83    #[test]
84    fn portrait_ratio_correct_formula() {
85        // raw=1: 100 / (1 + 99) = 1.00
86        assert_eq!(ScreenSize::Portrait(1).portrait_ratio(), Some(1.00));
87        // raw=100: 100 / (100 + 99) = 100 / 199 ≈ 0.50251
88        let r = ScreenSize::Portrait(100).portrait_ratio().unwrap();
89        assert!((r - 100.0 / 199.0).abs() < 1e-5);
90    }
91
92    #[test]
93    fn portrait_ratio_none_for_other_variants() {
94        assert_eq!(
95            ScreenSize::Physical {
96                width_cm: 60,
97                height_cm: 34
98            }
99            .portrait_ratio(),
100            None
101        );
102        assert_eq!(ScreenSize::Landscape(1).portrait_ratio(), None);
103    }
104}