Skip to main content

display_types/
capabilities.rs

1/// A reference-counted, type-erased warning value.
2///
3/// Any type that implements [`core::error::Error`] + [`Send`] + [`Sync`] + `'static` can be
4/// wrapped in a `ParseWarning`. The built-in library variants use `EdidWarning`, but
5/// custom handlers may push their own error types without wrapping them in `EdidWarning`.
6///
7/// Using [`Arc`][crate::prelude::Arc] (rather than `Box`) means `ParseWarning` is
8/// [`Clone`], which lets warnings be copied from a parsed representation into
9/// [`DisplayCapabilities`] without consuming the parsed result.
10///
11/// To inspect a specific variant, use the inherent `downcast_ref` method available on
12/// `dyn core::error::Error + Send + Sync + 'static` in `std` builds:
13///
14/// ```text
15/// for w in caps.iter_warnings() {
16///     if let Some(ew) = (**w).downcast_ref::<EdidWarning>() { ... }
17/// }
18/// ```
19#[cfg(any(feature = "alloc", feature = "std"))]
20pub type ParseWarning = crate::prelude::Arc<dyn core::error::Error + Send + Sync + 'static>;
21
22/// Stereo viewing support decoded from DTD byte 17 bits 6, 5, and 0.
23#[non_exhaustive]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
26pub enum StereoMode {
27    /// Normal display; no stereo (bits 6–5 = `0b00`; bit 0 is don't-care).
28    #[default]
29    None,
30    /// Field-sequential stereo, right image when stereo sync = 1 (bits 6–5 = `0b01`, bit 0 = 0).
31    FieldSequentialRightFirst,
32    /// Field-sequential stereo, left image when stereo sync = 1 (bits 6–5 = `0b10`, bit 0 = 0).
33    FieldSequentialLeftFirst,
34    /// 2-way interleaved stereo, right image on even lines (bits 6–5 = `0b01`, bit 0 = 1).
35    TwoWayInterleavedRightEven,
36    /// 2-way interleaved stereo, left image on even lines (bits 6–5 = `0b10`, bit 0 = 1).
37    TwoWayInterleavedLeftEven,
38    /// 4-way interleaved stereo (bits 6–5 = `0b11`, bit 0 = 0).
39    FourWayInterleaved,
40    /// Side-by-side interleaved stereo (bits 6–5 = `0b11`, bit 0 = 1).
41    SideBySideInterleaved,
42}
43
44/// Sync signal definition decoded from DTD byte 17 bits 4–1.
45#[non_exhaustive]
46#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum SyncDefinition {
49    /// Analog composite sync (bit 4 = 0, bit 3 = 0).
50    AnalogComposite {
51        /// H-sync pulse present during V-sync (serrations).
52        serrations: bool,
53        /// Sync on all three RGB signals (`true`) or green only (`false`).
54        sync_on_all_rgb: bool,
55    },
56    /// Bipolar analog composite sync (bit 4 = 0, bit 3 = 1).
57    BipolarAnalogComposite {
58        /// H-sync pulse present during V-sync (serrations).
59        serrations: bool,
60        /// Sync on all three RGB signals (`true`) or green only (`false`).
61        sync_on_all_rgb: bool,
62    },
63    /// Digital composite sync on H-sync pin (bit 4 = 1, bit 3 = 0).
64    DigitalComposite {
65        /// H-sync pulse present during V-sync (serrations).
66        serrations: bool,
67        /// H-sync polarity outside V-sync: `true` = positive.
68        h_sync_positive: bool,
69    },
70    /// Digital separate sync (bit 4 = 1, bit 3 = 1).
71    DigitalSeparate {
72        /// V-sync polarity: `true` = positive.
73        v_sync_positive: bool,
74        /// H-sync polarity: `true` = positive.
75        h_sync_positive: bool,
76    },
77}
78
79/// A display video mode expressed as resolution, refresh rate, and scan type.
80#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
81#[derive(Debug, Clone, PartialEq, Default)]
82pub struct VideoMode {
83    /// Horizontal resolution in pixels.
84    pub width: u16,
85    /// Vertical resolution in pixels.
86    pub height: u16,
87    /// Refresh rate in Hz.
88    pub refresh_rate: u8,
89    /// `true` for interlaced modes; `false` for progressive (the common case).
90    pub interlaced: bool,
91    /// Horizontal front porch in pixels (0 when not decoded from a DTD).
92    pub h_front_porch: u16,
93    /// Horizontal sync pulse width in pixels (0 when not decoded from a DTD).
94    pub h_sync_width: u16,
95    /// Vertical front porch in lines (0 when not decoded from a DTD).
96    pub v_front_porch: u16,
97    /// Vertical sync pulse width in lines (0 when not decoded from a DTD).
98    pub v_sync_width: u16,
99    /// Horizontal border width in pixels on each side of the active area (0 when not from a DTD).
100    pub h_border: u8,
101    /// Vertical border height in lines on each side of the active area (0 when not from a DTD).
102    pub v_border: u8,
103    /// Stereo viewing support (default `None` for non-DTD modes).
104    pub stereo: StereoMode,
105    /// Sync signal definition (`None` for non-DTD modes).
106    pub sync: Option<SyncDefinition>,
107}
108
109/// EDID specification version and revision, decoded from base block bytes 18–19.
110///
111/// Most displays in use report version 1 with revision 3 or 4.
112#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
113#[derive(Debug, Clone, Copy, PartialEq, Eq)]
114pub struct EdidVersion {
115    /// EDID version number (byte 18). Always `1` for all current displays.
116    pub version: u8,
117    /// EDID revision number (byte 19).
118    pub revision: u8,
119}
120
121impl core::fmt::Display for EdidVersion {
122    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
123        write!(f, "{}.{}", self.version, self.revision)
124    }
125}
126
127/// Trait for typed data stored in [`DisplayCapabilities::extension_data`] by custom handlers.
128///
129/// A blanket implementation covers any type that is `Any + Debug + Send + Sync`, so consumers
130/// do not need to implement this trait manually — `#[derive(Debug)]` on a `Send + Sync` type
131/// is sufficient.
132#[cfg(any(feature = "alloc", feature = "std"))]
133pub trait ExtensionData: core::any::Any + core::fmt::Debug + Send + Sync {
134    /// Returns `self` as `&dyn Any` to enable downcasting.
135    fn as_any(&self) -> &dyn core::any::Any;
136}
137
138#[cfg(any(feature = "alloc", feature = "std"))]
139impl<T: core::any::Any + core::fmt::Debug + Send + Sync> ExtensionData for T {
140    fn as_any(&self) -> &dyn core::any::Any {
141        self
142    }
143}
144
145/// Consumer-facing display capability model produced by a display data parser.
146///
147/// All fields defined by the relevant specification are decoded and exposed here.
148/// No field is omitted because it appears obscure or unlikely to be needed — that
149/// judgement belongs to the consumer, not the library.
150///
151/// Fields are `Option` where the underlying data may be absent or undecodable.
152/// `None` means the value was not present or could not be reliably determined; it does
153/// not imply the field is unimportant. The library never invents or defaults data.
154#[non_exhaustive]
155#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
156#[derive(Debug, Clone, Default)]
157pub struct DisplayCapabilities {
158    /// Three-character PNP manufacturer ID (e.g. `GSM` for LG, `SAM` for Samsung).
159    pub manufacturer: Option<crate::manufacture::ManufacturerId>,
160    /// Manufacture date or model year.
161    pub manufacture_date: Option<crate::manufacture::ManufactureDate>,
162    /// EDID specification version and revision.
163    pub edid_version: Option<EdidVersion>,
164    /// Manufacturer-assigned product code.
165    pub product_code: Option<u16>,
166    /// Manufacturer-assigned serial number, if encoded numerically in the base block.
167    pub serial_number: Option<u32>,
168    /// Serial number string from the monitor serial number descriptor (`0xFF`), if present.
169    pub serial_number_string: Option<crate::manufacture::MonitorString>,
170    /// Human-readable display name from the monitor name descriptor, if present.
171    pub display_name: Option<crate::manufacture::MonitorString>,
172    /// Unspecified ASCII text strings from `0xFE` descriptors, in descriptor slot order.
173    ///
174    /// Up to four entries (one per descriptor slot). Each slot is `None` if the corresponding
175    /// descriptor was not a `0xFE` entry.
176    pub unspecified_text: [Option<crate::manufacture::MonitorString>; 4],
177    /// Additional white points from the `0xFB` descriptor.
178    ///
179    /// Up to two entries (the EDID `0xFB` descriptor has two fixed slots). Each slot is
180    /// `None` if the corresponding entry was unused (index byte `0x00`).
181    pub white_points: [Option<crate::color::WhitePoint>; 2],
182    /// `true` if the display uses a digital input interface.
183    pub digital: bool,
184    /// Color bit depth per primary channel.
185    /// `None` for analog displays or when the field is undefined or reserved.
186    pub color_bit_depth: Option<crate::color::ColorBitDepth>,
187    /// Physical display technology (e.g. TFT, OLED, PDP).
188    /// `None` when the Display Device Data Block is absent.
189    pub display_technology: Option<crate::panel::DisplayTechnology>,
190    /// Technology-specific sub-type code (raw, 0–15).
191    /// `None` when the Display Device Data Block is absent.
192    pub display_subtype: Option<u8>,
193    /// Panel operating mode (continuous or non-continuous refresh).
194    /// `None` when the Display Device Data Block is absent.
195    pub operating_mode: Option<crate::panel::OperatingMode>,
196    /// Backlight type.
197    /// `None` when the Display Device Data Block is absent.
198    pub backlight_type: Option<crate::panel::BacklightType>,
199    /// Whether the panel uses a Data Enable (DE) signal.
200    /// `None` when the Display Device Data Block is absent.
201    pub data_enable_used: Option<bool>,
202    /// Data Enable signal polarity: `true` = positive, `false` = negative.
203    /// Valid only when `data_enable_used` is `Some(true)`.
204    /// `None` when the Display Device Data Block is absent.
205    pub data_enable_positive: Option<bool>,
206    /// Native pixel format `(width_px, height_px)`.
207    /// `None` when the Display Device Data Block is absent or either dimension is zero.
208    pub native_pixels: Option<(u16, u16)>,
209    /// Panel aspect ratio encoded as `(AR − 1) × 100` (raw byte).
210    /// For example `77` represents approximately 16:9 (AR ≈ 1.77). `None` when the block is absent.
211    pub panel_aspect_ratio_100: Option<u8>,
212    /// Physical mounting orientation of the panel.
213    /// `None` when the Display Device Data Block is absent.
214    pub physical_orientation: Option<crate::panel::PhysicalOrientation>,
215    /// Panel rotation capability.
216    /// `None` when the Display Device Data Block is absent.
217    pub rotation_capability: Option<crate::panel::RotationCapability>,
218    /// Location of the zero (origin) pixel in the framebuffer.
219    /// `None` when the Display Device Data Block is absent.
220    pub zero_pixel_location: Option<crate::panel::ZeroPixelLocation>,
221    /// Fast-scan direction relative to H-sync.
222    /// `None` when the Display Device Data Block is absent.
223    pub scan_direction: Option<crate::panel::ScanDirection>,
224    /// Sub-pixel color filter arrangement.
225    /// `None` when the Display Device Data Block is absent.
226    pub subpixel_layout: Option<crate::panel::SubpixelLayout>,
227    /// Pixel pitch `(horizontal_hundredths_mm, vertical_hundredths_mm)` in 0.01 mm units.
228    /// `None` when the Display Device Data Block is absent or either pitch is zero.
229    pub pixel_pitch_hundredths_mm: Option<(u8, u8)>,
230    /// Pixel response time in milliseconds.
231    /// `None` when the Display Device Data Block is absent or the value is zero.
232    pub pixel_response_time_ms: Option<u8>,
233    /// Interface power sequencing timing parameters.
234    /// `None` when the Interface Power Sequencing Block is absent.
235    pub power_sequencing: Option<crate::panel::PowerSequencing>,
236    /// Display luminance transfer function.
237    /// `None` when the Transfer Characteristics Block is absent.
238    #[cfg(any(feature = "alloc", feature = "std"))]
239    pub transfer_characteristic: Option<crate::transfer::DisplayIdTransferCharacteristic>,
240    /// Physical display interface capabilities.
241    /// `None` when the Display Interface Data Block is absent.
242    pub display_id_interface: Option<crate::panel::DisplayIdInterface>,
243    /// Stereo display interface parameters.
244    /// `None` when the Stereo Display Interface Data Block is absent.
245    pub stereo_interface: Option<crate::panel::DisplayIdStereoInterface>,
246    /// Tiled display topology.
247    /// `None` when the Tiled Display Topology Data Block is absent.
248    pub tiled_topology: Option<crate::panel::DisplayIdTiledTopology>,
249    /// CIE xy chromaticity coordinates for the color primaries and white point.
250    pub chromaticity: crate::color::Chromaticity,
251    /// Display gamma. `None` if the display did not specify a gamma value.
252    pub gamma: Option<crate::color::DisplayGamma>,
253    /// Display feature support flags.
254    pub display_features: Option<crate::features::DisplayFeatureFlags>,
255    /// Supported color encoding formats. Only populated for EDID 1.4+ digital displays.
256    pub digital_color_encoding: Option<crate::color::DigitalColorEncoding>,
257    /// Color type for analog displays; `None` for the undefined value (`0b11`).
258    pub analog_color_type: Option<crate::color::AnalogColorType>,
259    /// Video interface type.
260    /// `None` for analog displays or when the field is undefined or reserved.
261    pub video_interface: Option<crate::input::VideoInterface>,
262    /// Analog sync and video white levels. Only populated for analog displays.
263    pub analog_sync_level: Option<crate::input::AnalogSyncLevel>,
264    /// Physical screen dimensions or aspect ratio.
265    /// `None` when both bytes are zero (undefined).
266    pub screen_size: Option<crate::screen::ScreenSize>,
267    /// Minimum supported vertical refresh rate in Hz.
268    pub min_v_rate: Option<u16>,
269    /// Maximum supported vertical refresh rate in Hz.
270    pub max_v_rate: Option<u16>,
271    /// Minimum supported horizontal scan rate in kHz.
272    pub min_h_rate_khz: Option<u16>,
273    /// Maximum supported horizontal scan rate in kHz.
274    pub max_h_rate_khz: Option<u16>,
275    /// Maximum pixel clock in MHz.
276    pub max_pixel_clock_mhz: Option<u16>,
277    /// Physical image area dimensions in millimetres `(width_mm, height_mm)`.
278    ///
279    /// More precise than [`screen_size`][Self::screen_size] (which is in cm).
280    /// `None` when all DTD image-size fields are zero.
281    pub preferred_image_size_mm: Option<(u16, u16)>,
282    /// Video timing formula reported in the display range limits descriptor.
283    pub timing_formula: Option<crate::timing::TimingFormula>,
284    /// DCM polynomial coefficients.
285    pub color_management: Option<crate::color::ColorManagementData>,
286    /// Video modes decoded from the display data.
287    #[cfg(any(feature = "alloc", feature = "std"))]
288    pub supported_modes: crate::prelude::Vec<VideoMode>,
289    /// Non-fatal conditions collected from the parser and all handlers.
290    ///
291    /// Not serialized — use a custom handler to map warnings to a serializable form.
292    #[cfg(any(feature = "alloc", feature = "std"))]
293    #[cfg_attr(feature = "serde", serde(skip))]
294    pub warnings: crate::prelude::Vec<ParseWarning>,
295    /// Typed data attached by extension handlers, keyed by extension tag byte.
296    ///
297    /// Uses a `Vec` of `(tag, data)` pairs rather than a `HashMap` so that this field is
298    /// available in `alloc`-only (no_std) builds. The number of distinct extension tags in
299    /// any real EDID is small enough that linear scan is negligible.
300    ///
301    /// Not serialized — use a custom handler to map this to a serializable form.
302    #[cfg(any(feature = "alloc", feature = "std"))]
303    #[cfg_attr(feature = "serde", serde(skip))]
304    pub extension_data: crate::prelude::Vec<(u8, crate::prelude::Arc<dyn ExtensionData>)>,
305}
306
307#[cfg(any(feature = "alloc", feature = "std"))]
308impl DisplayCapabilities {
309    /// Returns an iterator over all collected warnings.
310    pub fn iter_warnings(&self) -> impl Iterator<Item = &ParseWarning> {
311        self.warnings.iter()
312    }
313
314    /// Appends a warning, wrapping it in a [`ParseWarning`].
315    pub fn push_warning(&mut self, w: impl core::error::Error + Send + Sync + 'static) {
316        self.warnings.push(crate::prelude::Arc::new(w));
317    }
318
319    /// Store typed data from a handler, keyed by an extension tag.
320    /// Replaces any previously stored entry for the same tag.
321    pub fn set_extension_data<T: ExtensionData>(&mut self, tag: u8, data: T) {
322        if let Some(entry) = self.extension_data.iter_mut().find(|(t, _)| *t == tag) {
323            entry.1 = crate::prelude::Arc::new(data);
324        } else {
325            self.extension_data
326                .push((tag, crate::prelude::Arc::new(data)));
327        }
328    }
329
330    /// Retrieve typed data previously stored by a handler for the given tag.
331    /// Returns `None` if no data is stored for the tag or the type does not match.
332    pub fn get_extension_data<T: core::any::Any>(&self, tag: u8) -> Option<&T> {
333        self.extension_data
334            .iter()
335            .find(|(t, _)| *t == tag)
336            // `**data` deref-chains through `&` then through Arc's Deref to reach
337            // `dyn ExtensionData`, forcing vtable dispatch for `as_any()`.
338            // Calling `.as_any()` on `&Arc<dyn ExtensionData>` would hit the blanket
339            // `ExtensionData` impl for Arc itself and return the wrong TypeId.
340            .and_then(|(_, data)| (**data).as_any().downcast_ref::<T>())
341    }
342}