mod base;
mod cea861;
mod displayid;
#[cfg(any(feature = "alloc", feature = "std"))]
pub use base::BaseBlockHandler;
pub use cea861::Cea861Flags;
#[cfg(any(feature = "alloc", feature = "std"))]
pub use cea861::{
AudioFormat, AudioFormatInfo, AudioSampleRates, Cea861Capabilities, ColorimetryBlock,
ColorimetryFlags, DtcPointEncoding, HdmiAudioBlock, HdmiDscMaxSlices, HdmiForumDsc,
HdmiForumFrl, HdmiForumSinkCap, HdmiVsdb, HdmiVsdbFlags, HdrDynamicMetadataDescriptor, HdrEotf,
HdrStaticMetadata, InfoFrameDescriptor, RoomConfigurationBlock, ShortAudioDescriptor,
SpeakerAllocation, SpeakerAllocationFlags, SpeakerAllocationFlags2, SpeakerAllocationFlags3,
SpeakerLocationEntry, T7VtdbBlock, T8VtdbBlock, T10VtdbBlock, T10VtdbEntry,
VendorSpecificBlock, VesaDisplayDeviceBlock, VesaTransferCharacteristic, VideoCapability,
VideoCapabilityFlags, VtbExtBlock, infoframe_type,
};
pub use cea861::{CEA861_HANDLER, Cea861Handler};
#[cfg(any(feature = "alloc", feature = "std"))]
pub use displayid::DisplayIdCapabilities;
pub use displayid::{DISPLAYID_HANDLER, DisplayIdHandler};
pub static STANDARD_HANDLERS: &[&dyn StaticExtensionHandler] = &[&Cea861Handler, &DisplayIdHandler];
use crate::model::capabilities::{DisplayCapabilities, ModeSink, StaticDisplayCapabilities};
#[cfg(any(feature = "alloc", feature = "std"))]
use crate::model::diagnostics::ParseWarning;
use crate::model::edid::EdidSource;
use crate::model::extension::{ExtensionLibrary, StaticExtensionHandler};
#[cfg(any(feature = "alloc", feature = "std"))]
use crate::model::prelude::{Box, Vec};
#[cfg(not(any(feature = "alloc", feature = "std")))]
struct NullSink;
#[cfg(not(any(feature = "alloc", feature = "std")))]
impl ModeSink for NullSink {
fn push_mode(&mut self, _: crate::model::capabilities::VideoMode) {}
fn push_warning(&mut self, _: crate::model::diagnostics::EdidWarning) {}
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl ExtensionLibrary {
pub fn with_standard_handlers() -> Self {
let mut lib = Self::with_standard_extensions();
lib.add_base_handler(BaseBlockHandler);
if let Some(cea) = lib.extensions.iter_mut().find(|e| e.tag == 0x02) {
cea.handler = Some(Box::new(Cea861Handler));
}
if let Some(did) = lib.extensions.iter_mut().find(|e| e.tag == 0x70) {
did.handler = Some(Box::new(DisplayIdHandler));
}
lib
}
}
pub fn capabilities_from_edid<T: EdidSource>(
edid: &T,
library: &ExtensionLibrary,
) -> DisplayCapabilities {
let mut caps = DisplayCapabilities::default();
#[cfg(any(feature = "alloc", feature = "std"))]
{
let mut warnings: Vec<ParseWarning> = Vec::new();
let base = edid.base_block();
for handler in &library.base_handlers {
handler.process(&[base], &mut caps, &mut warnings);
}
for metadata in &library.extensions {
if let Some(handler) = &metadata.handler {
let blocks: Vec<&[u8; 128]> = edid
.extension_blocks()
.filter(|b| b[0] == metadata.tag)
.collect();
if !blocks.is_empty() {
handler.process(&blocks, &mut caps, &mut warnings);
}
}
}
edid.propagate_parse_warnings(&mut caps);
caps.warnings.extend(warnings);
}
#[cfg(not(any(feature = "alloc", feature = "std")))]
{
let _ = library;
base::decode_base_block(edid.base_block(), &mut caps, &mut NullSink);
}
caps
}
pub fn capabilities_from_edid_static<const N: usize, T: EdidSource>(
parsed: &T,
handlers: &[&dyn StaticExtensionHandler],
) -> StaticDisplayCapabilities<N> {
let mut base_caps = DisplayCapabilities::default();
let mut caps = StaticDisplayCapabilities::<N>::default();
#[cfg(any(feature = "alloc", feature = "std"))]
{
use crate::model::extension::ExtensionHandler;
let mut w: Vec<ParseWarning> = Vec::new();
base::BaseBlockHandler.process(&[parsed.base_block()], &mut base_caps, &mut w);
}
#[cfg(not(any(feature = "alloc", feature = "std")))]
{
base::decode_base_block(parsed.base_block(), &mut base_caps, &mut caps);
}
caps.manufacturer = base_caps.manufacturer;
caps.manufacture_date = base_caps.manufacture_date;
caps.edid_version = base_caps.edid_version;
caps.product_code = base_caps.product_code;
caps.serial_number = base_caps.serial_number;
caps.serial_number_string = base_caps.serial_number_string;
caps.display_name = base_caps.display_name;
caps.unspecified_text = base_caps.unspecified_text;
caps.white_points = base_caps.white_points;
caps.digital = base_caps.digital;
caps.color_bit_depth = base_caps.color_bit_depth;
caps.chromaticity = base_caps.chromaticity;
caps.gamma = base_caps.gamma;
caps.display_features = base_caps.display_features;
caps.digital_color_encoding = base_caps.digital_color_encoding;
caps.analog_color_type = base_caps.analog_color_type;
caps.video_interface = base_caps.video_interface;
caps.analog_sync_level = base_caps.analog_sync_level;
caps.screen_size = base_caps.screen_size;
caps.min_v_rate = base_caps.min_v_rate;
caps.max_v_rate = base_caps.max_v_rate;
caps.min_h_rate_khz = base_caps.min_h_rate_khz;
caps.max_h_rate_khz = base_caps.max_h_rate_khz;
caps.max_pixel_clock_mhz = base_caps.max_pixel_clock_mhz;
caps.preferred_image_size_mm = base_caps.preferred_image_size_mm;
caps.timing_formula = base_caps.timing_formula;
caps.color_management = base_caps.color_management;
#[cfg(any(feature = "alloc", feature = "std"))]
{
for mode in base_caps.supported_modes {
caps.push_mode(mode);
}
}
#[cfg(not(any(feature = "alloc", feature = "std")))]
{
base::decode_base_modes(parsed.base_block(), &mut caps);
}
#[cfg(any(feature = "alloc", feature = "std"))]
{
use crate::model::capabilities::StaticContext;
for handler in handlers {
let blocks: Vec<&[u8; 128]> = parsed
.extension_blocks()
.filter(|b| b[0] == handler.tag())
.collect();
if !blocks.is_empty() {
let mut ctx = StaticContext::new(&mut caps);
handler.process(&blocks, &mut ctx);
}
}
}
#[cfg(not(any(feature = "alloc", feature = "std")))]
{
use crate::model::capabilities::StaticContext;
for ext in parsed.extension_blocks() {
if let Some(handler) = handlers.iter().find(|h| h.tag() == ext[0]) {
let mut ctx = StaticContext::new(&mut caps);
handler.process(&[ext], &mut ctx);
}
}
}
caps
}
#[cfg(test)]
#[cfg(any(feature = "alloc", feature = "std"))]
mod tests {
use super::*;
use crate::model::extension::ExtensionTagRegistry;
use crate::parser::parse_edid;
#[test]
fn test_standard_handlers_are_wired() {
let mut bytes = [0u8; 128];
bytes[0..8].copy_from_slice(&crate::parser::EDID_HEADER);
bytes[0x14] = 0x80;
let mut sum = 0u8;
for &b in bytes[..127].iter() {
sum = sum.wrapping_add(b);
}
bytes[127] = 0u8.wrapping_sub(sum);
let registry = ExtensionTagRegistry::new();
let parsed = parse_edid(&bytes, ®istry).unwrap();
let caps = capabilities_from_edid(&parsed, &ExtensionLibrary::with_standard_handlers());
assert!(caps.digital);
}
}