edid_rs/
lib.rs

1#![cfg_attr(feature = "no_std", no_std)]
2
3//! A pure-Rust crate to parse EDID data with `no_std` support. This crate does not include methods for gathering the data from the monitor.
4//! 
5//! To enable `no_std` support, ensure the `alloc` crate is available, use feature `no_std`, and then implement `edid_rs::Read` instead of `std::io::Read` for data sources.
6//! 
7//! ### Examples
8//! 
9//! Basic usage:
10//! ```rust
11//! extern crate edid_rs;
12//! 
13//! use std::io::Cursor;
14//! 
15//! fn main() {
16//!     let bytes = vec![...];
17//!     println!("{:?}",
18//!         edid_rs::parse(&mut Cursor::new(bytes))
19//!     );
20//! }
21//! ```
22//! 
23//! Reading current monitor EDID on OSX:
24//! ```text
25//! $ ioreg -l -w0 -d0 -r -c AppleBacklightDisplay | grep IODisplayEDID - | tail -c 258 | head -c 256 | xxd -r -p | cargo run --example stdin
26//!    Compiling edid-rs v0.1.0 (../edid)
27//!     Finished dev [unoptimized + debuginfo] target(s) in 0.39s
28//!      Running `target/debug/examples/stdin`
29//! Ok(EDID { product: ProductInformation { manufacturer_id: ManufacturerID('\u{4}', '\u{0}', '\u{6}'), product_code: 40994, serial_number: 0, manufacture_date: ManufactureDate { week: 4, year: 2013 } }, version: Version { version: 1, revision: 4 }, display: DisplayParameters { input: Digital { dfp_compatible: true }, max_size: Some(ImageSize { width: 33.0, height: 21.0 }), gamma: Some(2.2), dpms: DPMSFeatures { standby_supported: false, suspend_supported: false, low_power_supported: false, display_type: Monochrome, default_srgb: false, preferred_timing_mode: true, default_gtf_supported: false } }, color: ColorCharacteristics { red: (0.6533203, 0.33398438), green: (0.2998047, 0.6201172), blue: (0.14648438, 0.049804688), white: (0.3125, 0.32910156), white_points: [] }, timings: Timings { established_timings: [], standard_timings: [], detailed_timings: [DetailedTiming { pixel_clock: 337750000, active: (2880, 1800), front_porch: (48, 3), sync_length: (32, 6), back_porch: (80, 43), image_size: ImageSize { width: 33.1, height: 20.7 }, border: (0, 0), interlaced: false, stereo: None, sync_type: Seperate { horizontal: Positive, vertical: Negative } }] }, descriptors: MonitorDescriptors([MonitorName("Color LCD"), ManufacturerDefined(0, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0])]), extensions: 0 })
30//! ```
31
32/// Trait which all data sources must implement. In a `std` environment,
33/// there is a blanket impl of `edid_rs::Read` for `std::io::Read`.
34pub trait Read {
35    fn read(&mut self, buf: &mut [u8]) -> Option<usize>;
36}
37
38#[cfg(not(feature = "no_std"))]
39impl<T: std::io::Read> Read for T {
40    fn read(&mut self, buf: &mut [u8]) -> Option<usize> {
41        self.read(buf).ok()
42    }
43}
44
45#[cfg(feature = "no_std")]
46#[macro_use]
47extern crate alloc;
48#[cfg(feature = "no_std")]
49use alloc::{vec::Vec, string::String};
50
51
52/// The type of parsing results.
53pub type Result<T> = core::result::Result<T, &'static str>;
54
55// Like `assert!` but returning Err instead of panicking.
56fn ensure(pred: bool, msg: &'static str) -> Result<()> {
57    if pred {
58        Ok(())
59    } else {
60        Err(msg)
61    }
62}
63
64/// Used to parse the binary data from a Read value.
65pub struct Reader<'a> {
66    // The source we are reading from,
67    value: &'a mut dyn Read,
68    // and a 128-byte buffer of data.
69    buffer: Vec<u8>
70}
71
72impl<'a> Reader<'a> {
73    pub fn new<T: Read>(value: &'a mut T) -> Reader<'a> {
74        Reader {
75            value: value as &mut dyn Read, buffer: Vec::with_capacity(128)
76        }
77    }
78
79    // Get one character from the input.
80    fn get(&mut self) -> Result<u8> {
81        if self.buffer.len() == 0 {
82            self.buffer.resize(128, 0);
83            let num = self.value.read(self.buffer.as_mut()).ok_or("Error reading data!")?;
84            self.buffer.truncate(num);
85        }
86
87        if self.buffer.len() > 0 {
88            Ok(self.buffer.remove(0))
89        } else{
90            Err("Unexpectedly out of data!")
91        }
92    }
93
94    fn read_u8(&mut self) -> Result<u8> {
95        self.get()
96    }
97
98    // Both this and `read_u32` are little-endian.
99    fn read_u16(&mut self) -> Result<u16> {
100        Ok((self.read_u8()? as u16) | ((self.read_u8()? as u16) << 8))
101    }
102
103    fn read_u32(&mut self) -> Result<u32> {
104        Ok((self.read_u16()? as u32) | ((self.read_u16()? as u32) << 16))
105    }
106}
107
108/// The EDID information block.
109#[derive(Debug, Clone)]
110pub struct EDID {
111    /// Product version information.
112    pub product: ProductInformation,
113    /// EDID specification version.
114    pub version: Version,
115    /// Display characteristic parameters.
116    pub display: DisplayParameters,
117    /// Color calibration parameters.
118    pub color: ColorCharacteristics,
119    /// Accepted timing modes.
120    pub timings: Timings,
121    /// Extra monitor information.
122    pub descriptors: MonitorDescriptors,
123    /// Number of extensions following the EDID block.
124    pub extensions: u8,
125}
126
127impl EDID {
128    pub fn parse(r: &mut Reader) -> Result<EDID> {
129        ensure(r.read_u32()? == 0xffffff00, "Invalid header.")?;
130        ensure(r.read_u32()? == 0x00ffffff, "Invalid header.")?;
131        
132        // Parse the different parts of the data,
133        let product = ProductInformation::parse(r)?;
134        let version = Version::parse(r)?;
135        let display = DisplayParameters::parse(r)?;
136        let mut color = ColorCharacteristics::parse(r)?;
137        let mut timings = Timings::parse(r)?;
138        let (descriptors, mut detailed_timings, mut standard_timings, mut white) = MonitorDescriptors::parse(r)?;
139
140        // And do a little rearranging of the monitor descriptors to 
141        // put the timing information all in one place.
142        color.white_points.append(&mut white);
143        timings.detailed_timings.append(&mut detailed_timings);
144        timings.standard_timings.append(&mut standard_timings);
145
146        // Finish by reading how many extensions should follow this data.
147        // We do not attempt to parse these in any way.
148        let extensions = r.read_u8()?;
149
150        Ok(EDID {
151            product, version, display, color, timings, descriptors, extensions
152        })
153    }
154}
155
156/// Information about the product and its manufacture.
157#[derive(Debug, Clone)]
158pub struct ProductInformation {
159    pub manufacturer_id: ManufacturerID,
160    pub product_code: u16,
161    pub serial_number: u32,
162    pub manufacture_date: ManufactureDate
163}
164
165impl ProductInformation {
166    fn parse(r: &mut Reader) -> Result<ProductInformation> {
167        let manufacturer_id = ManufacturerID::parse(r)?;
168        let product_code = r.read_u16()?;
169        let serial_number = r.read_u32()?;
170        let manufacture_date = ManufactureDate::parse(r)?;
171
172        Ok(ProductInformation {
173            manufacturer_id, product_code, serial_number, manufacture_date
174        })
175    }
176}
177
178/// Three character manufacturer ID.
179#[derive(Debug, Clone, Copy)]
180pub struct ManufacturerID(pub char, pub char, pub char);
181
182impl ManufacturerID {
183    fn parse(r: &mut Reader) -> Result<ManufacturerID> {
184        // The manufacturer ID is stored as three 5-bit
185        // characters in a 16-bit little endian field.
186        let k = r.read_u16()?;
187        let c1 = ((k & 0b0111110000000000) >> 10) as u8;
188        let c2 = ((k & 0b0000001111100000) >> 05) as u8;
189        let c3 = ((k & 0b0000000000011111) >> 00) as u8;
190        Ok(ManufacturerID(c1 as char, c2 as char, c3 as char))
191    }
192}
193
194/// Gregorian calendar date of manufacture, all years are CE.
195#[derive(Debug, Clone, Copy)]
196pub struct ManufactureDate {
197    pub week: u8,
198    pub year: u16
199}
200
201impl ManufactureDate {
202    fn parse(r: &mut Reader) -> Result<ManufactureDate> {
203        let week = r.read_u8()?;
204        let year = r.read_u8()? as u16 + 1990;
205
206        Ok(ManufactureDate { week, year })
207    }
208}
209
210/// EDID specification version.
211#[derive(Debug, Clone, Copy)]
212pub struct Version {
213    pub version: u8,
214    pub revision: u8
215}
216
217impl Version {
218    fn parse(r: &mut Reader) -> Result<Version> {
219        let version = r.read_u8()?;
220        let revision = r.read_u8()?;
221
222        Ok(Version { version, revision })
223    }
224}
225
226/// Information about the display hardware.
227#[derive(Debug, Clone)]
228pub struct DisplayParameters {
229    pub input: VideoInput,
230    /// The maximum size of the image on the monitor.
231    pub max_size: Option<ImageSize>,
232    /// The display's gamma factor.
233    pub gamma: Option<f32>,
234    /// DPMS feature support.
235    pub dpms: DPMSFeatures
236}
237
238impl DisplayParameters {
239    fn parse(r: &mut Reader) -> Result<DisplayParameters> {
240        let input = VideoInput::parse(r)?;
241        let max_width = r.read_u8()?;
242        let max_height = r.read_u8()?;
243
244        let max_size = if max_width == 0 || max_height == 0 {
245            None
246        } else {
247            Some(ImageSize {
248                width: max_width as f32,
249                height: max_height as f32
250            })
251        };
252
253        let gamma_val = r.read_u8()?;
254        let gamma = if gamma_val == 0xff {
255            None
256        } else {
257            Some((gamma_val as f32 + 100.0) / 100.0)
258        };
259
260        let dpms = DPMSFeatures::parse(r)?;
261
262        Ok(DisplayParameters { input, max_size, gamma, dpms })
263    }   
264}
265
266/// Describes the format of the monitors video input.
267#[derive(Debug, Clone, Copy)]
268pub enum VideoInput {
269    Analog {
270        /// The video signal voltages.
271        signal_level: SignalLevel,
272        /// Whether a blank-to-black setup is expected.
273        setup_expected: bool,
274        /// Which sync signals the monitor supports.
275        supported_sync: SupportedSync
276    },
277    Digital {
278        /// Compatible with VESA DFP 1.x
279        dfp_compatible: bool
280    }
281}
282
283impl VideoInput {
284    fn parse(r: &mut Reader) -> Result<VideoInput> {
285        let val = r.read_u8()?;
286        if val & (1 << 7) == 0 {
287            let signal_level = match (val & 0b01100000) >> 5 {
288                0 => SignalLevel { high: 0.700, low: 0.300 },
289                1 => SignalLevel { high: 0.714, low: 0.286 },
290                2 => SignalLevel { high: 1.000, low: 0.400 },
291                3 => SignalLevel { high: 0.700, low: 0.000 },
292                _ => unreachable!()
293            };
294            let setup_expected = val & (1 << 4) > 0;
295            let supported_sync = SupportedSync {
296                serrated_vsync: val & (1 << 3) > 0,
297                sync_on_green: val & (1 << 2) > 0,
298                composite_sync: val & (1 << 1) > 0,
299                seperate_sync: val & (1 << 0) > 0
300            };
301            Ok(VideoInput::Analog { signal_level, setup_expected, supported_sync })
302        } else {
303            Ok(VideoInput::Digital { dfp_compatible: val & 1 > 0 })
304        }
305    }
306}
307
308/// Gives the minimum and maximum voltages on the video lines.
309#[derive(Debug, Clone, Copy)]
310pub struct SignalLevel {
311    pub high: f32,
312    pub low: f32
313}
314
315/// Describes what sync signals the monitor accepts.
316#[derive(Debug, Clone, Copy)]
317pub struct SupportedSync {
318    /// HSync during VSync
319    pub serrated_vsync: bool,
320    /// Sync on just green line or RGB.
321    pub sync_on_green: bool,
322    /// Sync on HSync line
323    pub composite_sync: bool,
324    /// Seperate sync signals supported.
325    pub seperate_sync: bool
326}
327
328/// Image size specified in centimetres.
329#[derive(Debug, Clone, Copy)]
330pub struct ImageSize {
331    pub width: f32,
332    pub height: f32
333}
334
335/// DPMS features supported by the display.
336#[derive(Debug, Clone)]
337pub struct DPMSFeatures {
338    pub standby_supported: bool,
339    pub suspend_supported: bool,
340    pub low_power_supported: bool,
341    pub display_type: DisplayType,
342    pub default_srgb: bool,
343    /// If set, the preferred timing mode is specified
344    /// in the first detailed timing block. 
345    pub preferred_timing_mode: bool,
346    /// If set, all timings from the standard GTF will work.
347    pub default_gtf_supported: bool
348}
349
350impl DPMSFeatures {
351    fn parse(r: &mut Reader) -> Result<DPMSFeatures> {
352        let val = r.read_u8()?;
353
354        Ok(DPMSFeatures {
355            standby_supported: val & (1 << 7) > 0,
356            suspend_supported: val & (1 << 6) > 0,
357            low_power_supported: val & (1 << 5) > 0,
358            display_type: match (val & 0b00011000) >> 3 {
359                0 => DisplayType::Monochrome,
360                1 => DisplayType::RGBColor,
361                2 => DisplayType::OtherColor,
362                3 => DisplayType::Undefined,
363                _ => unreachable!()
364            },
365            default_srgb: val & (1 << 2) > 0,
366            preferred_timing_mode: val & (1 << 1) > 0,
367            default_gtf_supported: val & (1 << 0) > 0
368        })
369    }
370}
371
372/// The type of display.
373#[derive(Debug, Clone, Copy)]
374pub enum DisplayType {
375    Monochrome,
376    RGBColor,
377    OtherColor,
378    Undefined
379}
380
381/// Color chromaticity coordinates expressed as CIE 1931 x, y coordinates,
382/// as well as additional white points given in the monitor descriptors.
383#[derive(Debug, Clone)]
384pub struct ColorCharacteristics {
385    pub red: (f32, f32),
386    pub green: (f32, f32),
387    pub blue: (f32, f32),
388    pub white: (f32, f32),
389    pub white_points: Vec<WhitePoint>
390}
391
392impl ColorCharacteristics {
393    fn parse(r: &mut Reader) -> Result<ColorCharacteristics> {
394        let rg_low = r.read_u8()? as u16;
395        let bw_low = r.read_u8()? as u16;
396        let rh_x = r.read_u8()? as u16;
397        let rh_y = r.read_u8()? as u16;
398        let gh_x = r.read_u8()? as u16;
399        let gh_y = r.read_u8()? as u16;
400        let bh_x = r.read_u8()? as u16;
401        let bh_y = r.read_u8()? as u16;
402        let wh_x = r.read_u8()? as u16;
403        let wh_y = r.read_u8()? as u16;
404
405        let red_x = (rh_x << 2 | (rg_low & 0b11000000) >> 6) as f32 / 1024.0;
406        let red_y = (rh_y << 2 | (rg_low & 0b00110000) >> 4) as f32 / 1024.0;
407        let green_x = (gh_x << 2 | (rg_low & 0b00001100) >> 2) as f32 / 1024.0;
408        let green_y = (gh_y << 2 | (rg_low & 0b00000011) >> 0) as f32 / 1024.0;
409        let blue_x = (bh_x << 2 | (bw_low & 0b11000000) >> 6) as f32 / 1024.0;
410        let blue_y = (bh_y << 2 | (bw_low & 0b00110000) >> 4) as f32 / 1024.0;
411        let white_x = (wh_x << 2 | (bw_low & 0b00001100) >> 2) as f32 / 1024.0;
412        let white_y = (wh_y << 2 | (bw_low & 0b00000011) >> 0) as f32 / 1024.0;
413
414        Ok(ColorCharacteristics {
415            red: (red_x, red_y),
416            green: (green_x, green_y),
417            blue: (blue_x, blue_y),
418            white: (white_x, white_y),
419            white_points: Vec::new()
420        })
421    }
422}
423
424/// A single white point for the display, with x and y
425/// chromaticity coordinates given in the CIE 1931 space.
426#[derive(Debug, Clone, Copy)]
427pub struct WhitePoint {
428    pub index: u8,
429    pub x: f32,
430    pub y: f32,
431    pub gamma: f32
432}
433
434/// The timing modes accepted by the display.
435#[derive(Debug, Clone)]
436pub struct Timings {
437    /// The timings supported from the VESA 'established timing' list.
438    pub established_timings: Vec<EstablishedTiming>,
439    /// Standard timings given that can be derived from the GTF.
440    pub standard_timings: Vec<StandardTiming>,
441    /// Detailed timings specific to the display. If it exists, the first
442    /// detailed timing is the preferred timing.
443    pub detailed_timings: Vec<DetailedTiming>
444}
445
446impl Timings {
447    fn parse(r: &mut Reader) -> Result<Timings> {
448        let mut ft = r.read_u16()?;
449
450        let mut established_timings = Vec::new();
451        if ft & 1 > 0 {
452            established_timings.push(EstablishedTiming::H800V600F60);
453        }
454        ft >>= 1;
455        if ft & 1 > 0 {
456            established_timings.push(EstablishedTiming::H800V600F56);
457        }
458        ft >>= 1;
459        if ft & 1 > 0 {
460            established_timings.push(EstablishedTiming::H640V480F75);
461        }
462        ft >>= 1;
463        if ft & 1 > 0 {
464            established_timings.push(EstablishedTiming::H640V480F72);
465        }
466        ft >>= 1;
467        if ft & 1 > 0 {
468            established_timings.push(EstablishedTiming::H640V480F67);
469        }
470        ft >>= 1;
471        if ft & 1 > 0 {
472            established_timings.push(EstablishedTiming::H640V480F60);
473        }
474        ft >>= 1;
475        if ft & 1 > 0 {
476            established_timings.push(EstablishedTiming::H720V400F88);
477        }
478        ft >>= 1;
479        if ft & 1 > 0 {
480            established_timings.push(EstablishedTiming::H720V400F70);
481        }
482        ft >>= 1;
483        if ft & 1 > 0 {
484            established_timings.push(EstablishedTiming::H1280V1024F75);
485        }
486        ft >>= 1;
487        if ft & 1 > 0 {
488            established_timings.push(EstablishedTiming::H1024V768F75);
489        }
490        ft >>= 1;
491        if ft & 1 > 0 {
492            established_timings.push(EstablishedTiming::H1024V768F70);
493        }
494        ft >>= 1;
495        if ft & 1 > 0 {
496            established_timings.push(EstablishedTiming::H1024V768F60);
497        }
498        ft >>= 1;
499        if ft & 1 > 0 {
500            established_timings.push(EstablishedTiming::H1024V768F87);
501        }
502        ft >>= 1;
503        if ft & 1 > 0 {
504            established_timings.push(EstablishedTiming::H832V624F75);
505        }
506        ft >>= 1;
507        if ft & 1 > 0 {
508            established_timings.push(EstablishedTiming::H800V600F75);
509        }
510        ft >>= 1;
511        if ft & 1 > 0 {
512            established_timings.push(EstablishedTiming::H800V600F72);
513        }
514
515        let ft = r.read_u8()?;
516        if ft & (1 << 7) > 0 {
517            established_timings.push(EstablishedTiming::H1152V870F75);
518        }
519
520        let mut standard_timings = Vec::new();
521        for _ in 0..8 {
522            let low = r.read_u8()?;
523            let high = r.read_u8()?;
524            if low == 1 && high == 1 {
525                continue;
526            } else {
527                standard_timings.push(StandardTiming {
528                    horizontal_resolution: (low as u16 + 31) * 8,
529                    aspect_ratio: match high >> 6 {
530                        0 => 16.0/10.0,
531                        1 => 4.0/3.0,
532                        2 => 5.0/4.0,
533                        3 => 16.0/9.0,
534                        _ => unreachable!()
535                    },
536                    refresh_rate: (high & 0b00111111) + 60
537                });
538            }
539        }
540
541        let detailed_timings = Vec::new();
542
543        Ok(Timings { established_timings, standard_timings, detailed_timings })
544    }
545}
546
547/// The 'established timings' specified by VESA.
548#[derive(Debug, Clone, Copy)]
549pub enum EstablishedTiming {
550    H720V400F70,
551    H720V400F88,
552    H640V480F60,
553    H640V480F67,
554    H640V480F72,
555    H640V480F75,
556    H800V600F56,
557    H800V600F60,
558    H800V600F72,
559    H800V600F75,
560    H832V624F75,
561    H1024V768F87,
562    H1024V768F60,
563    H1024V768F70,
564    H1024V768F75,
565    H1280V1024F75,
566    H1152V870F75
567}
568
569/// A standard timing which contains enough information to derive the
570/// other parameters from the GTF.
571#[derive(Debug, Clone, Copy)]
572pub struct StandardTiming {
573    pub horizontal_resolution: u16,
574    pub aspect_ratio: f32,
575    pub refresh_rate: u8
576}
577
578/// A non-standard timing with all parameters specified.
579#[derive(Debug, Clone)]
580pub struct DetailedTiming {
581    /// Given in Hz
582    pub pixel_clock: u32,
583    /// Active area in pixels.
584    pub active: (u16, u16),
585    /// Length of front porch in pixels and lines.
586    pub front_porch: (u16, u16),
587    /// Length of sync pulse in pixels and lines.
588    pub sync_length: (u16, u16),
589    /// Length of back porch in pixels and lines.
590    pub back_porch: (u16, u16),
591    /// Image size in centimetres.
592    pub image_size: ImageSize,
593    /// Border size in pixels.
594    pub border: (u16, u16),
595    pub interlaced: bool,
596    pub stereo: StereoType,
597    pub sync_type: SyncType
598}
599
600impl DetailedTiming {
601    fn parse(r: &mut Reader) -> Result<Option<DetailedTiming>> {
602        let pixel_clock = r.read_u16()? as u32 * 10000;
603        let ha_low = r.read_u8()? as u16;
604
605        if pixel_clock == 0 || ha_low == 0 {
606            return Ok(None);
607        }
608
609        let hb_low = r.read_u8()? as u16;
610        let h_high = r.read_u8()? as u16;
611
612        let horizontal_active = ha_low | (((h_high & 0xf0) >> 4) << 8);
613        let horizontal_blanking = hb_low | (((h_high & 0x0f) >> 0) << 8);
614
615        let va_low = r.read_u8()? as u16;
616        let vb_low = r.read_u8()? as u16;
617        let v_high = r.read_u8()? as u16;
618
619        let vertical_active = va_low | (((v_high & 0xf0) >> 4) << 8);
620        let vertical_blanking = vb_low | (((v_high & 0x0f) >> 0) << 8);
621
622        let hso_low = r.read_u8()? as u16;
623        let hsw_low = r.read_u8()? as u16;
624        let vs_low = r.read_u8()? as u16;
625        let hvs_high = r.read_u8()? as u16;
626
627        let hso_high = (hvs_high & 0b11000000) >> 6;
628        let hsw_high = (hvs_high & 0b00110000) >> 4;
629        let vso_high = (hvs_high & 0b00001100) >> 2;
630        let vsw_high = (hvs_high & 0b00000011) >> 0;
631        let vso_low = (vs_low & 0xf0) >> 4;
632        let vsw_low = (vs_low & 0x0f) >> 0;
633        let vertical_front_porch = vso_low | (vso_high << 4);
634        let horizontal_front_porch = hso_low | (hso_high << 8);
635        let vertical_sync_width = vsw_low | (vsw_high << 4);
636        let horizontal_sync_width = hsw_low | (hsw_high << 8);
637        let active = (horizontal_active, vertical_active);
638        let front_porch = (horizontal_front_porch, vertical_front_porch);
639        let sync_length = (horizontal_sync_width, vertical_sync_width);
640        let back_porch = (
641            horizontal_blanking - horizontal_sync_width - horizontal_front_porch,
642            vertical_blanking - vertical_sync_width - vertical_front_porch
643        );
644
645        let hs_low = r.read_u8()? as u16;
646        let vs_low = r.read_u8()? as u16;
647        let s_high = r.read_u8()? as u16;
648        
649        let h_size = hs_low | ((s_high & 0xf0) >> 4) << 8;
650        let v_size = vs_low | ((s_high & 0x0f) >> 0) << 8;
651        let image_size = ImageSize { width: (h_size as f32) / 10.0, height: (v_size as f32) / 10.0 };
652
653        let hb = r.read_u8()? as u16;
654        let vb = r.read_u8()? as u16;
655
656        let border = (hb, vb);
657
658        let (interlaced, stereo, sync_type) = SyncType::parse(r)?;
659
660        Ok(Some(DetailedTiming {
661            pixel_clock, active, front_porch, sync_length, back_porch, 
662            image_size, border, interlaced, stereo, sync_type
663        }))
664    }
665}
666
667/// Type of stereo image supported by the display.
668#[derive(Debug, Clone, Copy)]
669pub enum StereoType {
670    None,
671    SequentialRightSync,
672    SequentialLeftSync,
673    InterleavedLinesRightEven,
674    InterleavedLinesLeftEven,
675    Interleaved4Way,
676    SideBySide
677}
678
679/// Sync type for a given timing.
680#[derive(Debug, Clone, Copy)]
681pub enum SyncType {
682    /// Single sync signal.
683    Composite {
684        /// HSync during VSync
685        serrated: bool,
686        /// Which line to sync on.
687        line: SyncLine
688    },
689    /// Seperate sync signals.
690    Seperate {
691        /// Horizontal polarity.
692        horizontal: SyncPolarity,
693        /// Vertical polarity.
694        vertical: SyncPolarity
695    }
696}
697
698impl SyncType {
699    fn parse(r: &mut Reader) -> Result<(bool, StereoType, SyncType)> {
700        let val = r.read_u8()?;
701
702        let interlaced = val & (1 << 7) > 0;
703        let stereo = match (val & (1 << 6) > 0, val & (1 << 5) > 0, val & (1 << 0) > 0) {
704            (false, false, _) => StereoType::None,
705            (false, true, false) => StereoType::SequentialRightSync,
706            (true, false, false) => StereoType::SequentialLeftSync,
707            (false, true, true) => StereoType::InterleavedLinesRightEven,
708            (true, false, true) => StereoType::InterleavedLinesLeftEven,
709            (true, true, false) => StereoType::Interleaved4Way,
710            (true, true, true) => StereoType::SideBySide
711        };
712
713        let sync_type = match (val & 0b00011000) >> 3 {
714            0 | 1 => SyncType::Composite {
715                serrated: val & (1 << 2) > 0,
716                line: if val & (1 << 1) > 0 {
717                    SyncLine::RGB
718                } else {
719                    SyncLine::Green
720                }
721            },
722            2 => SyncType::Composite {
723                serrated: val & (1 << 2) > 0,
724                line: SyncLine::Digital(if val & (1 << 1) > 0 {
725                    SyncPolarity::Positive
726                } else {
727                    SyncPolarity::Negative
728                })
729            },
730            3 => SyncType::Seperate {
731                vertical: if val & (1 << 2) > 0 {
732                    SyncPolarity::Positive
733                } else {
734                    SyncPolarity::Negative
735                },
736                horizontal: if val & (1 << 1) > 0 {
737                    SyncPolarity::Positive
738                } else {
739                    SyncPolarity::Negative
740                }
741            },
742            _ => unreachable!()
743        };
744
745        Ok((interlaced, stereo, sync_type))
746    }
747}
748
749/// A line to perform sync on.
750#[derive(Debug, Clone, Copy)]
751pub enum SyncLine {
752    RGB,
753    Green,
754    Digital(SyncPolarity)
755}
756
757/// The direction of the sync pulse.
758#[derive(Debug, Clone, Copy)]
759pub enum SyncPolarity {
760    Positive,
761    Negative
762}
763
764/// Additional monitor information.
765#[derive(Debug, Clone)]
766pub struct MonitorDescriptors(pub Vec<MonitorDescriptor>);
767
768impl MonitorDescriptors {
769    fn parse(r: &mut Reader) -> Result<(MonitorDescriptors, Vec<DetailedTiming>, Vec<StandardTiming>, Vec<WhitePoint>)> {
770        let mut detailed_timings = vec![DetailedTiming::parse(r)?.ok_or("Expected detailed timing block.")?];
771
772        let mut standard_timings = Vec::new();
773        let mut monitor_descriptors = Vec::new();
774        let mut white_points = Vec::new();
775
776        for _ in 0..3 {
777            if let Some(timing) = DetailedTiming::parse(r)? {
778                detailed_timings.push(timing);
779            } else {
780                let tag = r.read_u8()?;
781                r.read_u8()?;
782
783                match tag {
784                    0x00..=0x0f => monitor_descriptors.push(MonitorDescriptor::ManufacturerDefined(tag, [
785                        r.read_u8()?,
786                        r.read_u8()?,
787                        r.read_u8()?,
788                        r.read_u8()?,
789                        r.read_u8()?,
790                        r.read_u8()?,
791                        r.read_u8()?,
792                        r.read_u8()?,
793                        r.read_u8()?,
794                        r.read_u8()?,
795                        r.read_u8()?,
796                        r.read_u8()?,
797                        r.read_u8()?
798                    ])),
799                    0x10 => continue,
800                    0x11..=0xf9 => monitor_descriptors.push(MonitorDescriptor::Undefined(tag, [
801                        r.read_u8()?,
802                        r.read_u8()?,
803                        r.read_u8()?,
804                        r.read_u8()?,
805                        r.read_u8()?,
806                        r.read_u8()?,
807                        r.read_u8()?,
808                        r.read_u8()?,
809                        r.read_u8()?,
810                        r.read_u8()?,
811                        r.read_u8()?,
812                        r.read_u8()?,
813                        r.read_u8()?
814                    ])),
815                    0xfa => {
816                        for _ in 0..6 {
817                            let low = r.read_u8()?;
818                            let high = r.read_u8()?;
819                            if low == 1 && high == 1 {
820                                continue;
821                            } else {
822                                standard_timings.push(StandardTiming {
823                                    horizontal_resolution: (low as u16 + 31) * 8,
824                                    aspect_ratio: match high >> 6 {
825                                        0 => 16.0/10.0,
826                                        1 => 4.0/3.0,
827                                        2 => 5.0/4.0,
828                                        3 => 16.0/9.0,
829                                        _ => unreachable!()
830                                    },
831                                    refresh_rate: (high & 0b00111111) + 60
832                                });
833                            }
834                        }
835
836                        ensure(r.read_u8()? == 0x0a, "Expected 0x0a in monitor descriptor.")?;
837                    },
838                    0xfb => {
839                        for _ in 0..2 {
840                            let index = r.read_u8()?;
841                            let w_low = r.read_u8()? as u16; 
842                            let wx_high = r.read_u8()? as u16;
843                            let wy_high = r.read_u8()? as u16;
844                            let white_x = (wx_high << 2 | (w_low & 0b00001100) >> 2) as f32 / 1024.0;
845                            let white_y = (wy_high << 2 | (w_low & 0b00000011) >> 0) as f32 / 1024.0;
846                            let gamma_val = r.read_u8()? as u16;
847                            let gamma = (gamma_val as f32 + 100.0) / 100.0;
848                            white_points.push(WhitePoint { x: white_x, y: white_y, gamma, index });
849                            if index == 0 {
850                                r.read_u32()?;
851                                r.read_u8()?;
852                                break;
853                            }
854                        }
855
856                        ensure(r.read_u8()? == 0x0a, "Expected 0x0a in monitor descriptor.")?;
857                        ensure(r.read_u16()? == 0x2020, "Expected 0x20 in monitor descriptor.")?;
858                    },
859                    0xfc | 0xfe | 0xff => {
860                        let mut out = String::new();
861                        let mut byte = r.read_u8()?;
862                        let mut i = 0;
863                        while byte != 0x0a {
864                            out.push(byte as char);
865                            i += 1;
866                            if i == 13 {
867                                break;
868                            }
869                            byte = r.read_u8()?;
870                        }
871                        i += 1;
872                        while i < 13 {
873                            ensure(r.read_u8()? == 0x20, "Expected 0x20 in monitor descriptor.")?;
874                            i += 1;
875                        }
876
877                        match tag {
878                            0xfc => monitor_descriptors.push(MonitorDescriptor::MonitorName(out)),
879                            0xfe => monitor_descriptors.push(MonitorDescriptor::OtherString(out)),
880                            0xff => monitor_descriptors.push(MonitorDescriptor::SerialNumber(out)),
881                            _ => unreachable!()
882                        }
883                    },
884                    0xfd => {
885                        let min_vrate = r.read_u8()?;
886                        let max_vrate = r.read_u8()?;
887                        let min_hrate = r.read_u8()? as u32 * 1000;
888                        let max_hrate = r.read_u8()? as u32 * 1000;
889                        let pixel_clock = r.read_u8()? as u32 * 10000000;
890                        let stime = r.read_u8()?;
891                        let secondary_timing = match stime {
892                            0x00 => {
893                                ensure(r.read_u8()? == 0x0a, "Expected 0x0a in monitor descriptor.")?;
894                                ensure(r.read_u16()? == 0x2020, "Expected 0x20 in monitor descriptor.")?;
895                                ensure(r.read_u16()? == 0x2020, "Expected 0x20 in monitor descriptor.")?;
896                                ensure(r.read_u16()? == 0x2020, "Expected 0x20 in monitor descriptor.")?;
897                                SecondaryTiming::None
898                            },
899                            0x02 => {
900                                ensure(r.read_u8()? == 0x00, "Expected 0x0a in monitor descriptor.")?;
901                                let start_horizontal_freq = r.read_u8()? as u32 * 2000;
902                                let c = r.read_u8()? as f32 / 2.0;
903                                let m = r.read_u16()? as f32;
904                                let k = r.read_u8()? as f32;
905                                let j = r.read_u8()? as f32 / 2.0;
906                                SecondaryTiming::GTF {
907                                    start_horizontal_freq, c, m, k, j
908                                }
909                            },
910                            _ => {
911                                let data = [
912                                    r.read_u8()?,
913                                    r.read_u8()?,
914                                    r.read_u8()?,
915                                    r.read_u8()?,
916                                    r.read_u8()?,
917                                    r.read_u8()?,
918                                    r.read_u8()?
919                                ];
920                                SecondaryTiming::Other(stime, data)
921                            }
922                        };
923                        monitor_descriptors.push(MonitorDescriptor::RangeLimits {
924                            vertical_rate: (min_vrate, max_vrate),
925                            horizontal_rate: (min_hrate, max_hrate),
926                            pixel_clock, secondary_timing
927                        });
928                    }
929                }
930            }
931        }
932
933        Ok((MonitorDescriptors(monitor_descriptors), detailed_timings, standard_timings, white_points))
934    }
935}
936
937/// One piece of additional monitor information.
938#[derive(Debug, Clone)]
939pub enum MonitorDescriptor {
940    SerialNumber(String),
941    OtherString(String),
942    RangeLimits {
943        /// Vertical frequency limits in Hz.
944        vertical_rate: (u8, u8),
945        /// Horizontal frequency limits in Hz.
946        horizontal_rate: (u32, u32),
947        /// Pixel frequency limits in Hz.
948        pixel_clock: u32,
949        /// Seconday timing information.
950        secondary_timing: SecondaryTiming
951    },
952    MonitorName(String),
953    Undefined(u8, [u8; 13]),
954    ManufacturerDefined(u8, [u8; 13])
955}
956
957/// Parameters for a secondary timing formula.
958#[derive(Debug, Clone)]
959pub enum SecondaryTiming {
960    None,
961    /// Alternative GTF parameters.
962    GTF {
963        /// Horizontal frequency from which this applies.
964        start_horizontal_freq: u32,
965        c: f32,
966        m: f32,
967        k: f32,
968        j: f32
969    },
970    Other(u8, [u8; 7])
971}
972
973/// Parse EDID data from a Read value.
974pub fn parse<T: Read + 'static>(value: &mut T) -> Result<EDID> {
975    EDID::parse(&mut Reader::new(value))
976}