#![cfg_attr(feature = "no_std", no_std)]
pub trait Read {
fn read(&mut self, buf: &mut [u8]) -> Option<usize>;
}
#[cfg(not(feature = "no_std"))]
impl<T: std::io::Read> Read for T {
fn read(&mut self, buf: &mut [u8]) -> Option<usize> {
self.read(buf).ok()
}
}
#[cfg(feature = "no_std")]
#[macro_use]
extern crate alloc;
#[cfg(feature = "no_std")]
use alloc::{vec::Vec, string::String};
pub type Result<T> = core::result::Result<T, &'static str>;
fn ensure(pred: bool, msg: &'static str) -> Result<()> {
if pred {
Ok(())
} else {
Err(msg)
}
}
pub struct Reader<'a> {
value: &'a mut dyn Read,
buffer: Vec<u8>
}
impl<'a> Reader<'a> {
pub fn new<T: Read>(value: &'a mut T) -> Reader<'a> {
Reader {
value: value as &mut dyn Read, buffer: Vec::with_capacity(128)
}
}
fn get(&mut self) -> Result<u8> {
if self.buffer.len() == 0 {
self.buffer.resize(128, 0);
let num = self.value.read(self.buffer.as_mut()).ok_or("Error reading data!")?;
self.buffer.truncate(num);
}
if self.buffer.len() > 0 {
Ok(self.buffer.remove(0))
} else{
Err("Unexpectedly out of data!")
}
}
fn read_u8(&mut self) -> Result<u8> {
self.get()
}
fn read_u16(&mut self) -> Result<u16> {
Ok((self.read_u8()? as u16) | ((self.read_u8()? as u16) << 8))
}
fn read_u32(&mut self) -> Result<u32> {
Ok((self.read_u16()? as u32) | ((self.read_u16()? as u32) << 16))
}
}
#[derive(Debug, Clone)]
pub struct EDID {
pub product: ProductInformation,
pub version: Version,
pub display: DisplayParameters,
pub color: ColorCharacteristics,
pub timings: Timings,
pub descriptors: MonitorDescriptors,
pub extensions: u8,
}
impl EDID {
pub fn parse(r: &mut Reader) -> Result<EDID> {
ensure(r.read_u32()? == 0xffffff00, "Invalid header.")?;
ensure(r.read_u32()? == 0x00ffffff, "Invalid header.")?;
let product = ProductInformation::parse(r)?;
let version = Version::parse(r)?;
let display = DisplayParameters::parse(r)?;
let mut color = ColorCharacteristics::parse(r)?;
let mut timings = Timings::parse(r)?;
let (descriptors, mut detailed_timings, mut standard_timings, mut white) = MonitorDescriptors::parse(r)?;
color.white_points.append(&mut white);
timings.detailed_timings.append(&mut detailed_timings);
timings.standard_timings.append(&mut standard_timings);
let extensions = r.read_u8()?;
Ok(EDID {
product, version, display, color, timings, descriptors, extensions
})
}
}
#[derive(Debug, Clone)]
pub struct ProductInformation {
pub manufacturer_id: ManufacturerID,
pub product_code: u16,
pub serial_number: u32,
pub manufacture_date: ManufactureDate
}
impl ProductInformation {
fn parse(r: &mut Reader) -> Result<ProductInformation> {
let manufacturer_id = ManufacturerID::parse(r)?;
let product_code = r.read_u16()?;
let serial_number = r.read_u32()?;
let manufacture_date = ManufactureDate::parse(r)?;
Ok(ProductInformation {
manufacturer_id, product_code, serial_number, manufacture_date
})
}
}
#[derive(Debug, Clone, Copy)]
pub struct ManufacturerID(pub char, pub char, pub char);
impl ManufacturerID {
fn parse(r: &mut Reader) -> Result<ManufacturerID> {
let k = r.read_u16()?;
let c1 = ((k & 0b0111110000000000) >> 10) as u8;
let c2 = ((k & 0b0000001111100000) >> 05) as u8;
let c3 = ((k & 0b0000000000011111) >> 00) as u8;
Ok(ManufacturerID(c1 as char, c2 as char, c3 as char))
}
}
#[derive(Debug, Clone, Copy)]
pub struct ManufactureDate {
pub week: u8,
pub year: u16
}
impl ManufactureDate {
fn parse(r: &mut Reader) -> Result<ManufactureDate> {
let week = r.read_u8()?;
let year = r.read_u8()? as u16 + 1990;
Ok(ManufactureDate { week, year })
}
}
#[derive(Debug, Clone, Copy)]
pub struct Version {
pub version: u8,
pub revision: u8
}
impl Version {
fn parse(r: &mut Reader) -> Result<Version> {
let version = r.read_u8()?;
let revision = r.read_u8()?;
Ok(Version { version, revision })
}
}
#[derive(Debug, Clone)]
pub struct DisplayParameters {
pub input: VideoInput,
pub max_size: Option<ImageSize>,
pub gamma: Option<f32>,
pub dpms: DPMSFeatures
}
impl DisplayParameters {
fn parse(r: &mut Reader) -> Result<DisplayParameters> {
let input = VideoInput::parse(r)?;
let max_width = r.read_u8()?;
let max_height = r.read_u8()?;
let max_size = if max_width == 0 || max_height == 0 {
None
} else {
Some(ImageSize {
width: max_width as f32,
height: max_height as f32
})
};
let gamma_val = r.read_u8()?;
let gamma = if gamma_val == 0xff {
None
} else {
Some((gamma_val as f32 + 100.0) / 100.0)
};
let dpms = DPMSFeatures::parse(r)?;
Ok(DisplayParameters { input, max_size, gamma, dpms })
}
}
#[derive(Debug, Clone, Copy)]
pub enum VideoInput {
Analog {
signal_level: SignalLevel,
setup_expected: bool,
supported_sync: SupportedSync
},
Digital {
dfp_compatible: bool
}
}
impl VideoInput {
fn parse(r: &mut Reader) -> Result<VideoInput> {
let val = r.read_u8()?;
if val & (1 << 7) == 0 {
let signal_level = match (val & 0b01100000) >> 5 {
0 => SignalLevel { high: 0.700, low: 0.300 },
1 => SignalLevel { high: 0.714, low: 0.286 },
2 => SignalLevel { high: 1.000, low: 0.400 },
3 => SignalLevel { high: 0.700, low: 0.000 },
_ => unreachable!()
};
let setup_expected = val & (1 << 4) > 0;
let supported_sync = SupportedSync {
serrated_vsync: val & (1 << 3) > 0,
sync_on_green: val & (1 << 2) > 0,
composite_sync: val & (1 << 1) > 0,
seperate_sync: val & (1 << 0) > 0
};
Ok(VideoInput::Analog { signal_level, setup_expected, supported_sync })
} else {
Ok(VideoInput::Digital { dfp_compatible: val & 1 > 0 })
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct SignalLevel {
pub high: f32,
pub low: f32
}
#[derive(Debug, Clone, Copy)]
pub struct SupportedSync {
pub serrated_vsync: bool,
pub sync_on_green: bool,
pub composite_sync: bool,
pub seperate_sync: bool
}
#[derive(Debug, Clone, Copy)]
pub struct ImageSize {
pub width: f32,
pub height: f32
}
#[derive(Debug, Clone)]
pub struct DPMSFeatures {
pub standby_supported: bool,
pub suspend_supported: bool,
pub low_power_supported: bool,
pub display_type: DisplayType,
pub default_srgb: bool,
pub preferred_timing_mode: bool,
pub default_gtf_supported: bool
}
impl DPMSFeatures {
fn parse(r: &mut Reader) -> Result<DPMSFeatures> {
let val = r.read_u8()?;
Ok(DPMSFeatures {
standby_supported: val & (1 << 7) > 0,
suspend_supported: val & (1 << 6) > 0,
low_power_supported: val & (1 << 5) > 0,
display_type: match (val & 0b00011000) >> 3 {
0 => DisplayType::Monochrome,
1 => DisplayType::RGBColor,
2 => DisplayType::OtherColor,
3 => DisplayType::Undefined,
_ => unreachable!()
},
default_srgb: val & (1 << 2) > 0,
preferred_timing_mode: val & (1 << 1) > 0,
default_gtf_supported: val & (1 << 0) > 0
})
}
}
#[derive(Debug, Clone, Copy)]
pub enum DisplayType {
Monochrome,
RGBColor,
OtherColor,
Undefined
}
#[derive(Debug, Clone)]
pub struct ColorCharacteristics {
pub red: (f32, f32),
pub green: (f32, f32),
pub blue: (f32, f32),
pub white: (f32, f32),
pub white_points: Vec<WhitePoint>
}
impl ColorCharacteristics {
fn parse(r: &mut Reader) -> Result<ColorCharacteristics> {
let rg_low = r.read_u8()? as u16;
let bw_low = r.read_u8()? as u16;
let rh_x = r.read_u8()? as u16;
let rh_y = r.read_u8()? as u16;
let gh_x = r.read_u8()? as u16;
let gh_y = r.read_u8()? as u16;
let bh_x = r.read_u8()? as u16;
let bh_y = r.read_u8()? as u16;
let wh_x = r.read_u8()? as u16;
let wh_y = r.read_u8()? as u16;
let red_x = (rh_x << 2 | (rg_low & 0b11000000) >> 6) as f32 / 1024.0;
let red_y = (rh_y << 2 | (rg_low & 0b00110000) >> 4) as f32 / 1024.0;
let green_x = (gh_x << 2 | (rg_low & 0b00001100) >> 2) as f32 / 1024.0;
let green_y = (gh_y << 2 | (rg_low & 0b00000011) >> 0) as f32 / 1024.0;
let blue_x = (bh_x << 2 | (bw_low & 0b11000000) >> 6) as f32 / 1024.0;
let blue_y = (bh_y << 2 | (bw_low & 0b00110000) >> 4) as f32 / 1024.0;
let white_x = (wh_x << 2 | (bw_low & 0b00001100) >> 2) as f32 / 1024.0;
let white_y = (wh_y << 2 | (bw_low & 0b00000011) >> 0) as f32 / 1024.0;
Ok(ColorCharacteristics {
red: (red_x, red_y),
green: (green_x, green_y),
blue: (blue_x, blue_y),
white: (white_x, white_y),
white_points: Vec::new()
})
}
}
#[derive(Debug, Clone, Copy)]
pub struct WhitePoint {
pub index: u8,
pub x: f32,
pub y: f32,
pub gamma: f32
}
#[derive(Debug, Clone)]
pub struct Timings {
pub established_timings: Vec<EstablishedTiming>,
pub standard_timings: Vec<StandardTiming>,
pub detailed_timings: Vec<DetailedTiming>
}
impl Timings {
fn parse(r: &mut Reader) -> Result<Timings> {
let mut ft = r.read_u16()?;
let mut established_timings = Vec::new();
if ft & 1 > 0 {
established_timings.push(EstablishedTiming::H800V600F60);
}
ft >>= 1;
if ft & 1 > 0 {
established_timings.push(EstablishedTiming::H800V600F56);
}
ft >>= 1;
if ft & 1 > 0 {
established_timings.push(EstablishedTiming::H640V480F75);
}
ft >>= 1;
if ft & 1 > 0 {
established_timings.push(EstablishedTiming::H640V480F72);
}
ft >>= 1;
if ft & 1 > 0 {
established_timings.push(EstablishedTiming::H640V480F67);
}
ft >>= 1;
if ft & 1 > 0 {
established_timings.push(EstablishedTiming::H640V480F60);
}
ft >>= 1;
if ft & 1 > 0 {
established_timings.push(EstablishedTiming::H720V400F88);
}
ft >>= 1;
if ft & 1 > 0 {
established_timings.push(EstablishedTiming::H720V400F70);
}
ft >>= 1;
if ft & 1 > 0 {
established_timings.push(EstablishedTiming::H1280V1024F75);
}
ft >>= 1;
if ft & 1 > 0 {
established_timings.push(EstablishedTiming::H1024V768F75);
}
ft >>= 1;
if ft & 1 > 0 {
established_timings.push(EstablishedTiming::H1024V768F70);
}
ft >>= 1;
if ft & 1 > 0 {
established_timings.push(EstablishedTiming::H1024V768F60);
}
ft >>= 1;
if ft & 1 > 0 {
established_timings.push(EstablishedTiming::H1024V768F87);
}
ft >>= 1;
if ft & 1 > 0 {
established_timings.push(EstablishedTiming::H832V624F75);
}
ft >>= 1;
if ft & 1 > 0 {
established_timings.push(EstablishedTiming::H800V600F75);
}
ft >>= 1;
if ft & 1 > 0 {
established_timings.push(EstablishedTiming::H800V600F72);
}
let ft = r.read_u8()?;
if ft & (1 << 7) > 0 {
established_timings.push(EstablishedTiming::H1152V870F75);
}
let mut standard_timings = Vec::new();
for _ in 0..8 {
let low = r.read_u8()?;
let high = r.read_u8()?;
if low == 1 && high == 1 {
continue;
} else {
standard_timings.push(StandardTiming {
horizontal_resolution: (low as u16 + 31) * 8,
aspect_ratio: match high >> 6 {
0 => 16.0/10.0,
1 => 4.0/3.0,
2 => 5.0/4.0,
3 => 16.0/9.0,
_ => unreachable!()
},
refresh_rate: (high & 0b00111111) + 60
});
}
}
let detailed_timings = Vec::new();
Ok(Timings { established_timings, standard_timings, detailed_timings })
}
}
#[derive(Debug, Clone, Copy)]
pub enum EstablishedTiming {
H720V400F70,
H720V400F88,
H640V480F60,
H640V480F67,
H640V480F72,
H640V480F75,
H800V600F56,
H800V600F60,
H800V600F72,
H800V600F75,
H832V624F75,
H1024V768F87,
H1024V768F60,
H1024V768F70,
H1024V768F75,
H1280V1024F75,
H1152V870F75
}
#[derive(Debug, Clone, Copy)]
pub struct StandardTiming {
pub horizontal_resolution: u16,
pub aspect_ratio: f32,
pub refresh_rate: u8
}
#[derive(Debug, Clone)]
pub struct DetailedTiming {
pub pixel_clock: u32,
pub active: (u16, u16),
pub front_porch: (u16, u16),
pub sync_length: (u16, u16),
pub back_porch: (u16, u16),
pub image_size: ImageSize,
pub border: (u16, u16),
pub interlaced: bool,
pub stereo: StereoType,
pub sync_type: SyncType
}
impl DetailedTiming {
fn parse(r: &mut Reader) -> Result<Option<DetailedTiming>> {
let pixel_clock = r.read_u16()? as u32 * 10000;
let ha_low = r.read_u8()? as u16;
if pixel_clock == 0 || ha_low == 0 {
return Ok(None);
}
let hb_low = r.read_u8()? as u16;
let h_high = r.read_u8()? as u16;
let horizontal_active = ha_low | (((h_high & 0xf0) >> 4) << 8);
let horizontal_blanking = hb_low | (((h_high & 0x0f) >> 0) << 8);
let va_low = r.read_u8()? as u16;
let vb_low = r.read_u8()? as u16;
let v_high = r.read_u8()? as u16;
let vertical_active = va_low | (((v_high & 0xf0) >> 4) << 8);
let vertical_blanking = vb_low | (((v_high & 0x0f) >> 0) << 8);
let hso_low = r.read_u8()? as u16;
let hsw_low = r.read_u8()? as u16;
let vs_low = r.read_u8()? as u16;
let hvs_high = r.read_u8()? as u16;
let hso_high = (hvs_high & 0b11000000) >> 6;
let hsw_high = (hvs_high & 0b00110000) >> 4;
let vso_high = (hvs_high & 0b00001100) >> 2;
let vsw_high = (hvs_high & 0b00000011) >> 0;
let vso_low = (vs_low & 0xf0) >> 4;
let vsw_low = (vs_low & 0x0f) >> 0;
let vertical_front_porch = vso_low | (vso_high << 4);
let horizontal_front_porch = hso_low | (hso_high << 8);
let vertical_sync_width = vsw_low | (vsw_high << 4);
let horizontal_sync_width = hsw_low | (hsw_high << 8);
let active = (horizontal_active, vertical_active);
let front_porch = (horizontal_front_porch, vertical_front_porch);
let sync_length = (horizontal_sync_width, vertical_sync_width);
let back_porch = (
horizontal_blanking - horizontal_sync_width - horizontal_front_porch,
vertical_blanking - vertical_sync_width - vertical_front_porch
);
let hs_low = r.read_u8()? as u16;
let vs_low = r.read_u8()? as u16;
let s_high = r.read_u8()? as u16;
let h_size = hs_low | ((s_high & 0xf0) >> 4) << 8;
let v_size = vs_low | ((s_high & 0x0f) >> 0) << 8;
let image_size = ImageSize { width: (h_size as f32) / 10.0, height: (v_size as f32) / 10.0 };
let hb = r.read_u8()? as u16;
let vb = r.read_u8()? as u16;
let border = (hb, vb);
let (interlaced, stereo, sync_type) = SyncType::parse(r)?;
Ok(Some(DetailedTiming {
pixel_clock, active, front_porch, sync_length, back_porch,
image_size, border, interlaced, stereo, sync_type
}))
}
}
#[derive(Debug, Clone, Copy)]
pub enum StereoType {
None,
SequentialRightSync,
SequentialLeftSync,
InterleavedLinesRightEven,
InterleavedLinesLeftEven,
Interleaved4Way,
SideBySide
}
#[derive(Debug, Clone, Copy)]
pub enum SyncType {
Composite {
serrated: bool,
line: SyncLine
},
Seperate {
horizontal: SyncPolarity,
vertical: SyncPolarity
}
}
impl SyncType {
fn parse(r: &mut Reader) -> Result<(bool, StereoType, SyncType)> {
let val = r.read_u8()?;
let interlaced = val & (1 << 7) > 0;
let stereo = match (val & (1 << 6) > 0, val & (1 << 5) > 0, val & (1 << 0) > 0) {
(false, false, _) => StereoType::None,
(false, true, false) => StereoType::SequentialRightSync,
(true, false, false) => StereoType::SequentialLeftSync,
(false, true, true) => StereoType::InterleavedLinesRightEven,
(true, false, true) => StereoType::InterleavedLinesLeftEven,
(true, true, false) => StereoType::Interleaved4Way,
(true, true, true) => StereoType::SideBySide
};
let sync_type = match (val & 0b00011000) >> 3 {
0 | 1 => SyncType::Composite {
serrated: val & (1 << 2) > 0,
line: if val & (1 << 1) > 0 {
SyncLine::RGB
} else {
SyncLine::Green
}
},
2 => SyncType::Composite {
serrated: val & (1 << 2) > 0,
line: SyncLine::Digital(if val & (1 << 1) > 0 {
SyncPolarity::Positive
} else {
SyncPolarity::Negative
})
},
3 => SyncType::Seperate {
vertical: if val & (1 << 2) > 0 {
SyncPolarity::Positive
} else {
SyncPolarity::Negative
},
horizontal: if val & (1 << 1) > 0 {
SyncPolarity::Positive
} else {
SyncPolarity::Negative
}
},
_ => unreachable!()
};
Ok((interlaced, stereo, sync_type))
}
}
#[derive(Debug, Clone, Copy)]
pub enum SyncLine {
RGB,
Green,
Digital(SyncPolarity)
}
#[derive(Debug, Clone, Copy)]
pub enum SyncPolarity {
Positive,
Negative
}
#[derive(Debug, Clone)]
pub struct MonitorDescriptors(pub Vec<MonitorDescriptor>);
impl MonitorDescriptors {
fn parse(r: &mut Reader) -> Result<(MonitorDescriptors, Vec<DetailedTiming>, Vec<StandardTiming>, Vec<WhitePoint>)> {
let mut detailed_timings = vec![DetailedTiming::parse(r)?.ok_or("Expected detailed timing block.")?];
let mut standard_timings = Vec::new();
let mut monitor_descriptors = Vec::new();
let mut white_points = Vec::new();
for _ in 0..3 {
if let Some(timing) = DetailedTiming::parse(r)? {
detailed_timings.push(timing);
} else {
let tag = r.read_u8()?;
r.read_u8()?;
match tag {
0x00..=0x0f => monitor_descriptors.push(MonitorDescriptor::ManufacturerDefined(tag, [
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?
])),
0x10 => continue,
0x11..=0xf9 => monitor_descriptors.push(MonitorDescriptor::Undefined(tag, [
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?
])),
0xfa => {
for _ in 0..6 {
let low = r.read_u8()?;
let high = r.read_u8()?;
if low == 1 && high == 1 {
continue;
} else {
standard_timings.push(StandardTiming {
horizontal_resolution: (low as u16 + 31) * 8,
aspect_ratio: match high >> 6 {
0 => 16.0/10.0,
1 => 4.0/3.0,
2 => 5.0/4.0,
3 => 16.0/9.0,
_ => unreachable!()
},
refresh_rate: (high & 0b00111111) + 60
});
}
}
ensure(r.read_u8()? == 0x0a, "Expected 0x0a in monitor descriptor.")?;
},
0xfb => {
for _ in 0..2 {
let index = r.read_u8()?;
let w_low = r.read_u8()? as u16;
let wx_high = r.read_u8()? as u16;
let wy_high = r.read_u8()? as u16;
let white_x = (wx_high << 2 | (w_low & 0b00001100) >> 2) as f32 / 1024.0;
let white_y = (wy_high << 2 | (w_low & 0b00000011) >> 0) as f32 / 1024.0;
let gamma_val = r.read_u8()? as u16;
let gamma = (gamma_val as f32 + 100.0) / 100.0;
white_points.push(WhitePoint { x: white_x, y: white_y, gamma, index });
if index == 0 {
r.read_u32()?;
r.read_u8()?;
break;
}
}
ensure(r.read_u8()? == 0x0a, "Expected 0x0a in monitor descriptor.")?;
ensure(r.read_u16()? == 0x2020, "Expected 0x20 in monitor descriptor.")?;
},
0xfc | 0xfe | 0xff => {
let mut out = String::new();
let mut byte = r.read_u8()?;
let mut i = 0;
while byte != 0x0a {
out.push(byte as char);
i += 1;
if i == 13 {
break;
}
byte = r.read_u8()?;
}
i += 1;
while i < 13 {
ensure(r.read_u8()? == 0x20, "Expected 0x20 in monitor descriptor.")?;
i += 1;
}
match tag {
0xfc => monitor_descriptors.push(MonitorDescriptor::MonitorName(out)),
0xfe => monitor_descriptors.push(MonitorDescriptor::OtherString(out)),
0xff => monitor_descriptors.push(MonitorDescriptor::SerialNumber(out)),
_ => unreachable!()
}
},
0xfd => {
let min_vrate = r.read_u8()?;
let max_vrate = r.read_u8()?;
let min_hrate = r.read_u8()? as u32 * 1000;
let max_hrate = r.read_u8()? as u32 * 1000;
let pixel_clock = r.read_u8()? as u32 * 10000000;
let stime = r.read_u8()?;
let secondary_timing = match stime {
0x00 => {
ensure(r.read_u8()? == 0x0a, "Expected 0x0a in monitor descriptor.")?;
ensure(r.read_u16()? == 0x2020, "Expected 0x20 in monitor descriptor.")?;
ensure(r.read_u16()? == 0x2020, "Expected 0x20 in monitor descriptor.")?;
ensure(r.read_u16()? == 0x2020, "Expected 0x20 in monitor descriptor.")?;
SecondaryTiming::None
},
0x02 => {
ensure(r.read_u8()? == 0x00, "Expected 0x0a in monitor descriptor.")?;
let start_horizontal_freq = r.read_u8()? as u32 * 2000;
let c = r.read_u8()? as f32 / 2.0;
let m = r.read_u16()? as f32;
let k = r.read_u8()? as f32;
let j = r.read_u8()? as f32 / 2.0;
SecondaryTiming::GTF {
start_horizontal_freq, c, m, k, j
}
},
_ => {
let data = [
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?,
r.read_u8()?
];
SecondaryTiming::Other(stime, data)
}
};
monitor_descriptors.push(MonitorDescriptor::RangeLimits {
vertical_rate: (min_vrate, max_vrate),
horizontal_rate: (min_hrate, max_hrate),
pixel_clock, secondary_timing
});
}
}
}
}
Ok((MonitorDescriptors(monitor_descriptors), detailed_timings, standard_timings, white_points))
}
}
#[derive(Debug, Clone)]
pub enum MonitorDescriptor {
SerialNumber(String),
OtherString(String),
RangeLimits {
vertical_rate: (u8, u8),
horizontal_rate: (u32, u32),
pixel_clock: u32,
secondary_timing: SecondaryTiming
},
MonitorName(String),
Undefined(u8, [u8; 13]),
ManufacturerDefined(u8, [u8; 13])
}
#[derive(Debug, Clone)]
pub enum SecondaryTiming {
None,
GTF {
start_horizontal_freq: u32,
c: f32,
m: f32,
k: f32,
j: f32
},
Other(u8, [u8; 7])
}
pub fn parse<T: Read + 'static>(value: &mut T) -> Result<EDID> {
EDID::parse(&mut Reader::new(value))
}