libdisplay-info-0.2.2/.cargo_vcs_info.json0000644000000001550000000000100141230ustar { "git": { "sha1": "6d1489cf25892baa2cf1569ced43d1c69fc949bd" }, "path_in_vcs": "libdisplay-info" }libdisplay-info-0.2.2/Cargo.lock0000644000000154540000000000100121060ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "anyhow" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "cfg-expr" version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d4ba6e40bd1184518716a6e1a781bf9160e286d219ccdb8ab2612e74cfe4789" dependencies = [ "smallvec", "target-lexicon", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "indexmap" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libdisplay-info" version = "0.2.2" dependencies = [ "anyhow", "bitflags", "libc", "libdisplay-info-derive", "libdisplay-info-sys", "thiserror", ] [[package]] name = "libdisplay-info-derive" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea1cd31036b732a546d845f9485c56b1b606b5e476b0821c680dd66c8cd6fcee" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "libdisplay-info-sys" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4f9264ece23c37ffa023ae635f48d588e1786745dad06dff10c9fb99dc646c" dependencies = [ "semver", "system-deps", ] [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "pkg-config" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "proc-macro2" version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "semver" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" [[package]] name = "serde" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_spanned" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "syn" version = "2.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "system-deps" version = "7.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005" dependencies = [ "cfg-expr", "heck", "pkg-config", "toml", "version-compare", ] [[package]] name = "target-lexicon" version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "thiserror" version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "toml" version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", "toml_edit", ] [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", "winnow", ] [[package]] name = "unicode-ident" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" [[package]] name = "version-compare" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "winnow" version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" dependencies = [ "memchr", ] libdisplay-info-0.2.2/Cargo.toml0000644000000027570000000000100121330ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "libdisplay-info" version = "0.2.2" authors = ["Christian Meissl "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "EDID and DisplayID library." documentation = "https://docs.rs/libdisplay-info/" readme = "README.md" keywords = [ "libdisplay", "DisplayID", "EDID", ] categories = ["api-bindings"] license = "MIT" repository = "https://github.com/Smithay/libdisplay-info-rs" [package.metadata.docs.rs] features = ["v0_2"] rustdoc-args = [ "--cfg", "docsrs", ] [lib] name = "libdisplay_info" path = "src/lib.rs" [[example]] name = "decode" path = "examples/decode.rs" [dependencies.bitflags] version = "2.6" [dependencies.libc] version = "0.2.155" [dependencies.libdisplay-info-derive] version = "0.1.0" [dependencies.libdisplay-info-sys] version = "0.2.2" [dependencies.thiserror] version = "2.0.9" [dev-dependencies.anyhow] version = "1.0" [features] v0_2 = ["libdisplay-info-sys/v0_2"] libdisplay-info-0.2.2/Cargo.toml.orig000064400000000000000000000014361046102023000156050ustar 00000000000000[package] authors = ["Christian Meissl "] categories = ["api-bindings"] description = "EDID and DisplayID library." documentation = "https://docs.rs/libdisplay-info/" edition = "2021" version = "0.2.2" keywords = ["libdisplay", "DisplayID", "EDID"] license = "MIT" name = "libdisplay-info" repository = "https://github.com/Smithay/libdisplay-info-rs" readme = "../README.md" [package.metadata.docs.rs] features = ["v0_2"] rustdoc-args = ["--cfg", "docsrs"] [features] v0_2 = ["libdisplay-info-sys/v0_2"] [dependencies] libdisplay-info-sys = { version = "0.2.2", path = "../libdisplay-info-sys" } libc = "0.2.155" thiserror = "2.0.9" libdisplay-info-derive = { version = "0.1.0", path = "../libdisplay-info-derive" } bitflags = "2.6" [dev-dependencies] anyhow = "1.0"libdisplay-info-0.2.2/README.md000064400000000000000000000007121046102023000141710ustar 00000000000000# libdisplay-info-rs This project contains rust bindings for [`libdisplay-info`](https://gitlab.freedesktop.org/emersion/libdisplay-info/). From the official libdisplay-info docs: > EDID and DisplayID library. > > Goals: > > - Provide a set of high-level, easy-to-use, opinionated functions as well as > low-level functions to access detailed information. > - Simplicity and correctness over performance and resource usage. > - Well-tested and fuzzed.libdisplay-info-0.2.2/examples/decode.rs000064400000000000000000001604221046102023000163260ustar 00000000000000use libdisplay_info::{ cta::{self, CTA}, cvt, displayid::{self, DisplayId}, dmt, edid::{ self, ColorPoint, CvtAspectRatio, CvtScaling, CvtTimingCode, CvtTimingCodePreferredVrate, DetailedTimingDef, DetailedTimingDefSignalType, DisplayDescriptorRef, DisplayRangeLimitsType, Edid, EstablishedTimings, ExtensionRef, StandardTimingRef, }, gtf, info::Info, }; pub fn main() -> anyhow::Result<()> { let Some(path) = std::env::args().nth(1) else { eprintln!("no path specified"); return Ok(()); }; let blob = std::fs::read(path)?; let info = Info::parse_edid(&blob)?; if let Some(edid) = info.edid() { print_edid(&edid); let extensions = edid.extensions().len(); if extensions > 0 { println!(" Extension blocks: {}", extensions); } for (index, ext) in edid.extensions().iter().enumerate() { print_ext(ext, index); } } print!("\n----------------\n\n"); if let Some(failure_msg) = info.failure_msg() { println!("{:?}\n", failure_msg); println!("EDID conformity: FAIL"); } else { println!("EDID conformity: PASS"); } Ok(()) } fn print_edid(edid: &Edid<'_>) { println!("Block 0, Base EDID:"); println!( " EDID Structure Version & Revision: {}.{}", edid.version(), edid.revision() ); let vendor_product = edid.vendor_product(); println!(" Vendor & Product Identification:"); println!(" Manufacturer: {:?}", vendor_product.manufacturer); println!(" Model: {}", vendor_product.product); if let Some(serial) = vendor_product.serial { println!(" Serial Number: {}", serial); } if let Some(model_year) = vendor_product.model_year { println!(" Model year: {}", model_year); } else { println!( " Made in: week {} of {}", vendor_product.manufacture_week, vendor_product.manufacture_year ); } println!(" Basic Display Parameters & Features:"); if let Some(video_input_analog) = edid.video_input_analog() { println!(" Analog display"); println!( " Signal Level Standard: {:?}", video_input_analog.signal_level_std ); match video_input_analog.video_setup { edid::VideoInputAnalogVideoSetup::BlankLevelEqBlack => { println!(" Blank level equals black level") } edid::VideoInputAnalogVideoSetup::BlankToBlackSetupPedestal => { println!(" Blank-to-black setup/pedestal") } }; print!(" Sync:"); if video_input_analog.sync_separate { print!(" Separate"); } if video_input_analog.sync_composite { print!(" Composite"); } if video_input_analog.sync_on_green { print!(" SyncOnGreen"); } if video_input_analog.sync_serrations { print!(" Serration"); } println!(); } let video_input_digital = edid.video_input_digital(); if let Some(video_input_digital) = video_input_digital.as_ref() { println!(" Digital display"); if edid.revision() >= 4 { if let Some(color_bit_depth) = video_input_digital.color_bit_depth { println!(" Bits per primary color channel: {}", color_bit_depth); } else { println!(" Color depth is undefined"); } println!(" {:?}\n", video_input_digital.interface); } if video_input_digital.dfp1 { println!(" DFP 1.x compatible TMDS"); } } let screen_size = edid.screen_size(); if let (Some(width_cm), Some(height_cm)) = (screen_size.width_cm, screen_size.height_cm) { println!(" Maximum image size: {} cm x {} cm", width_cm, height_cm); } else if let Some(aspect_ration) = screen_size.landscape_aspect_ratio { println!(" Aspect ratio: {:2} (landscape)", aspect_ration); } else if let Some(portait_aspect_ratio) = screen_size.portait_aspect_ratio { println!(" Aspect ratio: {:2} (portrait)", portait_aspect_ratio); } else { println!(" Image size is variable"); } if let Some(gamma) = edid.basic_gamma() { println!(" Gamma: {}", gamma); } else { println!(" Gamma is defined in an extension block"); } let dpms = edid.dpms(); if dpms.standby || dpms.suspend || dpms.off { print!(" DPMS levels:"); if dpms.standby { print!(" Standby"); } if dpms.suspend { print!(" Suspend"); } if dpms.off { print!(" Off"); } println!(); } if video_input_digital.is_none() || edid.revision() < 4 { println!(" {:?}", edid.display_color_type()); } if let Some(color_encoding_formats) = edid.color_encoding_formats() { assert!(color_encoding_formats.rgb444); print!(" Supported color formats: RGB 4:4:4"); if color_encoding_formats.ycrcb444 { print!(", YCrCb 4:4:4"); } if color_encoding_formats.ycrcb422 { print!(", YCrCb 4:2:2"); } println!(); } let misc_features = edid.misc_features(); if misc_features.srgb_is_primary { println!(" Default (sRGB) color space is primary color space"); } if edid.revision() >= 4 { assert!(misc_features.has_preferred_timing); if misc_features.preferred_timing_is_native { println!(" First detailed timing includes the native pixel format and preferred refresh rate"); } else { println!(" First detailed timing does not include the native pixel format and preferred refresh rate"); } } else if misc_features.has_preferred_timing { println!(" First detailed timing is the preferred timing"); } if misc_features.continuous_freq { println!(" Display is continuous frequency"); } if misc_features.default_gtf { println!(" Supports GTF timings within operating range"); } // /* edid-decode truncates the result, but %f rounds it */ // chromaticity_coords = di_edid_get_chromaticity_coords(edid); // print!(" Color Characteristics:\n"); // print!(" Red : %.4f, %.4f\n", // truncate_chromaticity_coord(chromaticity_coords->red_x), // truncate_chromaticity_coord(chromaticity_coords->red_y)); // print!(" Green: %.4f, %.4f\n", // truncate_chromaticity_coord(chromaticity_coords->green_x), // truncate_chromaticity_coord(chromaticity_coords->green_y)); // print!(" Blue : %.4f, %.4f\n", // truncate_chromaticity_coord(chromaticity_coords->blue_x), // truncate_chromaticity_coord(chromaticity_coords->blue_y)); // print!(" White: %.4f, %.4f\n", // truncate_chromaticity_coord(chromaticity_coords->white_x), // truncate_chromaticity_coord(chromaticity_coords->white_y)); print!(" Established Timings I & II:"); let established_timings_i_ii = edid.established_timings(); if !has_established_timings(&established_timings_i_ii) { print!(" none"); } println!(); if established_timings_i_ii.has_720x400_70hz { println!(" IBM : 720x400 70.081663 Hz 9:5 31.467 kHz 28.320000 MHz"); } if established_timings_i_ii.has_720x400_88hz { println!(" IBM : 720x400 87.849542 Hz 9:5 39.444 kHz 35.500000 MHz"); } if established_timings_i_ii.has_640x480_60hz { println!(" DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz"); } if established_timings_i_ii.has_640x480_67hz { println!(" Apple : 640x480 66.666667 Hz 4:3 35.000 kHz 30.240000 MHz"); } if established_timings_i_ii.has_640x480_72hz { println!(" DMT 0x05: 640x480 72.808802 Hz 4:3 37.861 kHz 31.500000 MHz"); } if established_timings_i_ii.has_640x480_75hz { println!(" DMT 0x06: 640x480 75.000000 Hz 4:3 37.500 kHz 31.500000 MHz"); } if established_timings_i_ii.has_800x600_56hz { println!(" DMT 0x08: 800x600 56.250000 Hz 4:3 35.156 kHz 36.000000 MHz"); } if established_timings_i_ii.has_800x600_60hz { println!(" DMT 0x09: 800x600 60.316541 Hz 4:3 37.879 kHz 40.000000 MHz"); } if established_timings_i_ii.has_800x600_72hz { println!(" DMT 0x0a: 800x600 72.187572 Hz 4:3 48.077 kHz 50.000000 MHz"); } if established_timings_i_ii.has_800x600_75hz { println!(" DMT 0x0b: 800x600 75.000000 Hz 4:3 46.875 kHz 49.500000 MHz"); } if established_timings_i_ii.has_832x624_75hz { println!(" Apple : 832x624 74.551266 Hz 4:3 49.726 kHz 57.284000 MHz"); } if established_timings_i_ii.has_1024x768_87hz_interlaced { println!(" DMT 0x0f: 1024x768i 86.957532 Hz 4:3 35.522 kHz 44.900000 MHz"); } if established_timings_i_ii.has_1024x768_60hz { println!(" DMT 0x10: 1024x768 60.003840 Hz 4:3 48.363 kHz 65.000000 MHz"); } if established_timings_i_ii.has_1024x768_70hz { println!(" DMT 0x11: 1024x768 70.069359 Hz 4:3 56.476 kHz 75.000000 MHz"); } if established_timings_i_ii.has_1024x768_75hz { println!(" DMT 0x12: 1024x768 75.028582 Hz 4:3 60.023 kHz 78.750000 MHz"); } if established_timings_i_ii.has_1280x1024_75hz { println!(" DMT 0x24: 1280x1024 75.024675 Hz 5:4 79.976 kHz 135.000000 MHz"); } if established_timings_i_ii.has_1152x870_75hz { println!(" Apple : 1152x870 75.061550 Hz 192:145 68.681 kHz 100.000000 MHz"); } print!(" Standard Timings:"); if edid.standard_timings().is_empty() { print!(" none"); } println!(); for standard_timing in edid.standard_timings() { print_standard_timing(standard_timing); } println!(" Detailed Timing Descriptors:\n"); for (index, detailed_timing_def) in edid.detailed_timing_defs().enumerate() { print_detailed_timing_def(index, detailed_timing_def) } for display_descriptor in edid.display_descriptors() { print_display_desc(edid, display_descriptor); } } fn print_standard_timing(timing_ref: &StandardTimingRef) { let vert_video = timing_ref.vert_video(); let dmt = timing_ref.dmt(); let timing = timing_ref.inner(); print!(" "); let (refresh, horiz_freq_hz, pixel_clock_mhz) = if let Some(dmt) = dmt { let hbl = dmt.horiz_blank - 2 * dmt.horiz_border; let vbl = dmt.vert_blank - 2 * dmt.vert_border; let horiz_total = dmt.horiz_video + hbl; let vert_total = dmt.vert_video + vbl; let refresh = dmt.pixel_clock_hz as f64 / (horiz_total * vert_total) as f64; let horiz_freq_hz = dmt.pixel_clock_hz as f64 / horiz_total as f64; let pixel_clock_mhz = dmt.pixel_clock_hz as f64 / (1000 * 1000) as f64; print!("DMT {:02x}", dmt.dmt_id); (refresh, horiz_freq_hz, pixel_clock_mhz) } else { /* TODO: CVT timings */ let gtf_options = gtf::Options { h_pixels: timing.horiz_video, v_lines: vert_video, margins_rqd: false, ip_param: gtf::IpParam::VFrameRate, ip_freq_rqd: timing.refresh_rate_hz as f64, int_rqd: false, m: gtf::DEFAULT_M, c: gtf::DEFAULT_C, k: gtf::DEFAULT_K, j: gtf::DEFAULT_J, }; let gtf = gtf::Timing::compute(gtf_options); let hbl = gtf.h_front_porch + gtf.h_sync + gtf.h_back_porch + 2 * gtf.h_border; let vbl = gtf.v_front_porch + gtf.v_sync + gtf.v_back_porch + 2 * gtf.v_border; let horiz_total = gtf.h_pixels + hbl; let vert_total = gtf.v_lines + vbl; /* Upstream edid-decode rounds the pixel clock to kHz... */ let pixel_clock_khz = f64::round(gtf.pixel_freq_mhz * 1000f64); let refresh = (pixel_clock_khz * 1000f64) / (horiz_total * vert_total) as f64; let horiz_freq_hz = (pixel_clock_khz * 1000f64) / horiz_total as f64; let pixel_clock_mhz = pixel_clock_khz / 1000f64; print!("GTF "); (refresh, horiz_freq_hz, pixel_clock_mhz) }; print!(":"); print!(" {:5}x{:-5}", timing.horiz_video, vert_video); print!(" {:10.6} Hz", refresh); print!(" {:?} ", timing.aspect_ratio); print!( " {:8.3} kHz {:13.6} MHz", horiz_freq_hz / 1000f64, pixel_clock_mhz ); if dmt.map(|dmt| dmt.reduced_blanking).unwrap_or(false) { print!(" (RB)"); } println!(); } fn print_detailed_timing_def(index: usize, def: DetailedTimingDef) { let hbl = def.horiz_blank - 2 * def.horiz_border.unwrap_or_default(); let vbl = def.vert_blank - 2 * def.vert_border.unwrap_or_default(); let horiz_total = def.horiz_video + hbl; let vert_total = def.vert_video + vbl; let refresh = def.pixel_clock_hz as f64 / (horiz_total * vert_total) as f64; let horiz_freq_hz = def.pixel_clock_hz as f64 / horiz_total as f64; // compute_aspect_ratio(def.horiz_video, def.vert_video, // &horiz_ratio, &vert_ratio); // signal_type_name = detailed_timing_def_signal_type_name(def.signal_type); // if signal_type_name != NULL) { // flags[flags_len++] = signal_type_name; // } // if detailed_timing_def_sync_serrations(def)) { // flags[flags_len++] = "serrate"; // } // if detailed_timing_def_sync_on_green(def)) { // flags[flags_len++] = "sync-on-green"; // } // if def.stereo != DI_EDID_DETAILED_TIMING_DEF_STEREO_NONE) { // flags[flags_len++] = detailed_timing_def_stereo_name(def.stereo); // } // if def.horiz_image_mm != 0 || def.vert_image_mm != 0) { // snprint!(size_mm, sizeof(size_mm), "%d mm x %d mm", // def.horiz_image_mm, def.vert_image_mm); // flags[flags_len++] = size_mm; // } // assert(flags_len < sizeof(flags) / sizeof(flags[0])); print!(" DTD {}:", index); print!(" {:5}x{:-5}", def.horiz_video, def.vert_video); if def.interlaced { print!("i"); } print!(" {:10.6} Hz", refresh); //print!(" {}:{}", horiz_ratio, vert_ratio); print!( " {:8.3} kHz {:13.6} MHz", horiz_freq_hz / 1000f64, def.pixel_clock_hz as f64 / (1000f64 * 1000f64) ); // if flags_len > 0) { // char *flags_str = join_str(flags); // print!(" (%s)", flags_str); // free(flags_str); // } println!(); let horiz_back_porch = hbl - def.horiz_sync_pulse - def.horiz_front_porch; print!( " Hfront {:4} Hsync {:3} Hback {:4}", def.horiz_front_porch, def.horiz_sync_pulse, horiz_back_porch ); if let Some(horiz_border) = def.horiz_border { print!(" Hborder {}", horiz_border); } if def.signal_type == DetailedTimingDefSignalType::DigitalComposite { print!( " Hpol {:?}", def.digital_composite.unwrap().sync_horiz_polarity ); } else if def.signal_type == DetailedTimingDefSignalType::DigitalSeparate { print!( " Hpol {:?}", def.digital_separate.unwrap().sync_horiz_polarity ); } println!(); let vert_back_porch = vbl - def.vert_sync_pulse - def.vert_front_porch; print!( " Vfront {:4} Vsync {:3} Vback {:4}", def.vert_front_porch, def.vert_sync_pulse, vert_back_porch ); if let Some(vert_border) = def.vert_border { print!(" Vborder {}", vert_border); } if def.signal_type == DetailedTimingDefSignalType::DigitalSeparate { print!( " Vpol {:?}", def.digital_separate.unwrap().sync_vert_polarity ); } println!(); } fn print_display_desc(edid: &Edid<'_>, desc_ref: &DisplayDescriptorRef) { let tag = desc_ref.tag(); print!(" {:?}:", tag); match tag { edid::DisplayDescriptorTag::ProductSerial | edid::DisplayDescriptorTag::DataString | edid::DisplayDescriptorTag::ProductName => { println!(" '{}'", desc_ref.string().unwrap()); } edid::DisplayDescriptorTag::RangeLimits => { let range_limits = desc_ref.range_limits().unwrap(); let mut range_limits_type = range_limits.type_; if edid.revision() < 4 && range_limits.type_ == DisplayRangeLimitsType::Bare { /* edid-decode always prints "GTF" for EDID 1.3 and * earlier even if the display doesn't support it */ range_limits_type = DisplayRangeLimitsType::DefaultGtf; } print!( "\n Monitor ranges ({:?}): {}-{} Hz V, {}-{} kHz H", range_limits_type, range_limits.min_vert_rate_hz, range_limits.max_vert_rate_hz, range_limits.min_horiz_rate_hz / 1000, range_limits.max_horiz_rate_hz / 1000 ); if let Some(max_pixel_clock_hz) = range_limits.max_pixel_clock_hz { print!(", max dotclock {} MHz", max_pixel_clock_hz / (1000 * 1000)); } println!(); match range_limits_type { DisplayRangeLimitsType::SecondaryGtf => { let secondary_gtf = range_limits.secondary_gtf.unwrap(); println!(" GTF Secondary Curve Block:"); println!( " Start frequency: {} kHz", secondary_gtf.start_freq_hz / 1000 ); println!(" C: {:.1}%", secondary_gtf.c); println!(" M: {}%/kHz", secondary_gtf.m); println!(" K: {}", secondary_gtf.k); println!(" J: {:.1}%", secondary_gtf.j); } DisplayRangeLimitsType::Cvt => { let cvt = range_limits.cvt.unwrap(); println!(" CVT version {}.{}", cvt.version, cvt.revision); if let Some(max_horiz_px) = cvt.max_horiz_px { println!(" Max active pixels per line: {}", max_horiz_px); } print!(" Supported aspect ratios:"); if cvt.supported_aspect_ratio.contains(CvtAspectRatio::_4_3) { print!(" 4:3"); } if cvt.supported_aspect_ratio.contains(CvtAspectRatio::_16_9) { print!(" 16:9"); } if cvt.supported_aspect_ratio.contains(CvtAspectRatio::_16_10) { print!(" 16:10"); } if cvt.supported_aspect_ratio.contains(CvtAspectRatio::_5_4) { print!(" 5:4"); } if cvt.supported_aspect_ratio.contains(CvtAspectRatio::_15_9) { print!(" 15:9"); } println!(); println!( " Preferred aspect ratio: {:?}", cvt.preferred_aspect_ratio ); if cvt.standard_blanking { println!(" Supports CVT standard blanking"); } if cvt.reduced_blanking { println!(" Supports CVT reduced blanking"); } if !cvt.supported_scaling.is_empty() { println!(" Supported display scaling:"); if cvt.supported_scaling.contains(CvtScaling::HorizShrink) { println!(" Horizontal shrink"); } if cvt.supported_scaling.contains(CvtScaling::HorizStretch) { println!(" Horizontal stretch"); } if cvt.supported_scaling.contains(CvtScaling::VertShrink) { println!(" Vertical shrink"); } if cvt.supported_scaling.contains(CvtScaling::VertStretch) { println!(" Vertical stretch"); } } println!( " Preferred vertical refresh: {} Hz", cvt.preferred_vert_refresh_hz ); } _ => {} }; } edid::DisplayDescriptorTag::StdTimingIds => { let standard_timing = desc_ref.standard_timings().unwrap(); println!(); for timing in standard_timing { print_standard_timing(timing); } } edid::DisplayDescriptorTag::ColorPoint => { for color_point in desc_ref.color_points() { print!(" "); print_color_point(color_point); } } edid::DisplayDescriptorTag::EstablishedTimingsIII => { println!(); for timing in desc_ref.established_timings_iii() { print_dmt_timing(timing); } } edid::DisplayDescriptorTag::DcmData => { let color_management_data = desc_ref.color_management_data().unwrap(); println!(" Version : {}", color_management_data.version); println!(" Red a3 : {:.2}", color_management_data.red_a3); println!(" Red a2 : {:.2}", color_management_data.red_a2); println!(" Green a3: {:.2}", color_management_data.green_a3); println!(" Green a2: {:.2}", color_management_data.green_a2); println!(" Blue a3 : {:.2}", color_management_data.blue_a3); println!(" Blue a2 : {:.2}", color_management_data.blue_a2); } edid::DisplayDescriptorTag::CvtTimingCodes => { println!(); for timing_code in desc_ref.cvt_timing_codes() { print_cvt_timing_code(timing_code); } } _ => println!(), }; } fn print_color_point(c: ColorPoint) { print!( "Index: {} White: {:.4}, {:.4} ", c.index, c.white_x, c.white_y ); if let Some(gamma) = c.gamma { println!("Gamma: {:.2}", gamma); } else { println!("Gamma: is defined in an extension block"); } } fn print_dmt_timing(t: dmt::Timing) { // int hbl, vbl, horiz_total, vert_total, horiz_ratio, vert_ratio; // double refresh, horiz_freq_hz, pixel_clock_mhz; // compute_aspect_ratio(t.horiz_video, t.vert_video, // &horiz_ratio, &vert_ratio); let hbl = t.horiz_blank - 2 * t.horiz_border; let vbl = t.vert_blank - 2 * t.vert_border; let horiz_total = t.horiz_video + hbl; let vert_total = t.vert_video + vbl; let refresh = t.pixel_clock_hz as f64 / (horiz_total * vert_total) as f64; let horiz_freq_hz = t.pixel_clock_hz / horiz_total; let pixel_clock_mhz = t.pixel_clock_hz as f64 / (1000f64 * 1000f64); print!(" DMT {:02}:", t.dmt_id); print!(" {:5}x{:-5}", t.horiz_video, t.vert_video); print!(" {:10.6} Hz", refresh); //print!(" {:3}:{:-3}", horiz_ratio, vert_ratio); print!( " {:8.3} kHz {:13.6} MHz", horiz_freq_hz / 1000, pixel_clock_mhz ); if t.reduced_blanking { print!(" (RB)"); } println!(); } fn print_cvt_timing_code(t: CvtTimingCode) { let mut options = cvt::Options { red_blank_ver: cvt::ReducedBlankingVersion::None, h_pixels: 0, v_lines: t.addressable_lines_per_field, ip_freq_rqd: 0f64, video_opt: false, vblank: 0f64, additional_hblank: 0, early_vsync_rqd: false, int_rqd: false, margins_rqd: false, }; let (hratio, vratio) = match t.aspect_ratio { edid::CvtTimingCodeAspectRatio::_4_3 => (4, 3), edid::CvtTimingCodeAspectRatio::_16_9 => (16, 9), edid::CvtTimingCodeAspectRatio::_16_10 => (16, 10), edid::CvtTimingCodeAspectRatio::_15_9 => (15, 9), }; options.h_pixels = 8 * (((options.v_lines * hratio) / vratio) / 8); if t.supports_50hz_sb { options.ip_freq_rqd = 50f64; options.red_blank_ver = cvt::ReducedBlankingVersion::None; let timing = cvt::Timing::compute(options); print_cvt_timing( timing, &options, hratio, vratio, t.preferred_vertical_rate == CvtTimingCodePreferredVrate::_50HZ, false, ); } if t.supports_60hz_sb { options.ip_freq_rqd = 60f64; options.red_blank_ver = cvt::ReducedBlankingVersion::None; let timing = cvt::Timing::compute(options); print_cvt_timing( timing, &options, hratio, vratio, t.preferred_vertical_rate == CvtTimingCodePreferredVrate::_60HZ && !t.supports_60hz_rb, false, ); } if t.supports_75hz_sb { options.ip_freq_rqd = 75f64; options.red_blank_ver = cvt::ReducedBlankingVersion::None; let timing = cvt::Timing::compute(options); print_cvt_timing( timing, &options, hratio, vratio, t.preferred_vertical_rate == CvtTimingCodePreferredVrate::_75HZ, false, ); } if t.supports_85hz_sb { options.ip_freq_rqd = 85f64; options.red_blank_ver = cvt::ReducedBlankingVersion::None; let timing = cvt::Timing::compute(options); print_cvt_timing( timing, &options, hratio, vratio, t.preferred_vertical_rate == CvtTimingCodePreferredVrate::_85HZ, false, ); } if t.supports_60hz_rb { options.ip_freq_rqd = 60f64; options.red_blank_ver = cvt::ReducedBlankingVersion::V1; let timing = cvt::Timing::compute(options); print_cvt_timing( timing, &options, hratio, vratio, t.preferred_vertical_rate == CvtTimingCodePreferredVrate::_60HZ, true, ); } } fn print_cvt_timing( t: cvt::Timing, options: &cvt::Options, hratio: i32, vratio: i32, preferred: bool, rb: bool, ) { let hbl = t.h_front_porch + t.h_sync + t.h_back_porch; let htotal = t.total_active_pixels + hbl; print!(" CVT: {:5}x{:-5}", { options.h_pixels }, { options.v_lines }); print!(" {:10.6} Hz", t.act_frame_rate); print!(" {:3}:{:-3}", hratio, vratio); print!( " {:8.3} kHz {:13.6} MHz", t.act_pixel_freq * 1000f64 / htotal, { t.act_pixel_freq } ); if preferred && rb { print!(" (RB, preferred vertical rate)"); } else if preferred { print!(" (preferred vertical rate)"); } else if rb { print!(" (RB)"); } println!(); } fn print_ext(ext: &ExtensionRef, ext_index: usize) { let tag = ext.tag(); println!("\n----------------\n"); print!("Block {}, {:?}:", ext_index + 1, tag); match tag { edid::ExtensionTag::CEA => { print_cta(CTA::from_extension(ext).unwrap()); } edid::ExtensionTag::DisplayId => { print_displayid(DisplayId::from_extension(ext).unwrap()); } _ => {} } } fn print_cta(cta: CTA<'_>) { println!(" Revision: {}", cta.revision()); let cta_flags = cta.flags(); if cta_flags.it_underscan { println!(" Underscans IT Video Formats by default"); } if cta_flags.basic_audio { println!(" Basic audio support"); } if cta_flags.ycc444 { println!(" Supports YCbCr 4:4:4"); } if cta_flags.ycc422 { println!(" Supports YCbCr 4:2:2"); } println!(" Native detailed modes: {}", cta_flags.native_dtds); for data_block in cta.data_blocks() { let tag = data_block.tag(); println!(" {:?}:", tag); match tag { cta::DataBlockTag::Video => { for svd in data_block.svds() { print_cta_svd(svd); } } cta::DataBlockTag::Ycbcr420 => { for svd in data_block.ycbcr420_svds() { print_cta_svd(svd); } } cta::DataBlockTag::SpeakerAlloc => { let speaker_alloc = data_block.speaker_alloc().unwrap().speakers; if speaker_alloc.flw_frw { println!(" FLw/FRw - Front Left/Right Wide"); } if speaker_alloc.flc_frc { println!(" FLc/FRc - Front Left/Right of Center"); } if speaker_alloc.bc { println!(" BC - Back Center"); } if speaker_alloc.bl_br { println!(" BL/BR - Back Left/Right"); } if speaker_alloc.fc { println!(" FC - Front Center"); } if speaker_alloc.lfe1 { println!(" LFE1 - Low Frequency Effects 1"); } if speaker_alloc.fl_fr { println!(" FL/FR - Front Left/Right"); } if speaker_alloc.tpsil_tpsir { println!(" TpSiL/TpSiR - Top Side Left/Right"); } if speaker_alloc.sil_sir { println!(" SiL/SiR - Side Left/Right"); } if speaker_alloc.tpbc { println!(" TpBC - Top Back Center"); } if speaker_alloc.lfe2 { println!(" LFE2 - Low Frequency Effects 2"); } if speaker_alloc.ls_rs { println!(" LS/RS - Left/Right Surround"); } if speaker_alloc.tpfc { println!(" TpFC - Top Front Center"); } if speaker_alloc.tpc { println!(" TpC - Top Center"); } if speaker_alloc.tpfl_tpfr { println!(" TpFL/TpFR - Top Front Left/Right"); } if speaker_alloc.btfl_btfr { println!(" BtFL/BtFR - Bottom Front Left/Right"); } if speaker_alloc.btfc { println!(" BtFC - Bottom Front Center"); } if speaker_alloc.tpbl_tpbr { println!(" TpBL/TpBR - Top Back Left/Right"); } } cta::DataBlockTag::VideoCap => { let video_cap = data_block.video_cap().unwrap(); println!( " YCbCr quantization: {}", if video_cap.selectable_ycc_quantization_range { "Selectable (via AVI YQ)" } else { "No Data" } ); println!( " RGB quantization: {}", if video_cap.selectable_rgb_quantization_range { "Selectable (via AVI Q)" } else { "No Data" } ); println!( " PT scan behavior: {}", video_cap_over_underscan_name(video_cap.pt_over_underscan, "No Data") ); println!( " IT scan behavior: {}", video_cap_over_underscan_name( video_cap.it_over_underscan, "IT video formats not supported" ) ); println!( " CE scan behavior: {}", video_cap_over_underscan_name( video_cap.ce_over_underscan, "CE video formats not supported" ) ); } cta::DataBlockTag::VesaDisplayDevice => { let vesa_dddb = data_block.vesa_dddb().unwrap(); print_cta_vesa_dddb(vesa_dddb); } cta::DataBlockTag::Colorimetry => { let colorimetry = data_block.colorimetry().unwrap(); if colorimetry.xvycc_601 { println!(" xvYCC601"); } if colorimetry.xvycc_709 { println!(" xvYCC709"); } if colorimetry.sycc_601 { println!(" sYCC601") } if colorimetry.opycc_601 { println!(" opYCC601"); } if colorimetry.oprgb { println!(" opRGB"); } if colorimetry.bt2020_cycc { println!(" BT2020cYCC"); } if colorimetry.bt2020_ycc { println!(" BT2020YCC"); } if colorimetry.bt2020_rgb { println!(" BT2020RGB"); } if colorimetry.ictcp { println!(" ICtCp"); } if colorimetry.st2113_rgb { println!(" ST2113RGB"); } } cta::DataBlockTag::HdrStaticMetadata => { let hdr_static_metadata = data_block.hdr_static_metadata().unwrap(); print_cta_hdr_static_metadata(hdr_static_metadata); } cta::DataBlockTag::HdrDynamicMetadata => { let hdr_dynamic_metadata = data_block.hdr_dynamic_metadata().unwrap(); print_cta_hdr_dynamic_metadata(hdr_dynamic_metadata); } cta::DataBlockTag::VesaDisplayTransferCharacteristic => { let transfer_characteristics = data_block.vesa_transfer_characteristics().unwrap(); print_cta_vesa_transfer_characteristics(transfer_characteristics); } cta::DataBlockTag::Audio => { for sad in data_block.sads() { print_cta_sad(sad); } } cta::DataBlockTag::Ycbcr420CapMap => { let ycbcr420_cap_map = data_block.ycbcr420_cap_map().unwrap(); print_ycbcr420_cap_map(&cta, ycbcr420_cap_map); } cta::DataBlockTag::Infoframe => { let infoframe = data_block.infoframe().unwrap(); println!( " VSIFs: {}", infoframe.inner().num_simultaneous_vsifs - 1 ); print_infoframes(infoframe.infoframes()); } _ => {} } } let detailed_timing_defs = cta.detailed_timing_defs().collect::>(); if !detailed_timing_defs.is_empty() { println!(" Detailed Timing Descriptors:"); } for (index, timing) in detailed_timing_defs.into_iter().enumerate() { print_detailed_timing_def(index, timing); } } fn print_displayid(displayid: DisplayId<'_>) { // const struct di_displayid_data_block *const *data_blocks; // const struct di_displayid_data_block *data_block; // enum di_displayid_data_block_tag tag; // size_t i; // const struct di_displayid_display_params *display_params; // const struct di_displayid_tiled_topo *tiled_topo; println!( " Version: {}.{}", displayid.version(), displayid.revision() ); // if is_displayid_base_block) // print!(" Display Product Type: %s\n", // displayid_product_type_name(di_displayid_get_product_type(displayid))); // is_displayid_base_block = false; for data_block in displayid.data_blocks() { let tag = data_block.tag(); println!(" {:?}:", tag); match tag { libdisplay_info::displayid::DataBlockTag::DisplayParams => { let display_params = data_block.display_params().unwrap(); print_displayid_display_params(display_params); } libdisplay_info::displayid::DataBlockTag::TypeITiming => { print_displayid_type_i_timing_block(data_block); } libdisplay_info::displayid::DataBlockTag::TiledDisplayTopo => { let tiled_topo = data_block.tiled_topo().unwrap(); print_displayid_tiled_topo(tiled_topo); } _ => {} } } } fn print_cta_svd(svd: cta::Svd) { print_vic(svd.vic); if svd.native { print!(" (native)"); } println!(); } fn print_vic(vic: u8) { print!(" VIC {:3}", vic); let Some(fmt) = cta::VideoFormat::from_vic(vic) else { return; }; let mut v_active = fmt.v_active; if fmt.interlaced { v_active /= 2; } let h_blank = fmt.h_front + fmt.h_sync + fmt.h_back; let v_blank = fmt.v_front + fmt.v_sync + fmt.v_back; let h_total = (fmt.h_active + h_blank) as f64; let mut v_total = (v_active + v_blank) as f64; if fmt.interlaced { v_total += 0.5; } let refresh = fmt.pixel_clock_hz as f64 / (h_total * v_total); let h_freq_hz = fmt.pixel_clock_hz as f64 / h_total; let pixel_clock_mhz = fmt.pixel_clock_hz as f64 / (1000f64 * 1000f64); let buf = format!( "{}{}", fmt.v_active, fmt.interlaced.then_some("i").unwrap_or_default() ); print!(":"); print!(" {:5}x{:-5}", fmt.h_active, buf); print!(" {:10.6} Hz", refresh); print!(" {:?}", fmt.picture_aspect_ratio); print!( " {:8.3} kHz {:13.6} MHz", h_freq_hz / 1000f64, pixel_clock_mhz ); } fn video_cap_over_underscan_name( over_underscan: cta::VideoCapOverUnderscan, unknown: &'static str, ) -> &'static str { match over_underscan { cta::VideoCapOverUnderscan::UnknownOverUnderscan => unknown, cta::VideoCapOverUnderscan::AlwaysOverscan => "Always Overscanned", cta::VideoCapOverUnderscan::AlwaysUnderscan => "Always Underscanned", cta::VideoCapOverUnderscan::BothOverUnderscan => "Supports both over- and underscan", } } fn print_cta_hdr_static_metadata(metadata: cta::HdrStaticMetadataBlock) { println!(" Electro optical transfer functions:"); if let Some(eotfs) = metadata.eotfs { if eotfs.traditional_sdr { println!(" Traditional gamma - SDR luminance range"); } if eotfs.traditional_hdr { println!(" Traditional gamma - HDR luminance range"); } if eotfs.pq { println!(" SMPTE ST2084"); } if eotfs.hlg { println!(" Hybrid Log-Gamma"); } } if let Some(descriptors) = metadata.descriptors { println!(" Supported static metadata descriptors:"); if descriptors.type1 { println!(" Static metadata type 1"); } } if let Some(desired_content_max_luminance) = metadata.desired_content_max_luminance { println!( " Desired content max luminance: {} ({:.3} cd/m^2)", encode_max_luminance(desired_content_max_luminance), desired_content_max_luminance ) } if let Some(desired_content_max_frame_avg_luminance) = metadata.desired_content_max_frame_avg_luminance { println!( " Desired content max frame-average luminance: {} ({:.3} cd/m^2)", encode_max_luminance(desired_content_max_frame_avg_luminance), desired_content_max_frame_avg_luminance ) } if let Some(desired_content_min_luminance) = metadata.desired_content_min_luminance { println!( " Desired content min luminance: {} ({:.3} cd/m^2)", encode_min_luminance( desired_content_min_luminance, metadata.desired_content_max_luminance.unwrap_or_default() ), desired_content_min_luminance ) } } fn print_cta_hdr_dynamic_metadata(metadata: cta::HdrDynamicMetadataBlock) { if let Some(type1) = metadata.type1 { println!(" HDR Dynamic Metadata Type 1"); println!(" Version: {}", type1.type_1_hdr_metadata_version); } if let Some(type2) = metadata.type2 { println!(" HDR Dynamic Metadata Type 2"); println!(" Version: {}", type2.ts_103_433_spec_version); if type2.ts_103_433_1_capable { println!(" ETSI TS 103 433-1 capable"); } if type2.ts_103_433_2_capable { println!(" ETSI TS 103 433-2 [i.12] capable"); } if type2.ts_103_433_3_capable { println!(" ETSI TS 103 433-3 [i.13] capable"); } } if let Some(_type3) = metadata.type3 { println!(" HDR Dynamic Metadata Type 3"); } if let Some(type4) = metadata.type4 { println!(" HDR Dynamic Metadata Type 4"); println!(" Version: {}", type4.type_4_hdr_metadata_version); } if let Some(type256) = metadata.type256 { println!(" HDR Dynamic Metadata Type 256"); println!(" Version: {}", type256.graphics_overlay_flag_version); } } fn encode_max_luminance(max: f32) -> u8 { if max == 0f32 { 0 } else { (f32::log2(max / 50f32) * 32f32) as u8 } } fn encode_min_luminance(min: f32, max: f32) -> u8 { if min == 0f32 { 0 } else { (255f32 * f32::sqrt(min / max * 100f32)) as u8 } } fn print_cta_sad(sad: cta::Sad) { println!(" {:?}:", sad.format); if let Some(max_channels) = sad.max_channels { println!(" Max channels: {}", max_channels); } if let Some(mpegh_3d) = sad.mpegh_3d { println!(" MPEG-H 3D Audio Level: {:?}", mpegh_3d.level); } if let Some(supported_sample_rates) = sad.supported_sample_rates { print!(" Supported sample rates (kHz):"); if supported_sample_rates.has_192_khz { print!(" 192"); } if supported_sample_rates.has_176_4_khz { print!(" 176.4"); } if supported_sample_rates.has_96_khz { print!(" 96"); } if supported_sample_rates.has_88_2_khz { print!(" 88.2"); } if supported_sample_rates.has_48_khz { print!(" 48"); } if supported_sample_rates.has_44_1_khz { print!(" 44.1"); } if supported_sample_rates.has_32_khz { print!(" 32"); } println!(); } if let Some(lpcm) = sad.lpcm { print!(" Supported sample sizes (bits):"); if lpcm.has_sample_size_24_bits { print!(" 24"); } if lpcm.has_sample_size_20_bits { print!(" 20"); } if lpcm.has_sample_size_16_bits { print!(" 16"); } println!(); } if let Some(max_bitrate_kbs) = sad.max_bitrate_kbs { println!(" Maximum bit rate: {} kb/s", max_bitrate_kbs); } if let Some(enhanced_ac3) = sad.enhanced_ac3 { if enhanced_ac3.supports_joint_object_coding { println!(" Supports Joint Object Coding"); } if enhanced_ac3.supports_joint_object_coding_ACMOD28 { println!(" Supports Joint Object Coding with ACMOD28"); } } if let Some(mat) = sad.mat { if mat.supports_object_audio_and_channel_based { println!(" Supports Dolby TrueHD, object audio PCM and channel-based PCM"); println!( " Hash calculation {}required for object audio PCM or channel-based PCM", if mat.requires_hash_calculation { "" } else { "not " } ); } else { println!(" Supports only Dolby TrueHD"); } } if let Some(wma_pro) = sad.wma_pro { println!(" Profile: {}", wma_pro.profile); } if let Some(mpegh_3d) = sad.mpegh_3d { if mpegh_3d.low_complexity_profile { println!(" Supports MPEG-H 3D Audio Low Complexity Profile"); } if mpegh_3d.baseline_profile { println!(" Supports MPEG-H 3D Audio Baseline Profile"); } } if let Some(mpeg_aac) = sad.mpeg_aac { println!( " AAC audio frame lengths:{}%{}", mpeg_aac .has_frame_length_1024 .then_some(" 1024_TL") .unwrap_or_default(), mpeg_aac .has_frame_length_960 .then_some(" 960_TL") .unwrap_or_default() ); } if let Some(mpeg_surround) = sad.mpeg_surround { println!( " Supports {} signaled MPEG Surround data", if mpeg_surround.signaling == cta::SadMpegSurroundSignaling::Implicit { "only implicitly" } else { "implicitly and explicitly" } ); } if let Some(mpeg_aac_le) = sad.mpeg_aac_le { if mpeg_aac_le.supports_multichannel_sound { println!(" Supports 22.2ch System H"); } } } fn print_cta_vesa_dddb(dddb: cta::VesaDddb) { print!(" Interface Type: {:?}", dddb.interface_type); if let Some(num_channels) = dddb.num_channels { let kind = match dddb.interface_type { cta::VesaDddbInterfaceType::LVDS | cta::VesaDddbInterfaceType::RSDS => "lanes", _ => "channels", }; print!(" {} {}", num_channels, kind); } println!(); println!( " Interface Standard Version: {}.{}", dddb.interface_version, dddb.interface_release ); println!( " Content Protection Support: {:?}", dddb.content_protection ); println!( " Minimum Clock Frequency: {} MHz", dddb.min_clock_freq_mhz ); println!( " Maximum Clock Frequency: {} MHz", dddb.max_clock_freq_mhz ); println!( " Device Native Pixel Format: {}x{}", dddb.native_horiz_pixels, dddb.native_vert_pixels ); println!(" Aspect Ratio: {:.2}", dddb.aspect_ratio); println!(" Default Orientation: {:?}", dddb.default_orientation); println!(" Rotation Capability: {:?}", dddb.rotation_cap); println!(" Zero Pixel Location: {:?}", dddb.zero_pixel_location); println!(" Scan Direction: {:?}", dddb.scan_direction); println!(" Subpixel Information: {:?}", dddb.subpixel_layout); println!( " Horizontal and vertical dot/pixel pitch: {:.2} x {:.2} mm", dddb.horiz_pitch_mm, dddb.vert_pitch_mm ); println!(" Dithering: {:?}", dddb.dithering_type); println!( " Direct Drive: {}", if dddb.direct_drive { "Yes" } else { "No" } ); println!( " Overdrive {}recommended", dddb.overdrive_not_recommended .then_some("not") .unwrap_or_default() ); println!( " Deinterlacing: {}", if dddb.deinterlacing { "Yes" } else { "No" } ); println!( " Audio Support: {}", if dddb.audio_support { "Yes" } else { "No" } ); println!( " Separate Audio Inputs Provided: {}", if dddb.separate_audio_inputs { "Yes" } else { "No" } ); println!( " Audio Input Override: {}", if dddb.audio_input_override { "Yes" } else { "No" } ); if dddb.audio_delay_provided { println!(" Audio Delay: {} ms", dddb.audio_delay_ms); } else { println!(" Audio Delay: no information provided"); } println!( " Frame Rate/Mode Conversion: {:?}", dddb.frame_rate_conversion ); if let Some(frame_rate_range_hz) = dddb.frame_rate_range_hz { println!( " Frame Rate Range: {} fps +/- {} fps", dddb.frame_rate_native_hz, frame_rate_range_hz ); } else { println!(" Nominal Frame Rate: {} fps", dddb.frame_rate_native_hz); println!( " Color Bit Depth: {} @ interface, {} @ display", dddb.bit_depth_interface, dddb.bit_depth_display ); } if dddb.additional_primary_chromaticities_len > 0 { println!(" Additional Primary Chromaticities:"); for (i, additional_primary_chromaticities) in dddb .additional_primary_chromaticities .iter() .take(dddb.additional_primary_chromaticities_len) .enumerate() { println!( " Primary {}: {:.4}, {:.4}", 4 + i, additional_primary_chromaticities.x, additional_primary_chromaticities.y ); } } println!( " Response Time {:?}: {} ms", dddb.resp_time_transition, dddb.resp_time_ms ); println!( " Overscan: {}% x {}%", dddb.overscan_horiz_pct, dddb.overscan_vert_pct ); } fn print_cta_vesa_transfer_characteristics(tf: cta::VesaTransferCharacteristics) { print!(" {:?}", tf.usage); print!(" transfer characteristics:"); for point in tf.points.iter().take(tf.points_len as usize) { print!(" {}", f32::round(point * 1023f32) as u16); } println!(); } fn print_ycbcr420_cap_map(cta: &cta::CTA<'_>, map: cta::Ycbcr420CapMapRef) { for data_block in cta.data_blocks() { if data_block.tag() != cta::DataBlockTag::Video { continue; } for (index, svd) in data_block.svds().enumerate() { if map.di_cta_ycbcr420_cap_map_supported(index) { print_cta_svd(svd) } } } } fn print_infoframes(info_frames: impl Iterator) { for infoframe in info_frames { println!(" {:?}", infoframe.type_); } } fn print_displayid_display_params(params: displayid::DisplayParams) { println!( " Image size: {:.1} mm x {:.1} mm", params.horiz_image_mm, params.vert_image_mm ); println!( " Display native pixel format: {}x{}", params.horiz_pixels, params.vert_pixels ); if let Some(features) = params.features { println!(" Feature support flags:"); if features.audio { println!(" Audio support on video interface"); } if features.separate_audio_inputs { println!(" Separate audio inputs provided"); } if features.audio_input_override { println!(" Audio input override"); } if features.power_management { println!(" Power management (DPM)"); } if features.fixed_timing { println!(" Fixed timing"); } if features.fixed_pixel_format { println!(" Fixed pixel format"); } if features.ai { println!(" Support ACP, ISRC1, or ISRC2packets"); } if features.deinterlacing { println!(" De-interlacing"); } } if let Some(gamma) = params.gamma { println!(" Gamma: {:.2}", gamma); } println!(" Aspect ratio: {:.2}", params.aspect_ratio); println!(" Dynamic bpc native: {}", params.bits_per_color_native); println!(" Dynamic bpc overall: {}", params.bits_per_color_overall); } fn print_displayid_type_i_timing_block(data_block: &displayid::DataBlockRef) { for timing in data_block.type_i_timings() { print_displayid_type_i_timing(timing); } } fn print_displayid_type_i_timing(t: displayid::TypeIIIVIITiming) { let (horiz_ratio, vert_ratio) = displayid_type_i_timing_aspect_ratio(t.aspect_ratio); let horiz_total = t.horiz_active + t.horiz_blank; let vert_total = t.vert_active + t.vert_blank; let pixel_clock_hz = t.pixel_clock_mhz * 1000f64 * 1000f64; let refresh = pixel_clock_hz / (horiz_total * vert_total) as f64; let horiz_freq_hz = pixel_clock_hz / horiz_total as f64; print!(" DTD:"); print!(" {:5}x{:-5}", t.horiz_active, t.vert_active); if t.interlaced { print!("i"); } print!(" {:10.6} Hz", refresh); print!(" {:3}:{:-3}", horiz_ratio, vert_ratio); print!( " {:8.3} kHz {:13.6} MHz", horiz_freq_hz / 1000f64, t.pixel_clock_mhz ); print!(" (aspect "); if t.aspect_ratio == displayid::TimingAspectRatio::Undefined { print!("undefined"); } else { print!("{}:{}", horiz_ratio, vert_ratio); } print!(", {:?}", t.stereo_3d); if t.preferred { print!(", preferred"); } println!(")"); let horiz_back_porch = t.horiz_blank - t.horiz_sync_width - t.horiz_offset; print!( " Hfront {:4} Hsync {:3} Hback {:4} Hpol {:?}", t.horiz_offset, t.horiz_sync_width, horiz_back_porch, t.horiz_sync_polarity ); println!(); let vert_back_porch = t.vert_blank - t.vert_sync_width - t.vert_offset; print!( " Vfront {:4} Vsync {:3} Vback {:4} Vpol {:?}", t.vert_offset, t.vert_sync_width, vert_back_porch, t.vert_sync_polarity ); println!(); } fn displayid_type_i_timing_aspect_ratio(ratio: displayid::TimingAspectRatio) -> (i32, i32) { match ratio { displayid::TimingAspectRatio::_1_1 => (1, 1), displayid::TimingAspectRatio::_5_4 => (5, 4), displayid::TimingAspectRatio::_4_3 => (4, 3), displayid::TimingAspectRatio::_15_9 => (15, 9), displayid::TimingAspectRatio::_16_9 => (16, 9), displayid::TimingAspectRatio::_16_10 => (16, 10), displayid::TimingAspectRatio::_64_27 => (64, 27), displayid::TimingAspectRatio::_256_135 => (256, 135), displayid::TimingAspectRatio::Undefined => (0, 0), } } fn print_displayid_tiled_topo(tiled_topo: displayid::TiledTopo) { if let Some(caps) = tiled_topo.caps { println!(" Capabilities:"); println!( " Behavior if it is the only tile: {:?}", caps.single_recv_behavior ); println!( " Behavior if more than one tile and fewer than total number of tiles: {:?}", caps.missing_recv_behavior ); if caps.single_enclosure { println!(" Tiled display consists of a single physical display enclosure"); } else { println!(" Tiled display consists of multiple physical display enclosures"); } } println!( " Num horizontal tiles: {} Num vertical tiles: {}", tiled_topo.total_horiz_tiles, tiled_topo.total_vert_tiles ); println!( " Tile location: {}, {}", tiled_topo.horiz_tile_location - 1, tiled_topo.vert_tile_location - 1 ); println!( " Tile resolution: {}x{}", tiled_topo.horiz_tile_pixels, tiled_topo.vert_tile_lines ); if let Some(bezel) = tiled_topo.bezel { println!(" Top bevel size: {:.1} pixels", bezel.top_px); println!(" Bottom bevel size: {:.1} pixels", bezel.bottom_px); println!(" Right bevel size: {:.1} pixels", bezel.right_px); println!(" Left bevel size: {:.1} pixels", bezel.left_px); } println!( " Tiled Display Manufacturer/Vendor ID: {:?}", tiled_topo.vendor_id ); println!( " Tiled Display Product ID Code: {}", tiled_topo.product_code ); println!( " Tiled Display Serial Number: {}", tiled_topo.serial_number ); } fn has_established_timings(timings: &EstablishedTimings) -> bool { timings.has_720x400_70hz || timings.has_720x400_88hz || timings.has_640x480_60hz || timings.has_640x480_67hz || timings.has_640x480_72hz || timings.has_640x480_75hz || timings.has_800x600_56hz || timings.has_800x600_60hz || timings.has_800x600_72hz || timings.has_800x600_75hz || timings.has_832x624_75hz || timings.has_1024x768_87hz_interlaced || timings.has_1024x768_60hz || timings.has_1024x768_70hz || timings.has_1024x768_75hz || timings.has_1280x1024_75hz || timings.has_1152x870_75hz } libdisplay-info-0.2.2/src/cta.rs000064400000000000000000001235401046102023000146230ustar 00000000000000//! Low-level API for Consumer Technology Association standards. //! //! The library implements CTA-861-H, available at: //! use std::marker::PhantomData; use libdisplay_info_derive::FFIFrom; use crate::{edid::ExtensionRef, ffi, FFIIter}; /// EDID CTA-861 extension block. #[derive(Debug)] pub struct CTA<'ext> { cta: *const ffi::cta::di_edid_cta, phantom: PhantomData<&'ext ()>, } impl<'ext> CTA<'ext> { /// Get a CTA-861 extension block. /// /// Returns `None` if the extension block tag is not [CEA](crate::edid::ExtensionTag::CEA). pub fn from_extension(extensions: &'ext ExtensionRef) -> Option> { let cta = unsafe { ffi::edid::di_edid_ext_get_cta(extensions.as_ptr()) }; if cta.is_null() { None } else { Some(Self { cta: cta as *const ffi::cta::di_edid_cta, phantom: PhantomData, }) } } /// Get the CTA extension revision (also referred to as `version`` by the /// specification). pub fn revision(&self) -> i32 { unsafe { ffi::cta::di_edid_cta_get_revision(self.cta) } } /// Get miscellaneous CTA flags. pub fn flags(&self) -> Flags { let flags = unsafe { ffi::cta::di_edid_cta_get_flags(self.cta) }; Flags::from(unsafe { *flags }) } /// Get CTA data blocks. pub fn data_blocks(&self) -> &[DataBlockRef] { let data_blocks = unsafe { ffi::cta::di_edid_cta_get_data_blocks(self.cta) }; let mut len = 0; while !unsafe { *data_blocks.offset(len) }.is_null() { len += 1; } unsafe { std::slice::from_raw_parts(data_blocks as *const DataBlockRef, len as usize) } } /// Get a list of EDID detailed timing definitions. pub fn detailed_timing_defs(&self) -> impl Iterator { FFIIter::new(unsafe { ffi::cta::di_edid_cta_get_detailed_timing_defs(self.cta) as *const *const ffi::edid::di_edid_detailed_timing_def }) } } /// CTA video format picture aspect ratio. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_video_format_picture_aspect_ratio)] #[repr(u32)] pub enum VideoFormatPictureAspectRatio { _4_3 = ffi::cta::di_cta_video_format_picture_aspect_ratio_DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, _16_9 = ffi::cta::di_cta_video_format_picture_aspect_ratio_DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, _64_27 = ffi::cta::di_cta_video_format_picture_aspect_ratio_DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, _256_135 = ffi::cta::di_cta_video_format_picture_aspect_ratio_DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_256_135, } /// CTA video format sync pulse polarity. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_video_format_sync_polarity)] #[repr(u32)] pub enum VideoFormatSyncPolarity { Negative = ffi::cta::di_cta_video_format_sync_polarity_DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, Positive = ffi::cta::di_cta_video_format_sync_polarity_DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, } /// A CTA-861 video format, defined in section 4. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_video_format)] pub struct VideoFormat { pub vic: u8, pub h_active: i32, pub v_active: i32, pub h_front: i32, pub v_front: i32, pub h_sync: i32, pub v_sync: i32, pub h_back: i32, pub v_back: i32, pub h_sync_polarity: VideoFormatSyncPolarity, pub v_sync_polarity: VideoFormatSyncPolarity, pub pixel_clock_hz: i64, pub interlaced: bool, pub picture_aspect_ratio: VideoFormatPictureAspectRatio, } impl VideoFormat { /// Get a CTA-861 video format from a VIC. /// /// Returns `None` if the VIC is unknown. pub fn from_vic(vic: u8) -> Option { let video_format = unsafe { ffi::cta::di_cta_video_format_from_vic(vic) }; if video_format.is_null() { None } else { Some(VideoFormat::from(unsafe { *video_format })) } } } /// Miscellaneous EDID CTA flags, defined in section 7.3.3. /// /// For CTA revision 1, all of the fields are zero. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_edid_cta_flags)] pub struct Flags { pub it_underscan: bool, pub basic_audio: bool, pub ycc444: bool, pub ycc422: bool, pub native_dtds: i32, } /// CTA data block, defined in section 7.4. #[repr(transparent)] pub struct DataBlockRef(*const ffi::cta::di_cta_data_block); impl DataBlockRef { /// Get the tag of the CTA data block. pub fn tag(&self) -> DataBlockTag { DataBlockTag::from(unsafe { ffi::cta::di_cta_data_block_get_tag(self.0) }) } /// Get an array of short audio descriptors from a CTA data block. /// /// Returns `None` if the data block tag is not DI_CTA_DATA_BLOCK_AUDIO. pub fn sads(&self) -> impl Iterator { FFIIter::new(unsafe { ffi::cta::di_cta_data_block_get_sads(self.0) }) } /// Get the speaker allocation from a CTA data block. /// /// Returns `None` if the data block tag is not DI_CTA_DATA_BLOCK_SPEAKER_ALLOC. pub fn speaker_alloc(&self) -> Option { SpeakerAllocBlock::from_ptr(unsafe { ffi::cta::di_cta_data_block_get_speaker_alloc(self.0) }) } /// Get the video capabilities from a CTA data block. /// /// Returns `None` if the data block tag is not DI_CTA_DATA_BLOCK_VIDEO_CAP. pub fn video_cap(&self) -> Option { VideoCapBlock::from_ptr(unsafe { ffi::cta::di_cta_data_block_get_video_cap(self.0) }) } /// Get the VESA Display Device Data Block (DDDB) from a CTA data block. /// /// Returns `None` if the data block tag is not /// DI_CTA_DATA_BLOCK_VESA_DISPLAY_DEVICE. pub fn vesa_dddb(&self) -> Option { VesaDddb::from_ptr(unsafe { ffi::cta::di_cta_data_block_get_vesa_dddb(self.0) }) } /// Get the colorimetry data from a CTA data block. /// /// Returns `None` if the data block tag is not DI_CTA_DATA_BLOCK_COLORIMETRY. pub fn colorimetry(&self) -> Option { ColorimetryBlock::from_ptr(unsafe { ffi::cta::di_cta_data_block_get_colorimetry(self.0) }) } /// Get the HDR static metadata from a CTA data block. /// /// Returns `None` if the data block tag is not /// DI_CTA_DATA_BLOCK_HDR_STATIC_METADATA. pub fn hdr_static_metadata(&self) -> Option { HdrStaticMetadataBlock::from_ptr(unsafe { ffi::cta::di_cta_data_block_get_hdr_static_metadata(self.0) }) } /// Get the HDR dynamic metadata from a CTA data block. /// /// Returns `None` if the data block tag is not /// DI_CTA_DATA_BLOCK_HDR_DYNAMIC_METADATA. pub fn hdr_dynamic_metadata(&self) -> Option { HdrDynamicMetadataBlock::from_ptr(unsafe { ffi::cta::di_cta_data_block_get_hdr_dynamic_metadata(self.0) }) } /// Get an array of short video descriptors from a CTA data block. /// /// Returns `None` if the data block tag is not DI_CTA_DATA_BLOCK_VIDEO. pub fn svds(&self) -> impl Iterator { FFIIter::new(unsafe { ffi::cta::di_cta_data_block_get_svds(self.0) }) } /// Get an array of short video descriptors which only allow YCbCr 4:2:0 sampling /// mode from a CTA data block. /// /// Returns `None` if the data block tag is not DI_CTA_DATA_BLOCK_YCBCR420. pub fn ycbcr420_svds(&self) -> impl Iterator { FFIIter::new(unsafe { ffi::cta::di_cta_data_block_get_ycbcr420_svds(self.0) }) } /// Get the Display Transfer Characteristic from a CTA data block. /// /// Returns `None` if the data block tag is not /// DI_CTA_DATA_BLOCK_VESA_DISPLAY_TRANSFER_CHARACTERISTIC. /// /// Upstream is not aware of any EDID blob containing a Display Transfer /// Characteristic data block. /// If such a blob is found, please share it with upstream! pub fn vesa_transfer_characteristics(&self) -> Option { VesaTransferCharacteristics::from_ptr(unsafe { ffi::cta::di_cta_data_block_get_vesa_transfer_characteristics(self.0) }) } /// Get the YCbCr 4:2:0 Capability Map from a CTA data block. /// /// Returns `None` if the data block tag is not DI_CTA_DATA_BLOCK_YCBCR420_CAP_MAP. pub fn ycbcr420_cap_map(&self) -> Option { Ycbcr420CapMapRef::from_ptr(unsafe { ffi::cta::di_cta_data_block_get_ycbcr420_cap_map(self.0) }) } /// Get the InfoFrame information from a CTA data block. /// /// Returns `None` if the data block tag is not DI_CTA_DATA_BLOCK_INFOFRAME. pub fn infoframe(&self) -> Option { InfoframeBlockRef::from_ptr(unsafe { ffi::cta::di_cta_data_block_get_infoframe(self.0) }) } /// Get the HDMI Audio information from a CTA data block. /// /// Returns `None` if the data block tag is not DI_CTA_DATA_BLOCK_HDMI_AUDIO. #[cfg(feature = "v0_2")] pub fn hdmi_audio(&self) -> Option { HdmiAudioBlockRef::from_ptr(unsafe { ffi::cta::di_cta_data_block_get_hdmi_audio(self.0) }) } /// Get the Room Configuration from a CTA data block. /// /// Returns `None` if the data block tag is not DI_CTA_DATA_BLOCK_ROOM_CONFIG. #[cfg(feature = "v0_2")] pub fn room_configuration(&self) -> Option { RoomConfiguration::from_ptr(unsafe { ffi::cta::di_cta_data_block_get_room_configuration(self.0) }) } /// Get an array of Speaker Locations. /// /// Returns `None` if the data block tag is not DI_CTA_DATA_BLOCK_SPEAKER_LOCATION. #[cfg(feature = "v0_2")] pub fn speaker_locations(&self) -> impl Iterator { FFIIter::new(unsafe { ffi::cta::di_cta_data_block_get_speaker_locations(self.0) }) } /// Get the DisplayID Type VII Video Timing from a CTA data block. /// /// Returns `None` if the data block tag is not /// DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_VII. #[cfg(feature = "v0_2")] pub fn did_type_vii_timing(&self) -> Option { crate::displayid::TypeIIIVIITiming::from_ptr(unsafe { ffi::cta::di_cta_data_block_get_did_type_vii_timing(self.0) as *const ffi::displayid::di_displayid_type_i_ii_vii_timing }) } /// Get an array of Short Video References (SVRs) from a CTA data block. The /// first SVR refers to the most-preferred Video Format, while the next SVRs /// are listed in order of decreasing preference. /// /// Returns `None` if the data block tag is not /// DI_CTA_DATA_BLOCK_VIDEO_FORMAT_PREF. #[cfg(feature = "v0_2")] pub fn svrs(&self) -> impl Iterator { FFIIter::new(unsafe { ffi::cta::di_cta_data_block_get_svrs(self.0) }) } } /// CTA data block tag. /// /// Note, the enum values don't match the specification. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_data_block_tag)] #[repr(u32)] pub enum DataBlockTag { Audio = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_AUDIO, Video = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_VIDEO, SpeakerAlloc = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_SPEAKER_ALLOC, VesaDisplayTransferCharacteristic = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_VESA_DISPLAY_TRANSFER_CHARACTERISTIC, VideoFormat = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_VIDEO_FORMAT, VideoCap = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_VIDEO_CAP, VesaDisplayDevice = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_VESA_DISPLAY_DEVICE, Colorimetry = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_COLORIMETRY, HdrStaticMetadata = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_HDR_STATIC_METADATA, HdrDynamicMetadata = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_HDR_DYNAMIC_METADATA, NativeVideoResolution = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_NATIVE_VIDEO_RESOLUTION, VideoFormatPref = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_VIDEO_FORMAT_PREF, Ycbcr420 = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_YCBCR420, Ycbcr420CapMap = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_YCBCR420_CAP_MAP, HdmiAudio = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_HDMI_AUDIO, RoomConfig = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_ROOM_CONFIG, SpeakerLocation = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_SPEAKER_LOCATION, Infoframe = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_INFOFRAME, DisplayidVideoTimingVii = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_VII, DisplayidVideoTimingViii = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_VIII, DisplayidVideoTimingX = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_X, HdmiEdidExtOverride = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_HDMI_EDID_EXT_OVERRIDE, HdmiSinkCap = ffi::cta::di_cta_data_block_tag_DI_CTA_DATA_BLOCK_HDMI_SINK_CAP, } /// Audio formats, defined in tables 37 and 39. /// /// Note, the enum values don't match the specification. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_audio_format)] #[repr(u32)] #[allow(non_camel_case_types)] pub enum AudioFormat { LPCM = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_LPCM, AC3 = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_AC3, MPEG1 = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_MPEG1, MP3 = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_MP3, MPEG2 = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_MPEG2, AAC_LC = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_AAC_LC, DTS = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_DTS, ATRAC = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_ATRAC, ONE_BIT_AUDIO = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_ONE_BIT_AUDIO, ENHANCED_AC3 = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_ENHANCED_AC3, DTS_HD = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_DTS_HD, MAT = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_MAT, DST = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_DST, WMA_PRO = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_WMA_PRO, MPEG4_HE_AAC = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC, MPEG4_HE_AAC_V2 = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_V2, MPEG4_AAC_LC = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC, DRA = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_DRA, MPEG4_HE_AAC_MPEG_SURROUND = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_MPEG_SURROUND, MPEG4_AAC_LC_MPEG_SURROUND = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC_MPEG_SURROUND, MPEGH_3D = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_MPEGH_3D, AC4 = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_AC4, LPCM_3D = ffi::cta::di_cta_audio_format_DI_CTA_AUDIO_FORMAT_LPCM_3D, } #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_sad_sample_rates)] pub struct SadSampleRates { pub has_192_khz: bool, pub has_176_4_khz: bool, pub has_96_khz: bool, pub has_88_2_khz: bool, pub has_48_khz: bool, pub has_44_1_khz: bool, pub has_32_khz: bool, } #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_sad_mpegh_3d_level)] #[repr(u32)] pub enum SadMpegh3dLevel { Unspecified = ffi::cta::di_cta_sad_mpegh_3d_level_DI_CTA_SAD_MPEGH_3D_LEVEL_UNSPECIFIED, _1 = ffi::cta::di_cta_sad_mpegh_3d_level_DI_CTA_SAD_MPEGH_3D_LEVEL_1, _2 = ffi::cta::di_cta_sad_mpegh_3d_level_DI_CTA_SAD_MPEGH_3D_LEVEL_2, _3 = ffi::cta::di_cta_sad_mpegh_3d_level_DI_CTA_SAD_MPEGH_3D_LEVEL_3, _4 = ffi::cta::di_cta_sad_mpegh_3d_level_DI_CTA_SAD_MPEGH_3D_LEVEL_4, _5 = ffi::cta::di_cta_sad_mpegh_3d_level_DI_CTA_SAD_MPEGH_3D_LEVEL_5, } #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_sad_mpegh_3d)] pub struct SadMpegh3d { pub level: SadMpegh3dLevel, pub low_complexity_profile: bool, pub baseline_profile: bool, } #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_sad_mpeg_aac)] pub struct SadMpegAac { pub has_frame_length_960: bool, pub has_frame_length_1024: bool, } #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_sad_mpeg_surround_signaling)] #[repr(u32)] pub enum SadMpegSurroundSignaling { Implicit = ffi::cta::di_cta_sad_mpeg_surround_signaling_DI_CTA_SAD_MPEG_SURROUND_SIGNALING_IMPLICIT, ImplicitAndExplicit = ffi::cta::di_cta_sad_mpeg_surround_signaling_DI_CTA_SAD_MPEG_SURROUND_SIGNALING_IMPLICIT_AND_EXPLICIT, } #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_sad_mpeg_surround)] pub struct SadMpegSurround { pub signaling: SadMpegSurroundSignaling, } #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_sad_mpeg_aac_le)] pub struct SadMpegAacLe { pub supports_multichannel_sound: bool, } #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_sad_lpcm)] pub struct SadLpcm { pub has_sample_size_24_bits: bool, pub has_sample_size_20_bits: bool, pub has_sample_size_16_bits: bool, } #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_sad_enhanced_ac3)] #[allow(non_snake_case)] pub struct SadEnhancedAc3 { pub supports_joint_object_coding: bool, pub supports_joint_object_coding_ACMOD28: bool, } #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_sad_mat)] pub struct SadMat { pub supports_object_audio_and_channel_based: bool, pub requires_hash_calculation: bool, } #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_sad_wma_pro)] pub struct SadWmaPro { pub profile: ::std::os::raw::c_int, } /// A CTA short audio descriptor (SAD), defined in section 7.5.2. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_sad)] pub struct Sad { pub format: AudioFormat, #[optional(0i32)] pub max_channels: Option, #[ptr_deref] pub supported_sample_rates: Option, #[optional(0i32)] pub max_bitrate_kbs: Option, #[ptr_deref] pub lpcm: Option, #[ptr_deref] pub mpegh_3d: Option, #[ptr_deref] pub mpeg_aac: Option, #[ptr_deref] pub mpeg_surround: Option, #[ptr_deref] pub mpeg_aac_le: Option, #[ptr_deref] pub enhanced_ac3: Option, #[ptr_deref] pub mat: Option, #[ptr_deref] pub wma_pro: Option, } /// Indicates which speakers are present. /// /// See figure 6 for the meaning of the fields. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_speaker_allocation)] pub struct SpeakerAllocation { pub flw_frw: bool, pub flc_frc: bool, pub bc: bool, pub bl_br: bool, pub fc: bool, pub lfe1: bool, pub fl_fr: bool, pub tpsil_tpsir: bool, pub sil_sir: bool, pub tpbc: bool, pub lfe2: bool, pub ls_rs: bool, pub tpfc: bool, pub tpc: bool, pub tpfl_tpfr: bool, pub btfl_btfr: bool, pub btfc: bool, pub tpbl_tpbr: bool, } /// Speaker allocation data block (SADB), defined in section 7.5.3. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_speaker_alloc_block)] pub struct SpeakerAllocBlock { pub speakers: SpeakerAllocation, } /// Over- and underscan capability. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_video_cap_over_underscan)] #[repr(u32)] pub enum VideoCapOverUnderscan { UnknownOverUnderscan = ffi::cta::di_cta_video_cap_over_underscan_DI_CTA_VIDEO_CAP_UNKNOWN_OVER_UNDERSCAN, AlwaysOverscan = ffi::cta::di_cta_video_cap_over_underscan_DI_CTA_VIDEO_CAP_ALWAYS_OVERSCAN, AlwaysUnderscan = ffi::cta::di_cta_video_cap_over_underscan_DI_CTA_VIDEO_CAP_ALWAYS_UNDERSCAN, BothOverUnderscan = ffi::cta::di_cta_video_cap_over_underscan_DI_CTA_VIDEO_CAP_BOTH_OVER_UNDERSCAN, } /// Video capability data block (VCDB), defined in section 7.5.6. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_video_cap_block)] pub struct VideoCapBlock { pub selectable_ycc_quantization_range: bool, pub selectable_rgb_quantization_range: bool, pub pt_over_underscan: VideoCapOverUnderscan, pub it_over_underscan: VideoCapOverUnderscan, pub ce_over_underscan: VideoCapOverUnderscan, } /// Interface types, defined in VESA DDDB section 2.3.1 and 2.3.2. /// /// Note, the enum values don't match the specification. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_vesa_dddb_interface_type)] #[repr(u32)] #[allow(non_camel_case_types)] pub enum VesaDddbInterfaceType { VGA = ffi::cta::di_cta_vesa_dddb_interface_type_DI_CTA_VESA_DDDB_INTERFACE_VGA, NAVI_V = ffi::cta::di_cta_vesa_dddb_interface_type_DI_CTA_VESA_DDDB_INTERFACE_NAVI_V, NAVI_D = ffi::cta::di_cta_vesa_dddb_interface_type_DI_CTA_VESA_DDDB_INTERFACE_NAVI_D, LVDS = ffi::cta::di_cta_vesa_dddb_interface_type_DI_CTA_VESA_DDDB_INTERFACE_LVDS, RSDS = ffi::cta::di_cta_vesa_dddb_interface_type_DI_CTA_VESA_DDDB_INTERFACE_RSDS, DVI_D = ffi::cta::di_cta_vesa_dddb_interface_type_DI_CTA_VESA_DDDB_INTERFACE_DVI_D, DVI_I_ANALOG = ffi::cta::di_cta_vesa_dddb_interface_type_DI_CTA_VESA_DDDB_INTERFACE_DVI_I_ANALOG, DVI_I_DIGITAL = ffi::cta::di_cta_vesa_dddb_interface_type_DI_CTA_VESA_DDDB_INTERFACE_DVI_I_DIGITAL, HDMI_A = ffi::cta::di_cta_vesa_dddb_interface_type_DI_CTA_VESA_DDDB_INTERFACE_HDMI_A, HDMI_B = ffi::cta::di_cta_vesa_dddb_interface_type_DI_CTA_VESA_DDDB_INTERFACE_HDMI_B, MDDI = ffi::cta::di_cta_vesa_dddb_interface_type_DI_CTA_VESA_DDDB_INTERFACE_MDDI, DISPLAYPORT = ffi::cta::di_cta_vesa_dddb_interface_type_DI_CTA_VESA_DDDB_INTERFACE_DISPLAYPORT, IEEE_1394 = ffi::cta::di_cta_vesa_dddb_interface_type_DI_CTA_VESA_DDDB_INTERFACE_IEEE_1394, M1_ANALOG = ffi::cta::di_cta_vesa_dddb_interface_type_DI_CTA_VESA_DDDB_INTERFACE_M1_ANALOG, M1_DIGITAL = ffi::cta::di_cta_vesa_dddb_interface_type_DI_CTA_VESA_DDDB_INTERFACE_M1_DIGITAL, } #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_vesa_dddb_content_protection)] #[repr(u32)] pub enum VesaDddbContentProtection { None = ffi::cta::di_cta_vesa_dddb_content_protection_DI_CTA_VESA_DDDB_CONTENT_PROTECTION_NONE, HDCP = ffi::cta::di_cta_vesa_dddb_content_protection_DI_CTA_VESA_DDDB_CONTENT_PROTECTION_HDCP, DTCP = ffi::cta::di_cta_vesa_dddb_content_protection_DI_CTA_VESA_DDDB_CONTENT_PROTECTION_DTCP, DPCP = ffi::cta::di_cta_vesa_dddb_content_protection_DI_CTA_VESA_DDDB_CONTENT_PROTECTION_DPCP, } #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_vesa_dddb_default_orientation)] #[repr(u32)] pub enum VesaDddbDefaultOrientation { Landscape = ffi::cta::di_cta_vesa_dddb_default_orientation_DI_CTA_VESA_DDDB_DEFAULT_ORIENTATION_LANDSCAPE, Portrait = ffi::cta::di_cta_vesa_dddb_default_orientation_DI_CTA_VESA_DDDB_DEFAULT_ORIENTATION_PORTAIT, Unfixed = ffi::cta::di_cta_vesa_dddb_default_orientation_DI_CTA_VESA_DDDB_DEFAULT_ORIENTATION_UNFIXED, Undefined = ffi::cta::di_cta_vesa_dddb_default_orientation_DI_CTA_VESA_DDDB_DEFAULT_ORIENTATION_UNDEFINED, } #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_vesa_dddb_rotation_cap)] #[repr(u32)] #[allow(non_camel_case_types)] pub enum VesaDddbRotationCap { None = ffi::cta::di_cta_vesa_dddb_rotation_cap_DI_CTA_VESA_DDDB_ROTATION_CAP_NONE, _90DEG_CLOCKWISE = ffi::cta::di_cta_vesa_dddb_rotation_cap_DI_CTA_VESA_DDDB_ROTATION_CAP_90DEG_CLOCKWISE, _90DEG_COUNTERCLOCKWISE = ffi::cta::di_cta_vesa_dddb_rotation_cap_DI_CTA_VESA_DDDB_ROTATION_CAP_90DEG_COUNTERCLOCKWISE, _90DEG_EITHER = ffi::cta::di_cta_vesa_dddb_rotation_cap_DI_CTA_VESA_DDDB_ROTATION_CAP_90DEG_EITHER, } #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_vesa_dddb_zero_pixel_location)] #[repr(u32)] pub enum VesaDddbZeroPixelLocation { UpperLeft = ffi::cta::di_cta_vesa_dddb_zero_pixel_location_DI_CTA_VESA_DDDB_ZERO_PIXEL_UPPER_LEFT, UpperRight = ffi::cta::di_cta_vesa_dddb_zero_pixel_location_DI_CTA_VESA_DDDB_ZERO_PIXEL_UPPER_RIGHT, LowerLeft = ffi::cta::di_cta_vesa_dddb_zero_pixel_location_DI_CTA_VESA_DDDB_ZERO_PIXEL_LOWER_LEFT, LowerRight = ffi::cta::di_cta_vesa_dddb_zero_pixel_location_DI_CTA_VESA_DDDB_ZERO_PIXEL_LOWER_RIGHT, } #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_vesa_dddb_scan_direction)] #[repr(u32)] pub enum VesaDddbScanDirection { Undefined = ffi::cta::di_cta_vesa_dddb_scan_direction_DI_CTA_VESA_DDDB_SCAN_DIRECTION_UNDEFINED, FastLongSlowShort = ffi::cta::di_cta_vesa_dddb_scan_direction_DI_CTA_VESA_DDDB_SCAN_DIRECTION_FAST_LONG_SLOW_SHORT, FastShortSlowLong = ffi::cta::di_cta_vesa_dddb_scan_direction_DI_CTA_VESA_DDDB_SCAN_DIRECTION_FAST_SHORT_SLOW_LONG, } /// Subpixel layout, defined in VESA DDDB section 2.9. /// /// For layouts with more than 3 subpixels, the color coordinates of the /// additional subpixels are defined in the additional primary chromaticities. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_vesa_dddb_subpixel_layout)] #[repr(u32)] #[allow(non_camel_case_types)] pub enum VesaDddbSubpixelLayout { Undefined = ffi::cta::di_cta_vesa_dddb_subpixel_layout_DI_CTA_VESA_DDDB_SUBPIXEL_UNDEFINED, RGB_VERT = ffi::cta::di_cta_vesa_dddb_subpixel_layout_DI_CTA_VESA_DDDB_SUBPIXEL_RGB_VERT, RGB_HORIZ = ffi::cta::di_cta_vesa_dddb_subpixel_layout_DI_CTA_VESA_DDDB_SUBPIXEL_RGB_HORIZ, EDID_CHROM_VERT = ffi::cta::di_cta_vesa_dddb_subpixel_layout_DI_CTA_VESA_DDDB_SUBPIXEL_EDID_CHROM_VERT, EDID_CHROM_HORIZ = ffi::cta::di_cta_vesa_dddb_subpixel_layout_DI_CTA_VESA_DDDB_SUBPIXEL_EDID_CHROM_HORIZ, QUAD_RGGB = ffi::cta::di_cta_vesa_dddb_subpixel_layout_DI_CTA_VESA_DDDB_SUBPIXEL_QUAD_RGGB, QUAD_GBRG = ffi::cta::di_cta_vesa_dddb_subpixel_layout_DI_CTA_VESA_DDDB_SUBPIXEL_QUAD_GBRG, DELTA_RGB = ffi::cta::di_cta_vesa_dddb_subpixel_layout_DI_CTA_VESA_DDDB_SUBPIXEL_DELTA_RGB, MOSAIC = ffi::cta::di_cta_vesa_dddb_subpixel_layout_DI_CTA_VESA_DDDB_SUBPIXEL_MOSAIC, QUAD_ANY = ffi::cta::di_cta_vesa_dddb_subpixel_layout_DI_CTA_VESA_DDDB_SUBPIXEL_QUAD_ANY, FIVE = ffi::cta::di_cta_vesa_dddb_subpixel_layout_DI_CTA_VESA_DDDB_SUBPIXEL_FIVE, SIX = ffi::cta::di_cta_vesa_dddb_subpixel_layout_DI_CTA_VESA_DDDB_SUBPIXEL_SIX, CLAIRVOYANTE_PENTILE = ffi::cta::di_cta_vesa_dddb_subpixel_layout_DI_CTA_VESA_DDDB_SUBPIXEL_CLAIRVOYANTE_PENTILE, } #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_vesa_dddb_dithering_type)] #[repr(u32)] pub enum VesaDddbDitheringType { None = ffi::cta::di_cta_vesa_dddb_dithering_type_DI_CTA_VESA_DDDB_DITHERING_NONE, Spacial = ffi::cta::di_cta_vesa_dddb_dithering_type_DI_CTA_VESA_DDDB_DITHERING_SPACIAL, Temporal = ffi::cta::di_cta_vesa_dddb_dithering_type_DI_CTA_VESA_DDDB_DITHERING_TEMPORAL, SpatialAndTemporal = ffi::cta::di_cta_vesa_dddb_dithering_type_DI_CTA_VESA_DDDB_DITHERING_SPATIAL_AND_TEMPORAL, } #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_vesa_dddb_additional_primary_chromaticity)] pub struct VesaDddbAdditionalPrimaryChromaticity { pub x: f32, pub y: f32, } #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_vesa_dddb_frame_rate_conversion)] #[repr(u32)] pub enum VesaDddbFrameRateConversion { None = ffi::cta::di_cta_vesa_dddb_frame_rate_conversion_DI_CTA_VESA_DDDB_FRAME_RATE_CONVERSION_NONE, SingleBuffering = ffi::cta::di_cta_vesa_dddb_frame_rate_conversion_DI_CTA_VESA_DDDB_FRAME_RATE_CONVERSION_SINGLE_BUFFERING, DoubleBuffering = ffi::cta::di_cta_vesa_dddb_frame_rate_conversion_DI_CTA_VESA_DDDB_FRAME_RATE_CONVERSION_DOUBLE_BUFFERING, Advanced = ffi::cta::di_cta_vesa_dddb_frame_rate_conversion_DI_CTA_VESA_DDDB_FRAME_RATE_CONVERSION_ADVANCED, } #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_vesa_dddb_resp_time_transition)] #[repr(u32)] pub enum VesaDddbRespTimeTransition { BlackToWhite = ffi::cta::di_cta_vesa_dddb_resp_time_transition_DI_CTA_VESA_DDDB_RESP_TIME_BLACK_TO_WHITE, WhiteToBlack = ffi::cta::di_cta_vesa_dddb_resp_time_transition_DI_CTA_VESA_DDDB_RESP_TIME_WHITE_TO_BLACK, } /// VESA Display Device Data Block (DDDB), defined in VESA Display Device Data /// Block (DDDB) Standard version 1. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_vesa_dddb)] pub struct VesaDddb { pub interface_type: VesaDddbInterfaceType, #[optional(0i32)] pub num_channels: Option, pub interface_version: i32, pub interface_release: i32, pub content_protection: VesaDddbContentProtection, pub min_clock_freq_mhz: i32, pub max_clock_freq_mhz: i32, pub native_horiz_pixels: i32, pub native_vert_pixels: i32, pub aspect_ratio: f32, pub default_orientation: VesaDddbDefaultOrientation, pub rotation_cap: VesaDddbRotationCap, pub zero_pixel_location: VesaDddbZeroPixelLocation, pub scan_direction: VesaDddbScanDirection, pub subpixel_layout: VesaDddbSubpixelLayout, pub horiz_pitch_mm: f32, pub vert_pitch_mm: f32, pub dithering_type: VesaDddbDitheringType, pub direct_drive: bool, pub overdrive_not_recommended: bool, pub deinterlacing: bool, pub audio_support: bool, pub separate_audio_inputs: bool, pub audio_input_override: bool, pub audio_delay_provided: bool, pub audio_delay_ms: i32, pub frame_rate_conversion: VesaDddbFrameRateConversion, #[optional(0i32)] pub frame_rate_range_hz: Option, pub frame_rate_native_hz: i32, pub bit_depth_interface: i32, pub bit_depth_display: i32, pub additional_primary_chromaticities_len: usize, pub additional_primary_chromaticities: [VesaDddbAdditionalPrimaryChromaticity; 3usize], pub resp_time_transition: VesaDddbRespTimeTransition, pub resp_time_ms: i32, pub overscan_horiz_pct: i32, pub overscan_vert_pct: i32, } /// CTA colorimetry data block, defined in section 7.5.5. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_colorimetry_block)] pub struct ColorimetryBlock { pub xvycc_601: bool, pub xvycc_709: bool, pub sycc_601: bool, pub opycc_601: bool, pub oprgb: bool, pub bt2020_cycc: bool, pub bt2020_ycc: bool, pub bt2020_rgb: bool, pub st2113_rgb: bool, pub ictcp: bool, } /// Supported Electro-Optical Transfer Functions for a CTA HDR static metadata /// block. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_hdr_static_metadata_block_eotfs)] pub struct HdrStaticMetadataBlockEotfs { pub traditional_sdr: bool, pub traditional_hdr: bool, pub pq: bool, pub hlg: bool, } /// Supported static metadata descriptors for a CTA HDR static metadata block. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_hdr_static_metadata_block_descriptors)] pub struct HdrStaticMetadataBlockDescriptors { pub type1: bool, } /// CTA HDR static metadata block, defined in section 7.5.13. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_hdr_static_metadata_block)] pub struct HdrStaticMetadataBlock { #[optional(0f32)] pub desired_content_max_luminance: Option, #[optional(0f32)] pub desired_content_max_frame_avg_luminance: Option, #[optional(0f32)] pub desired_content_min_luminance: Option, #[ptr_deref] pub eotfs: Option, #[ptr_deref] pub descriptors: Option, } #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_hdr_dynamic_metadata_block_type1)] pub struct HdrDynamicMetadataBlockType1 { pub type_1_hdr_metadata_version: u8, } #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_hdr_dynamic_metadata_block_type2)] pub struct HdrDynamicMetadataBlockType2 { pub ts_103_433_spec_version: u8, pub ts_103_433_1_capable: bool, pub ts_103_433_2_capable: bool, pub ts_103_433_3_capable: bool, } #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_hdr_dynamic_metadata_block_type3)] pub struct HdrDynamicMetadataBlockType3 {} #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_hdr_dynamic_metadata_block_type4)] pub struct HdrDynamicMetadataBlockType4 { pub type_4_hdr_metadata_version: u8, } #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_hdr_dynamic_metadata_block_type256)] pub struct HdrDynamicMetadataBlockType256 { pub graphics_overlay_flag_version: u8, } #[doc = " CTA HDR dynamic metadata block, defined in section 7.5.14."] #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_hdr_dynamic_metadata_block)] pub struct HdrDynamicMetadataBlock { #[ptr_deref] pub type1: Option, #[ptr_deref] pub type2: Option, #[ptr_deref] pub type3: Option, #[ptr_deref] pub type4: Option, #[ptr_deref] pub type256: Option, } /// A Short Video Descriptor (SVD). #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_svd)] pub struct Svd { pub vic: u8, pub native: bool, } #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_vesa_transfer_characteristics_usage)] #[repr(u32)] pub enum VesaTransferCharacteristicsUsage { White = ffi::cta::di_cta_vesa_transfer_characteristics_usage_DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_WHITE, Red = ffi::cta::di_cta_vesa_transfer_characteristics_usage_DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_RED, Green = ffi::cta::di_cta_vesa_transfer_characteristics_usage_DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_GREEN, Blue = ffi::cta::di_cta_vesa_transfer_characteristics_usage_DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_BLUE, } /// VESA Display Transfer Characteristic Data Block, defined in VESA Display /// Transfer Characteristics Data Block Standard Version 1.0 /// /// Contains 8, 16 or 32 evenly distributed points on the input axis describing /// the normalized relative luminance at that input. The first value includes the /// relative black level luminance. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_vesa_transfer_characteristics)] pub struct VesaTransferCharacteristics { pub usage: VesaTransferCharacteristicsUsage, pub points_len: u8, pub points: [f32; 32usize], } /// CTA YCbCr 4:2:0 Capability Map block, defined in section 7.5.11. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_ycbcr420_cap_map)] #[wrap] pub struct Ycbcr420CapMap {} impl Ycbcr420CapMapRef { /// Returns true if the SVD in regular Video Data Blocks at index `svd_index` /// supports YCbCr 4:2:0 subsampling. pub fn di_cta_ycbcr420_cap_map_supported(&self, svd_index: usize) -> bool { unsafe { ffi::cta::di_cta_ycbcr420_cap_map_supported(self.0, svd_index) } } } /// InfoFrame types, defined in table 7. /// /// Note, the enum values don't match the specification. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_infoframe_type)] #[repr(u32)] pub enum InfoframeType { AuxiliaryVideoInformation = ffi::cta::di_cta_infoframe_type_DI_CTA_INFOFRAME_TYPE_AUXILIARY_VIDEO_INFORMATION, SourceProductDescription = ffi::cta::di_cta_infoframe_type_DI_CTA_INFOFRAME_TYPE_SOURCE_PRODUCT_DESCRIPTION, Audio = ffi::cta::di_cta_infoframe_type_DI_CTA_INFOFRAME_TYPE_AUDIO, MpegSource = ffi::cta::di_cta_infoframe_type_DI_CTA_INFOFRAME_TYPE_MPEG_SOURCE, NtscVbi = ffi::cta::di_cta_infoframe_type_DI_CTA_INFOFRAME_TYPE_NTSC_VBI, DynamicRangeAndMastering = ffi::cta::di_cta_infoframe_type_DI_CTA_INFOFRAME_TYPE_DYNAMIC_RANGE_AND_MASTERING, } /// CTA InfoFrame descriptor, defined in section 7.5.9. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_infoframe_descriptor)] pub struct InfoframeDescriptor { pub type_: InfoframeType, } /// CTA InfoFrame processing, defined in section 7.5.9. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_infoframe_block)] #[wrap] pub struct InfoframeBlock { pub num_simultaneous_vsifs: i32, } impl InfoframeBlockRef { pub fn infoframes(&self) -> impl Iterator { FFIIter::new(unsafe { (*self.0).infoframes }) } } /// InfoFrame types, defined in table 7. /// /// Note, the enum values don't match the specification. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_hdmi_audio_3d_channels)] #[repr(u32)] #[cfg(feature = "v0_2")] pub enum HdmiAudio3DChannels { Unknown = ffi::cta::di_cta_hdmi_audio_3d_channels_DI_CTA_HDMI_AUDIO_3D_CHANNELS_UNKNOWN, _10_2 = ffi::cta::di_cta_hdmi_audio_3d_channels_DI_CTA_HDMI_AUDIO_3D_CHANNELS_10_2, _22_2 = ffi::cta::di_cta_hdmi_audio_3d_channels_DI_CTA_HDMI_AUDIO_3D_CHANNELS_22_2, _30_2 = ffi::cta::di_cta_hdmi_audio_3d_channels_DI_CTA_HDMI_AUDIO_3D_CHANNELS_30_2, } /// HDMI 3D Audio #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_hdmi_audio_3d)] #[wrap] #[cfg(feature = "v0_2")] pub struct HdmiAudio3d { pub channels: HdmiAudio3DChannels, pub speakers: SpeakerAllocation, } #[cfg(feature = "v0_2")] impl HdmiAudio3dRef { pub fn sads(&self) -> impl Iterator { FFIIter::new(unsafe { (*self.0).sads }) } } /// HDMI Multi-Stream Audio #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_hdmi_audio_multi_stream)] #[cfg(feature = "v0_2")] pub struct HdmiAudioMultiStream { pub max_streams: ::std::os::raw::c_int, pub supports_non_mixed: bool, } /// HDMI Audio #[derive(Debug, FFIFrom)] #[ffi(ffi::cta::di_cta_hdmi_audio_block)] #[wrap] #[cfg(feature = "v0_2")] pub struct HdmiAudioBlock { #[ptr_deref] pub multi_stream: Option, } #[cfg(feature = "v0_2")] impl HdmiAudioBlockRef { pub fn audio_3d(&self) -> Option { HdmiAudio3dRef::from_ptr(unsafe { (*self.0).audio_3d }) } } /// Room Configuration Data Block, defined in section 7.5.15. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_room_configuration)] #[cfg(feature = "v0_2")] pub struct RoomConfiguration { pub speakers: SpeakerAllocation, pub speaker_count: ::std::os::raw::c_int, pub has_speaker_location_descriptors: bool, pub max_x: ::std::os::raw::c_int, pub max_y: ::std::os::raw::c_int, pub max_z: ::std::os::raw::c_int, pub display_x: f64, pub display_y: f64, pub display_z: f64, } #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_speaker_placement)] #[repr(u32)] #[cfg(feature = "v0_2")] pub enum SpeakerPlacement { FL = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_FL, FR = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_FR, FC = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_FC, LFE1 = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_LFE1, BL = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_BL, BR = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_BR, FLC = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_FLC, FRC = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_FRC, BC = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_BC, LFE2 = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_LFE2, SIL = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_SIL, SIR = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_SIR, TPFL = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_TPFL, TPFR = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_TPFR, TPFC = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_TPFC, TPC = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_TPC, TPBL = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_TPBL, TPBR = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_TPBR, TPSIL = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_TPSIL, TPSIR = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_TPSIR, TPBC = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_TPBC, BTFC = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_BTFC, BTFL = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_BTFL, BRFR = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_BRFR, FLW = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_FLW, FRW = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_FRW, LS = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_LS, RS = ffi::cta::di_cta_speaker_placement_DI_CTA_SPEAKER_PLACEMENT_RS, } /// Speaker Location Data Block, defined in section 7.5.16. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_speaker_locations)] #[cfg(feature = "v0_2")] pub struct SpeakerLocations { pub channel_index: ::std::os::raw::c_int, pub is_active: bool, pub has_coords: bool, pub x: f64, pub y: f64, pub z: f64, pub speaker_id: SpeakerPlacement, } #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cta::di_cta_svr_type)] #[repr(u32)] #[cfg(feature = "v0_2")] pub enum SvrType { VIC = ffi::cta::di_cta_svr_type_DI_CTA_SVR_TYPE_VIC, DtdIndex = ffi::cta::di_cta_svr_type_DI_CTA_SVR_TYPE_DTD_INDEX, T7T10VTDB = ffi::cta::di_cta_svr_type_DI_CTA_SVR_TYPE_T7T10VTDB, FirstT8vtdb = ffi::cta::di_cta_svr_type_DI_CTA_SVR_TYPE_FIRST_T8VTDB, } /// Short Video Reference, defined in section 7.5.12. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cta::di_cta_svr)] #[cfg(feature = "v0_2")] pub struct Svr { pub type_: SvrType, pub vic: u8, pub dtd_index: u8, pub t7_t10_vtdb_index: u8, } libdisplay-info-0.2.2/src/cvt.rs000064400000000000000000000043621046102023000146500ustar 00000000000000//! Low-level API for VESA Coordinated Video Timings (CVT) version 2.0. use std::mem::MaybeUninit; use libdisplay_info_derive::FFIFrom; use crate::ffi; #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::cvt::di_cvt_reduced_blanking_version)] #[repr(u32)] pub enum ReducedBlankingVersion { None = ffi::cvt::di_cvt_reduced_blanking_version_DI_CVT_REDUCED_BLANKING_NONE, V1 = ffi::cvt::di_cvt_reduced_blanking_version_DI_CVT_REDUCED_BLANKING_V1, V2 = ffi::cvt::di_cvt_reduced_blanking_version_DI_CVT_REDUCED_BLANKING_V2, V3 = ffi::cvt::di_cvt_reduced_blanking_version_DI_CVT_REDUCED_BLANKING_V3, } /// Input parameters, defined in table 3-1. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cvt::di_cvt_options)] pub struct Options { pub red_blank_ver: ReducedBlankingVersion, pub h_pixels: i32, pub v_lines: i32, pub ip_freq_rqd: f64, pub video_opt: bool, pub vblank: f64, pub additional_hblank: i32, pub early_vsync_rqd: bool, pub int_rqd: bool, pub margins_rqd: bool, } /// Output parameters, defined in table 3-4. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::cvt::di_cvt_timing)] pub struct Timing { pub act_pixel_freq: f64, pub total_active_pixels: f64, pub v_lines_rnd: f64, pub h_front_porch: f64, pub h_sync: f64, pub h_back_porch: f64, pub v_front_porch: f64, pub v_sync: f64, pub v_back_porch: f64, pub act_frame_rate: f64, } impl Timing { /// Compute a timing via the CVT formula. pub fn compute(options: Options) -> Self { let mut timing = MaybeUninit::::uninit(); let options = ffi::cvt::di_cvt_options { red_blank_ver: options.red_blank_ver as u32, h_pixels: options.h_pixels, v_lines: options.v_lines, ip_freq_rqd: options.ip_freq_rqd, video_opt: options.video_opt, vblank: options.vblank, additional_hblank: options.additional_hblank, early_vsync_rqd: options.early_vsync_rqd, int_rqd: options.int_rqd, margins_rqd: options.margins_rqd, }; unsafe { ffi::cvt::di_cvt_compute(timing.as_mut_ptr(), &options) }; Timing::from(unsafe { timing.assume_init() }) } } libdisplay-info-0.2.2/src/displayid.rs000064400000000000000000000341141046102023000160340ustar 00000000000000//! Low-level API for VESA Display Identification Data (DisplayID). //! //! The library implements DisplayID version 1.3, available at: //! use std::marker::PhantomData; use libdisplay_info_derive::FFIFrom; use crate::{edid::ExtensionRef, ffi, FFIIter}; pub struct DisplayId<'ext> { display_id: *const ffi::displayid::di_displayid, phantom: PhantomData<&'ext ()>, } impl<'ext> DisplayId<'ext> { /// Get a DisplayID extension block. /// /// Returns `None` if the extension block tag is not [DisplayId](crate::edid::ExtensionTag::DisplayId). pub fn from_extension(extensions: &'ext ExtensionRef) -> Option> { let display_id = unsafe { ffi::edid::di_edid_ext_get_displayid(extensions.as_ptr()) }; if display_id.is_null() { None } else { Some(Self { display_id: display_id as *const ffi::displayid::di_displayid, phantom: PhantomData, }) } } /// Get the DisplayID version. pub fn version(&self) -> i32 { unsafe { ffi::displayid::di_displayid_get_version(self.display_id) } } /// Get the DisplayID revision. pub fn revision(&self) -> i32 { unsafe { ffi::displayid::di_displayid_get_revision(self.display_id) } } /// Get the DisplayID product type. pub fn product_type(&self) -> ProductType { ProductType::from(unsafe { ffi::displayid::di_displayid_get_product_type(self.display_id) }) } /// Get DisplayID data blocks pub fn data_blocks(&self) -> &[DataBlockRef] { let data_blocks = unsafe { ffi::displayid::di_displayid_get_data_blocks(self.display_id) }; let mut len = 0; while !unsafe { *data_blocks.offset(len) }.is_null() { len += 1; } unsafe { std::slice::from_raw_parts(data_blocks as *const DataBlockRef, len as usize) } } } /// Product type identifier, defined in section 2.3. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::displayid::di_displayid_product_type)] #[repr(u32)] pub enum ProductType { Extension = ffi::displayid::di_displayid_product_type_DI_DISPLAYID_PRODUCT_TYPE_EXTENSION, Test = ffi::displayid::di_displayid_product_type_DI_DISPLAYID_PRODUCT_TYPE_TEST, DisplayPanel = ffi::displayid::di_displayid_product_type_DI_DISPLAYID_PRODUCT_TYPE_DISPLAY_PANEL, StandaloneDisplay = ffi::displayid::di_displayid_product_type_DI_DISPLAYID_PRODUCT_TYPE_STANDALONE_DISPLAY, TvReceiver = ffi::displayid::di_displayid_product_type_DI_DISPLAYID_PRODUCT_TYPE_TV_RECEIVER, Repeater = ffi::displayid::di_displayid_product_type_DI_DISPLAYID_PRODUCT_TYPE_REPEATER, DirectDrive = ffi::displayid::di_displayid_product_type_DI_DISPLAYID_PRODUCT_TYPE_DIRECT_DRIVE, } /// DisplayID data block tag. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::displayid::di_displayid_product_type)] #[repr(u32)] pub enum DataBlockTag { ProductId = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_PRODUCT_ID, DisplayParams = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_DISPLAY_PARAMS, ColorCharact = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_COLOR_CHARACT, TypeITiming = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_TYPE_I_TIMING, TypeIITiming = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_TYPE_II_TIMING, TypeIIITiming = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING, TypIVTiming = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_TYPE_IV_TIMING, VesaTiming = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_VESA_TIMING, CeaTiming = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_CEA_TIMING, TimingRangeLimits = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_TIMING_RANGE_LIMITS, ProductSerial = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_PRODUCT_SERIAL, AsciiString = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_ASCII_STRING, DisplayDeviceData = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_DISPLAY_DEVICE_DATA, InterfacePowerSeq = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_INTERFACE_POWER_SEQ, TransferCharact = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_TRANSFER_CHARACT, DisplayInterface = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_DISPLAY_INTERFACE, StereoDisplayInterface = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_STEREO_DISPLAY_INTERFACE, TypeVTiming = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_TYPE_V_TIMING, TiledDisplayTopo = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_TILED_DISPLAY_TOPO, TypeVITiming = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_TYPE_VI_TIMING, } /// A DisplayID data block. #[repr(transparent)] pub struct DataBlockRef(*const ffi::displayid::di_displayid_data_block); impl DataBlockRef { /// Get a DisplayID data block tag. pub fn tag(&self) -> DataBlockTag { DataBlockTag::from(unsafe { ffi::displayid::di_displayid_data_block_get_tag(self.0) }) } /// Get display parameters from a DisplayID data block. /// /// Returns `None` if the data block tag isn't /// DI_DISPLAYID_DATA_BLOCK_DISPLAY_PARAMS. pub fn display_params(&self) -> Option { DisplayParams::from_ptr(unsafe { ffi::displayid::di_displayid_data_block_get_display_params(self.0) }) } /// Get type I timings from a DisplayID data block. /// Returns `None` if the data block tag isn't /// DI_DISPLAYID_DATA_BLOCK_TYPE_I_TIMING. pub fn type_i_timings(&self) -> impl Iterator { FFIIter::new(unsafe { ffi::displayid::di_displayid_data_block_get_type_i_timings(self.0) }) } /// Get type II timings from a DisplayID data block. /// Returns `None` if the data block tag isn't /// DI_DISPLAYID_DATA_BLOCK_TYPE_II_TIMING. #[cfg(feature = "v0_2")] pub fn type_ii_timings(&self) -> impl Iterator { FFIIter::new(unsafe { ffi::displayid::di_displayid_data_block_get_type_ii_timings(self.0) }) } /// Get type III timings from a DisplayID data block. /// Returns `None` if the data block tag isn't /// DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING. #[cfg(feature = "v0_2")] pub fn type_iii_timings(&self) -> impl Iterator { FFIIter::new(unsafe { ffi::displayid::di_displayid_data_block_get_type_iii_timings(self.0) }) } /// Get tiled display topology from a DisplayID data block. /// /// Returns `None` if the data block tag isn't /// DI_DISPLAYID_DATA_BLOCK_TILED_DISPLAY_TOPO. pub fn tiled_topo(&self) -> Option { TiledTopo::from_ptr(unsafe { ffi::displayid::di_displayid_data_block_get_tiled_topo(self.0) }) } } /// Display parameters feature support flags, defined in section 4.2.3. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::displayid::di_displayid_display_params_features)] pub struct DisplayParamsFeatures { pub audio: bool, pub separate_audio_inputs: bool, pub audio_input_override: bool, pub power_management: bool, pub fixed_timing: bool, pub fixed_pixel_format: bool, pub ai: bool, pub deinterlacing: bool, } /// Display parameters data block, defined in section 4.2. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::displayid::di_displayid_display_params)] pub struct DisplayParams { pub horiz_image_mm: f32, pub vert_image_mm: f32, pub horiz_pixels: i32, pub vert_pixels: i32, #[ptr_deref] pub features: Option, #[optional(0f32)] pub gamma: Option, pub aspect_ratio: f32, pub bits_per_color_overall: i32, pub bits_per_color_native: i32, } #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::displayid::di_displayid_type_i_ii_vii_timing_stereo_3d)] #[repr(u32)] pub enum TypeIIIVIITimingStereo3d { Never = ffi::displayid::di_displayid_type_i_ii_vii_timing_stereo_3d_DI_DISPLAYID_TYPE_I_II_VII_TIMING_STEREO_3D_NEVER, Always = ffi::displayid::di_displayid_type_i_ii_vii_timing_stereo_3d_DI_DISPLAYID_TYPE_I_II_VII_TIMING_STEREO_3D_ALWAYS, User = ffi::displayid::di_displayid_type_i_ii_vii_timing_stereo_3d_DI_DISPLAYID_TYPE_I_II_VII_TIMING_STEREO_3D_USER, } #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::displayid::di_displayid_timing_aspect_ratio)] #[repr(u32)] pub enum TimingAspectRatio { _1_1 = ffi::displayid::di_displayid_timing_aspect_ratio_DI_DISPLAYID_TIMING_ASPECT_RATIO_1_1, _5_4 = ffi::displayid::di_displayid_timing_aspect_ratio_DI_DISPLAYID_TIMING_ASPECT_RATIO_5_4, _4_3 = ffi::displayid::di_displayid_timing_aspect_ratio_DI_DISPLAYID_TIMING_ASPECT_RATIO_4_3, _15_9 = ffi::displayid::di_displayid_timing_aspect_ratio_DI_DISPLAYID_TIMING_ASPECT_RATIO_15_9, _16_9 = ffi::displayid::di_displayid_timing_aspect_ratio_DI_DISPLAYID_TIMING_ASPECT_RATIO_16_9, _16_10 = ffi::displayid::di_displayid_timing_aspect_ratio_DI_DISPLAYID_TIMING_ASPECT_RATIO_16_10, _64_27 = ffi::displayid::di_displayid_timing_aspect_ratio_DI_DISPLAYID_TIMING_ASPECT_RATIO_64_27, _256_135 = ffi::displayid::di_displayid_timing_aspect_ratio_DI_DISPLAYID_TIMING_ASPECT_RATIO_256_135, Undefined = ffi::displayid::di_displayid_timing_aspect_ratio_DI_DISPLAYID_TIMING_ASPECT_RATIO_UNDEFINED, } #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::displayid::di_displayid_type_i_ii_vii_timing_sync_polarity)] #[repr(u32)] pub enum TypeIIIVIITimingSyncPolarity { Negative = ffi::displayid::di_displayid_type_i_ii_vii_timing_sync_polarity_DI_DISPLAYID_TYPE_I_II_VII_TIMING_SYNC_NEGATIVE, Positive = ffi::displayid::di_displayid_type_i_ii_vii_timing_sync_polarity_DI_DISPLAYID_TYPE_I_II_VII_TIMING_SYNC_POSITIVE, } /// Type I timing, defined in section 4.4.1. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::displayid::di_displayid_type_i_ii_vii_timing)] pub struct TypeIIIVIITiming { pub pixel_clock_mhz: f64, pub preferred: bool, pub stereo_3d: TypeIIIVIITimingStereo3d, pub interlaced: bool, pub aspect_ratio: TimingAspectRatio, pub horiz_active: i32, pub vert_active: i32, pub horiz_blank: i32, pub vert_blank: i32, pub horiz_offset: i32, pub vert_offset: i32, pub horiz_sync_width: i32, pub vert_sync_width: i32, pub horiz_sync_polarity: TypeIIIVIITimingSyncPolarity, pub vert_sync_polarity: TypeIIIVIITimingSyncPolarity, } /// Formula/algorithm for type III timings. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::displayid::di_displayid_type_iii_timing_algo)] #[repr(u32)] #[cfg(feature = "v0_2")] pub enum TyoeIIITimingAlgo { CvtStandardBlanking = ffi::displayid::di_displayid_type_iii_timing_algo_DI_DISPLAYID_TYPE_III_TIMING_CVT_STANDARD_BLANKING, CvtReducedBlacking = ffi::displayid::di_displayid_type_iii_timing_algo_DI_DISPLAYID_TYPE_III_TIMING_CVT_REDUCED_BLANKING, } /// Type I timing, defined in section 4.4.1. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::displayid::di_displayid_type_iii_timing)] #[cfg(feature = "v0_2")] pub struct TypeIIITiming { pub preferred: bool, pub algo: TyoeIIITimingAlgo, pub aspect_ratio: TimingAspectRatio, pub horiz_active: i32, pub interlaced: bool, pub refresh_rate_hz: i32, } /// Behavior when more than 1 tile and less than total number of tiles are driven /// by the source. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::displayid::di_displayid_tiled_topo_missing_recv_behavior)] #[repr(u32)] pub enum TiledTopoMissingRecvBehavior { Undef = ffi::displayid::di_displayid_tiled_topo_missing_recv_behavior_DI_DISPLAYID_TILED_TOPO_MISSING_RECV_UNDEF, TileOnly = ffi::displayid::di_displayid_tiled_topo_missing_recv_behavior_DI_DISPLAYID_TILED_TOPO_MISSING_RECV_TILE_ONLY, } /// Behavior of this tile when it is the only tile receiving an image from the /// source. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::displayid::di_displayid_tiled_topo_single_recv_behavior)] #[repr(u32)] pub enum TiledTopoSingleRecvBehavior { Undef = ffi::displayid::di_displayid_tiled_topo_single_recv_behavior_DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_UNDEF, TileOnly = ffi::displayid::di_displayid_tiled_topo_single_recv_behavior_DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_TILE_ONLY, Scaled = ffi::displayid::di_displayid_tiled_topo_single_recv_behavior_DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_SCALED, Cloned = ffi::displayid::di_displayid_tiled_topo_single_recv_behavior_DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_CLONED, } /// Tiled display capabilities. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::displayid::di_displayid_tiled_topo_caps)] pub struct TiledTopoCaps { pub single_enclosure: bool, pub missing_recv_behavior: TiledTopoMissingRecvBehavior, pub single_recv_behavior: TiledTopoSingleRecvBehavior, } /// Tiled display bezel information. /// /// The lengths are measured in pixels, accurate to the tenths place. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::displayid::di_displayid_tiled_topo_bezel)] pub struct TiledTopoBezel { pub top_px: f32, pub bottom_px: f32, pub right_px: f32, pub left_px: f32, } /// Tiled display topology, defined in section 4.14. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::displayid::di_displayid_tiled_topo)] pub struct TiledTopo { #[ptr_deref] pub caps: Option, pub total_horiz_tiles: i32, pub total_vert_tiles: i32, pub horiz_tile_location: i32, pub vert_tile_location: i32, pub horiz_tile_pixels: i32, pub vert_tile_lines: i32, #[ptr_deref] pub bezel: Option, #[cast_as(u8)] pub vendor_id: [char; 3usize], pub product_code: u16, pub serial_number: u32, } libdisplay-info-0.2.2/src/dmt.rs000064400000000000000000000013241046102023000146330ustar 00000000000000//! Low-level API for VESA Display Monitor Timing (DMT). //! //! The library implements VESA DMT version 1.0 revision 13. use libdisplay_info_derive::FFIFrom; use crate::ffi; // A DMT timing. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::dmt::di_dmt_timing)] pub struct Timing { pub dmt_id: u8, pub edid_std_id: u16, pub cvt_id: u32, pub horiz_video: i32, pub vert_video: i32, pub refresh_rate_hz: f32, pub pixel_clock_hz: i32, pub horiz_blank: i32, pub vert_blank: i32, pub horiz_front_porch: i32, pub vert_front_porch: i32, pub horiz_sync_pulse: i32, pub vert_sync_pulse: i32, pub horiz_border: i32, pub vert_border: i32, pub reduced_blanking: bool, } libdisplay-info-0.2.2/src/edid.rs000064400000000000000000000750221046102023000147620ustar 00000000000000//! Low-level API for Extended Display Identification Data (EDID). //! //! EDID 1.4 is defined in VESA Enhanced Extended Display Identification Data //! Standard release A revision 2. use std::marker::PhantomData; use libdisplay_info_derive::FFIFrom; use crate::{dmt, ffi, string_from_ffi_ptr, FFIIter}; /// EDID data structure. #[derive(Debug)] pub struct Edid<'info> { edid: *const ffi::edid::di_edid, phantom: PhantomData<&'info ()>, } impl Edid<'_> { /// Get the EDID version. pub fn version(&self) -> i32 { unsafe { ffi::edid::di_edid_get_version(self.edid) } } /// Get the EDID revision. pub fn revision(&self) -> i32 { unsafe { ffi::edid::di_edid_get_revision(self.edid) } } /// Get the EDID vendor product pub fn vendor_product(&self) -> VendorProduct { VendorProduct::from(unsafe { *ffi::edid::di_edid_get_vendor_product(self.edid) }) } /// Get the analog video input basic information. /// /// Returns `None` if this isn't an analog display. pub fn video_input_analog(&self) -> Option { VideoInputAnalog::from_ptr(unsafe { ffi::edid::di_edid_get_video_input_analog(self.edid) }) } /// Get the digital video input basic information. /// /// Returns `None` if this isn't a digital display. pub fn video_input_digital(&self) -> Option { VideoInputDigital::from_ptr(unsafe { ffi::edid::di_edid_get_video_input_digital(self.edid) }) } /// Get the screen size. pub fn screen_size(&self) -> ScreenSize { ScreenSize::from_ptr(unsafe { ffi::edid::di_edid_get_screen_size(self.edid) }) .expect("expected non null ptr") } /// Get the display transfer characteristics from the basic EDID parameters, also /// known as \"gamma\". /// /// Returns `None` if unset (ie, stored in an extension block). pub fn basic_gamma(&self) -> Option { let basic_gamma = unsafe { ffi::edid::di_edid_get_basic_gamma(self.edid) }; if basic_gamma == 0f32 { None } else { Some(basic_gamma) } } /// Get the set of supported legacy DPMS states. pub fn dpms(&self) -> Dpms { Dpms::from_ptr(unsafe { ffi::edid::di_edid_get_dpms(self.edid) }) .expect("expected non null ptr") } /// Get the display color type. /// /// For digital displays using EDID 1.4 and later, [`DisplayColorType::Undefined`] /// is always returned. pub fn display_color_type(&self) -> DisplayColorType { DisplayColorType::from(unsafe { ffi::edid::di_edid_get_display_color_type(self.edid) }) } /// Get the set of supported color encoding formats. /// /// Returns `None` if the display is analog or if the color encoding formats are /// not specified. pub fn color_encoding_formats(&self) -> Option { ColorEncodingFormats::from_ptr(unsafe { ffi::edid::di_edid_get_color_encoding_formats(self.edid) }) } /// Get the set of miscellaneous basic features. pub fn misc_features(&self) -> MiscFeatures { MiscFeatures::from_ptr(unsafe { ffi::edid::di_edid_get_misc_features(self.edid) }) .expect("expected non null ptr") } /// Get chromaticity coordinates. pub fn chromaticity_coords(&self) -> ChromaticityCoords { ChromaticityCoords::from_ptr(unsafe { ffi::edid::di_edid_get_chromaticity_coords(self.edid) }) .expect("expected non null ptr") } /// Get established timings I and II. pub fn established_timings(&self) -> EstablishedTimings { EstablishedTimings::from_ptr(unsafe { ffi::edid::di_edid_get_established_timings_i_ii(self.edid) }) .expect("expected non null ptr") } /// Get a list of EDID standard timings. pub fn standard_timings(&self) -> &[StandardTimingRef] { let standard_timings = unsafe { ffi::edid::di_edid_get_standard_timings(self.edid) }; let mut len = 0; while !unsafe { *standard_timings.offset(len) }.is_null() { len += 1; } unsafe { std::slice::from_raw_parts(standard_timings as *const StandardTimingRef, len as usize) } } // Get a list of EDID detailed timing definitions. pub fn detailed_timing_defs(&self) -> impl Iterator { FFIIter::new(unsafe { ffi::edid::di_edid_get_detailed_timing_defs(self.edid) }) } /// Get a list of EDID display descriptors. pub fn display_descriptors(&self) -> &[DisplayDescriptorRef] { let display_descriptors = unsafe { ffi::edid::di_edid_get_display_descriptors(self.edid) }; let mut len = 0; while !unsafe { *display_descriptors.offset(len) }.is_null() { len += 1; } unsafe { std::slice::from_raw_parts( display_descriptors as *const DisplayDescriptorRef, len as usize, ) } } /// Get a list of EDID extensions. pub fn extensions(&self) -> &[ExtensionRef] { let extensions = unsafe { ffi::edid::di_edid_get_extensions(self.edid) }; let mut len = 0; while !unsafe { *extensions.offset(len) }.is_null() { len += 1; } unsafe { std::slice::from_raw_parts(extensions as *const ExtensionRef, len as usize) } } pub(crate) fn from_ptr(ptr: *const ffi::edid::di_edid) -> Option { if ptr.is_null() { None } else { Some(Self { edid: ptr, phantom: PhantomData, }) } } } /// EDID vendor & product identification. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_vendor_product)] pub struct VendorProduct { #[cast_as(u8)] pub manufacturer: [char; 3usize], pub product: u16, #[optional(0u32)] pub serial: Option, pub manufacture_week: i32, pub manufacture_year: i32, #[optional(0i32)] pub model_year: Option, } /// EDID analog signal level standard. #[derive(Debug, Copy, Clone, PartialEq, Eq, FFIFrom)] #[ffi(ffi::edid::di_edid_video_input_analog_signal_level_std)] #[repr(u32)] pub enum VideoInputAnalogSignalLevelStandard { Level0 = ffi::edid::di_edid_video_input_analog_signal_level_std_DI_EDID_VIDEO_INPUT_ANALOG_SIGNAL_LEVEL_0, Level1 = ffi::edid::di_edid_video_input_analog_signal_level_std_DI_EDID_VIDEO_INPUT_ANALOG_SIGNAL_LEVEL_1, Level2 = ffi::edid::di_edid_video_input_analog_signal_level_std_DI_EDID_VIDEO_INPUT_ANALOG_SIGNAL_LEVEL_2, Level3 = ffi::edid::di_edid_video_input_analog_signal_level_std_DI_EDID_VIDEO_INPUT_ANALOG_SIGNAL_LEVEL_3, } /// EDID analog video setup. #[derive(Debug, Copy, Clone, PartialEq, Eq, FFIFrom)] #[ffi(ffi::edid::di_edid_video_input_analog_video_setup)] #[repr(u32)] pub enum VideoInputAnalogVideoSetup { BlankLevelEqBlack = ffi::edid::di_edid_video_input_analog_video_setup_DI_EDID_VIDEO_INPUT_ANALOG_BLANK_LEVEL_EQ_BLACK, BlankToBlackSetupPedestal = ffi::edid::di_edid_video_input_analog_video_setup_DI_EDID_VIDEO_INPUT_ANALOG_BLANK_TO_BLACK_SETUP_PEDESTAL, } /// EDID analog video input basic information, defined in section 3.6.1. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_video_input_analog)] pub struct VideoInputAnalog { pub signal_level_std: VideoInputAnalogSignalLevelStandard, pub video_setup: VideoInputAnalogVideoSetup, pub sync_separate: bool, pub sync_composite: bool, pub sync_on_green: bool, pub sync_serrations: bool, } /// Digital video input interface standard. #[derive(Debug, Copy, Clone, PartialEq, Eq, FFIFrom)] #[ffi(ffi::edid::di_edid_video_input_digital_interface)] #[repr(u32)] pub enum VideoInputDigitalInterface { Undefined = ffi::edid::di_edid_video_input_digital_interface_DI_EDID_VIDEO_INPUT_DIGITAL_UNDEFINED, DVI = ffi::edid::di_edid_video_input_digital_interface_DI_EDID_VIDEO_INPUT_DIGITAL_DVI, HDMIA = ffi::edid::di_edid_video_input_digital_interface_DI_EDID_VIDEO_INPUT_DIGITAL_HDMI_A, HDMIB = ffi::edid::di_edid_video_input_digital_interface_DI_EDID_VIDEO_INPUT_DIGITAL_HDMI_B, MDDI = ffi::edid::di_edid_video_input_digital_interface_DI_EDID_VIDEO_INPUT_DIGITAL_MDDI, DisplayPort = ffi::edid::di_edid_video_input_digital_interface_DI_EDID_VIDEO_INPUT_DIGITAL_DISPLAYPORT, } /// EDID digital video input basic information, defined in section 3.6.1. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_video_input_digital)] pub struct VideoInputDigital { pub dfp1: bool, #[optional(0i32)] pub color_bit_depth: Option, pub interface: VideoInputDigitalInterface, } /// Screen size #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_screen_size)] pub struct ScreenSize { #[optional(0i32)] pub width_cm: Option, #[optional(0i32)] pub height_cm: Option, #[optional(0f32)] pub landscape_aspect_ratio: Option, #[optional(0f32)] pub portait_aspect_ratio: Option, } /// Supported legacy Display Power Management Signaling (DPMS) states, defined in /// section 3.6.4. /// /// Display Power Management (DPM) compliant displays only support \"off\"."] #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_dpms)] pub struct Dpms { pub standby: bool, pub suspend: bool, pub off: bool, } /// Display color type. #[derive(Debug, Copy, Clone, PartialEq, Eq, FFIFrom)] #[ffi(ffi::edid::di_edid_display_color_type)] #[repr(u32)] pub enum DisplayColorType { Monochrome = ffi::edid::di_edid_display_color_type_DI_EDID_DISPLAY_COLOR_MONOCHROME, RGB = ffi::edid::di_edid_display_color_type_DI_EDID_DISPLAY_COLOR_RGB, NonRGB = ffi::edid::di_edid_display_color_type_DI_EDID_DISPLAY_COLOR_NON_RGB, Undefined = ffi::edid::di_edid_display_color_type_DI_EDID_DISPLAY_COLOR_UNDEFINED, } /// Basic color encoding formats, defined in section 3.6.4." #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_color_encoding_formats)] pub struct ColorEncodingFormats { pub rgb444: bool, pub ycrcb444: bool, pub ycrcb422: bool, } /// Miscellaneous basic features, defined in section 3.6.4. /// /// Note, the enum values don't match the specification. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_misc_features)] pub struct MiscFeatures { /// First detailed timing is the preferred timing. /// /// Always set for EDID 1.4 and later. pub has_preferred_timing: bool, /// GTF using the default parameters is supported. /// /// Never set for EDID 1.4 and later. pub default_gtf: bool, /// sRGB standard default color space is primary color space. pub srgb_is_primary: bool, /// Preferred timing mode includes native pixel format and rate. /// /// Never set for EDID 1.3 and earlier. pub preferred_timing_is_native: bool, /// GTF or CVT generated timings within the display's range limits are /// accepted. /// /// Never set for EDID 1.3 and earlier. pub continuous_freq: bool, } /// EDID display chromaticity coordinates, defined in section 3.7. /// /// The values are accurate to the thousandth place. The red, green and blue /// values are zero for monochrome displays. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_chromaticity_coords)] pub struct ChromaticityCoords { pub red_x: f32, pub red_y: f32, pub green_x: f32, pub green_y: f32, pub blue_x: f32, pub blue_y: f32, pub white_x: f32, pub white_y: f32, } /// Established timings I and II, defined in section 3.8. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_established_timings_i_ii)] pub struct EstablishedTimings { pub has_720x400_70hz: bool, pub has_720x400_88hz: bool, pub has_640x480_60hz: bool, pub has_640x480_67hz: bool, pub has_640x480_72hz: bool, pub has_640x480_75hz: bool, pub has_800x600_56hz: bool, pub has_800x600_60hz: bool, pub has_800x600_72hz: bool, pub has_800x600_75hz: bool, pub has_832x624_75hz: bool, pub has_1024x768_87hz_interlaced: bool, pub has_1024x768_60hz: bool, pub has_1024x768_70hz: bool, pub has_1024x768_75hz: bool, pub has_1280x1024_75hz: bool, pub has_1152x870_75hz: bool, } /// Aspect ratio for an EDID standard timing. #[derive(Debug, Copy, Clone, PartialEq, Eq, FFIFrom)] #[ffi(ffi::edid::di_edid_standard_timing_aspect_ratio)] #[repr(u32)] pub enum StandardTimingAspectRatio { _16_10 = ffi::edid::di_edid_standard_timing_aspect_ratio_DI_EDID_STANDARD_TIMING_16_10, _4_3 = ffi::edid::di_edid_standard_timing_aspect_ratio_DI_EDID_STANDARD_TIMING_4_3, _5_4 = ffi::edid::di_edid_standard_timing_aspect_ratio_DI_EDID_STANDARD_TIMING_5_4, _16_9 = ffi::edid::di_edid_standard_timing_aspect_ratio_DI_EDID_STANDARD_TIMING_16_9, } /// EDID standard timing, defined in section 3.9. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_standard_timing)] #[wrap] pub struct StandardTiming { pub horiz_video: i32, pub aspect_ratio: StandardTimingAspectRatio, pub refresh_rate_hz: i32, } impl StandardTimingRef { /// Get the vertical addressable line count of an EDID standard timing. pub fn vert_video(&self) -> i32 { unsafe { ffi::edid::di_edid_standard_timing_get_vert_video(self.0) } } /// Get the VESA Display Monitor Timing (DMT), if any. /// /// `None` is returned if the standard timing doesn't have a DMT. pub fn dmt(&self) -> Option { let dmt = unsafe { ffi::edid::di_edid_standard_timing_get_dmt(self.0) }; if dmt.is_null() { None } else { Some(crate::dmt::Timing::from(unsafe { *(dmt as *const ffi::dmt::di_dmt_timing) })) } } } /// Stereo viewing support. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::edid::di_edid_detailed_timing_def_stereo)] #[repr(u32)] pub enum DetailedTimingDefStereo { NONE = ffi::edid::di_edid_detailed_timing_def_stereo_DI_EDID_DETAILED_TIMING_DEF_STEREO_NONE, FieldSeqRight = ffi::edid::di_edid_detailed_timing_def_stereo_DI_EDID_DETAILED_TIMING_DEF_STEREO_FIELD_SEQ_RIGHT, FieldSeqLeft = ffi::edid::di_edid_detailed_timing_def_stereo_DI_EDID_DETAILED_TIMING_DEF_STEREO_FIELD_SEQ_LEFT, TwoWayInterleavedRight = ffi::edid::di_edid_detailed_timing_def_stereo_DI_EDID_DETAILED_TIMING_DEF_STEREO_2_WAY_INTERLEAVED_RIGHT, TwoWayInterleavedLeft = ffi::edid::di_edid_detailed_timing_def_stereo_DI_EDID_DETAILED_TIMING_DEF_STEREO_2_WAY_INTERLEAVED_LEFT, FourWayInterleaved = ffi::edid::di_edid_detailed_timing_def_stereo_DI_EDID_DETAILED_TIMING_DEF_STEREO_4_WAY_INTERLEAVED, SideBySideInterleaved = ffi::edid::di_edid_detailed_timing_def_stereo_DI_EDID_DETAILED_TIMING_DEF_STEREO_SIDE_BY_SIDE_INTERLEAVED, } /// Signal definitions for EDID detailed timings, defined in notes for table 3.22. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::edid::di_edid_detailed_timing_def_signal_type)] #[repr(u32)] pub enum DetailedTimingDefSignalType { AnalogComposite = ffi::edid::di_edid_detailed_timing_def_signal_type_DI_EDID_DETAILED_TIMING_DEF_SIGNAL_ANALOG_COMPOSITE, BipolarAnalogComposite = ffi::edid::di_edid_detailed_timing_def_signal_type_DI_EDID_DETAILED_TIMING_DEF_SIGNAL_BIPOLAR_ANALOG_COMPOSITE, DigitalComposite = ffi::edid::di_edid_detailed_timing_def_signal_type_DI_EDID_DETAILED_TIMING_DEF_SIGNAL_DIGITAL_COMPOSITE, DigitalSeparate = ffi::edid::di_edid_detailed_timing_def_signal_type_DI_EDID_DETAILED_TIMING_DEF_SIGNAL_DIGITAL_SEPARATE, } /// Digital separate sync polarity for EDID detailed timings. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::edid::di_edid_detailed_timing_def_sync_polarity)] #[repr(u32)] pub enum DetailedTimingDefSyncPolarity { Negative = ffi::edid::di_edid_detailed_timing_def_sync_polarity_DI_EDID_DETAILED_TIMING_DEF_SYNC_NEGATIVE, Positive = ffi::edid::di_edid_detailed_timing_def_sync_polarity_DI_EDID_DETAILED_TIMING_DEF_SYNC_POSITIVE, } /// Flags for ANALOG_COMPOSITE signals #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_detailed_timing_analog_composite)] pub struct DetailedTimingAnalogComposite { pub sync_serrations: bool, pub sync_on_green: bool, } // Flags for BIPOLAR_ANALOG_COMPOSITE signals #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_detailed_timing_bipolar_analog_composite)] pub struct DetailedTimingBipolarAnalogComposite { pub sync_serrations: bool, pub sync_on_green: bool, } // Flags for DIGITAL_COMPOSITE signals #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_detailed_timing_digital_composite)] pub struct DetailedTimingDigitalComposite { pub sync_serrations: bool, pub sync_horiz_polarity: DetailedTimingDefSyncPolarity, } /// Flags for DIGITAL_SEPARATE signals #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_detailed_timing_digital_separate)] pub struct DetailedTimingDigitalSeparate { pub sync_vert_polarity: DetailedTimingDefSyncPolarity, pub sync_horiz_polarity: DetailedTimingDefSyncPolarity, } /// EDID detailed timing definition, defined in section 3.10.2. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_detailed_timing_def)] pub struct DetailedTimingDef { pub pixel_clock_hz: i32, pub horiz_video: i32, pub vert_video: i32, pub horiz_blank: i32, pub vert_blank: i32, pub horiz_front_porch: i32, pub vert_front_porch: i32, pub horiz_sync_pulse: i32, pub vert_sync_pulse: i32, pub horiz_image_mm: i32, pub vert_image_mm: i32, #[optional(0i32)] pub horiz_border: Option, #[optional(0i32)] pub vert_border: Option, pub interlaced: bool, pub stereo: DetailedTimingDefStereo, pub signal_type: DetailedTimingDefSignalType, #[ptr_deref] pub analog_composite: Option, #[ptr_deref] pub bipolar_analog_composite: Option, #[ptr_deref] pub digital_composite: Option, #[ptr_deref] pub digital_separate: Option, } /// EDID display descriptor tag, defined in section 3.10.3. #[derive(Debug, Copy, Clone, PartialEq, Eq, FFIFrom)] #[ffi(ffi::edid::di_edid_display_descriptor_tag)] #[repr(u32)] pub enum DisplayDescriptorTag { ProductSerial = ffi::edid::di_edid_display_descriptor_tag_DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_SERIAL, DataString = ffi::edid::di_edid_display_descriptor_tag_DI_EDID_DISPLAY_DESCRIPTOR_DATA_STRING, RangeLimits = ffi::edid::di_edid_display_descriptor_tag_DI_EDID_DISPLAY_DESCRIPTOR_RANGE_LIMITS, ProductName = ffi::edid::di_edid_display_descriptor_tag_DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_NAME, ColorPoint = ffi::edid::di_edid_display_descriptor_tag_DI_EDID_DISPLAY_DESCRIPTOR_COLOR_POINT, StdTimingIds = ffi::edid::di_edid_display_descriptor_tag_DI_EDID_DISPLAY_DESCRIPTOR_STD_TIMING_IDS, DcmData = ffi::edid::di_edid_display_descriptor_tag_DI_EDID_DISPLAY_DESCRIPTOR_DCM_DATA, CvtTimingCodes = ffi::edid::di_edid_display_descriptor_tag_DI_EDID_DISPLAY_DESCRIPTOR_CVT_TIMING_CODES, EstablishedTimingsIII = ffi::edid::di_edid_display_descriptor_tag_DI_EDID_DISPLAY_DESCRIPTOR_ESTABLISHED_TIMINGS_III, Dummy = ffi::edid::di_edid_display_descriptor_tag_DI_EDID_DISPLAY_DESCRIPTOR_DUMMY, } // EDID display descriptor. #[derive(Debug)] pub struct DisplayDescriptorRef(*const ffi::edid::di_edid_display_descriptor); impl DisplayDescriptorRef { /// Get the tag of an EDID display descriptor. pub fn tag(&self) -> DisplayDescriptorTag { DisplayDescriptorTag::from(unsafe { ffi::edid::di_edid_display_descriptor_get_tag(self.0) }) } /// Get the contents of a product serial number, a data string, or a product name /// display descriptor. /// /// Returns `None` if the display descriptor tag isn't either /// DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_SERIAL_NUMBER, /// DI_EDID_DISPLAY_DESCRIPTOR_DATA_STRING or /// DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_NAME. pub fn string(&self) -> Option { string_from_ffi_ptr(unsafe { ffi::edid::di_edid_display_descriptor_get_string(self.0) }) } /// Get the contents of a display range limits descriptor. /// /// Returns `None` if the display descriptor tag isn't /// DI_EDID_DISPLAY_DESCRIPTOR_RANGE_LIMITS. pub fn range_limits(&self) -> Option { let display_range_limits = unsafe { ffi::edid::di_edid_display_descriptor_get_range_limits(self.0) }; if display_range_limits.is_null() { None } else { Some(DisplayRangeLimits::from(unsafe { *display_range_limits })) } } /// Get a standard timing list from an EDID display descriptor. /// /// Returns `None` if the display descriptor tag isn't /// DI_EDID_DISPLAY_DESCRIPTOR_STD_TIMING_IDS. pub fn standard_timings(&self) -> Option<&[StandardTimingRef]> { let standard_timings = unsafe { ffi::edid::di_edid_display_descriptor_get_standard_timings(self.0) }; if standard_timings.is_null() { None } else { let mut len = 0; while !unsafe { *standard_timings.offset(len) }.is_null() { len += 1; } Some(unsafe { std::slice::from_raw_parts( standard_timings as *const StandardTimingRef, len as usize, ) }) } } /// Get a color point list from an EDID display descriptor. /// /// Returns `None` if the display descriptor tag isn't /// DI_EDID_DISPLAY_DESCRIPTOR_COLOR_POINT. /// /// Upstream is not aware of any EDID blob containing Color Point Descriptors. /// If such a blob is found, please share it with upstream! pub fn color_points(&self) -> impl Iterator { FFIIter::new(unsafe { ffi::edid::di_edid_display_descriptor_get_color_points(self.0) }) } /// Get a list of established timings III from an EDID display descriptor. /// /// Returns `None` if the display descriptor tag isn't /// DI_EDID_DISPLAY_DESCRIPTOR_ESTABLISHED_TIMINGS_III. pub fn established_timings_iii(&self) -> impl Iterator { FFIIter::new(unsafe { ffi::edid::di_edid_display_descriptor_get_established_timings_iii(self.0) as *const *const ffi::dmt::di_dmt_timing }) } /// Get the contents of a Display Color Management (DCM) Data descriptor. /// /// Returns `None` if the display descriptor tag isn't /// DI_EDID_DISPLAY_DESCRIPTOR_DCM_DATA. /// /// Upstream is not aware of any EDID blob containing DCM Data descriptors. /// If such a blob is found, please share it with upstream! pub fn color_management_data(&self) -> Option { let cmd = unsafe { ffi::edid::di_edid_display_descriptor_get_color_management_data(self.0) }; if cmd.is_null() { None } else { Some(ColorManagementData::from(unsafe { *cmd })) } } /// Get a list of CVT timing codes from an EDID display descriptor. /// /// The highest priority code comes first, the lowest priority code last. /// /// Returns `None` if the display descriptor tag isn't /// DI_EDID_DISPLAY_DESCRIPTOR_CVT_TIMING_CODES. pub fn cvt_timing_codes(&self) -> impl Iterator { FFIIter::new(unsafe { ffi::edid::di_edid_display_descriptor_get_cvt_timing_codes(self.0) }) } } /// EDID display range limits type. /// /// The values do not match the EDID specification. /// /// The CVT entry was introduced in EDID 1.4. #[derive(Debug, Copy, Clone, PartialEq, Eq, FFIFrom)] #[ffi(ffi::edid::di_edid_display_range_limits_type)] #[repr(u32)] pub enum DisplayRangeLimitsType { Bare = ffi::edid::di_edid_display_range_limits_type_DI_EDID_DISPLAY_RANGE_LIMITS_BARE, DefaultGtf = ffi::edid::di_edid_display_range_limits_type_DI_EDID_DISPLAY_RANGE_LIMITS_DEFAULT_GTF, SecondaryGtf = ffi::edid::di_edid_display_range_limits_type_DI_EDID_DISPLAY_RANGE_LIMITS_SECONDARY_GTF, Cvt = ffi::edid::di_edid_display_range_limits_type_DI_EDID_DISPLAY_RANGE_LIMITS_CVT, } #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_display_range_limits_secondary_gtf)] pub struct DisplayRangeLimitsSecondaryGtf { pub start_freq_hz: ::std::os::raw::c_int, pub c: f32, pub m: f32, pub k: f32, pub j: f32, } bitflags::bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct CvtAspectRatio: u32 { const _4_3 = ffi::edid::di_edid_cvt_aspect_ratio_DI_EDID_CVT_ASPECT_RATIO_4_3; const _16_9 = ffi::edid::di_edid_cvt_aspect_ratio_DI_EDID_CVT_ASPECT_RATIO_16_9; const _16_10 = ffi::edid::di_edid_cvt_aspect_ratio_DI_EDID_CVT_ASPECT_RATIO_16_10; const _5_4 = ffi::edid::di_edid_cvt_aspect_ratio_DI_EDID_CVT_ASPECT_RATIO_5_4; const _15_9 = ffi::edid::di_edid_cvt_aspect_ratio_DI_EDID_CVT_ASPECT_RATIO_15_9; const _ = !0; } } impl From for CvtAspectRatio { fn from(value: ffi::edid::di_edid_cvt_aspect_ratio) -> Self { CvtAspectRatio::from_bits_retain(value) } } bitflags::bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct CvtScaling: u32 { const HorizShrink = ffi::edid::di_edid_cvt_scaling_DI_EDID_CVT_SCALING_HORIZ_SHRINK; const HorizStretch = ffi::edid::di_edid_cvt_scaling_DI_EDID_CVT_SCALING_HORIZ_STRETCH; const VertShrink = ffi::edid::di_edid_cvt_scaling_DI_EDID_CVT_SCALING_VERT_SHRINK; const VertStretch = ffi::edid::di_edid_cvt_scaling_DI_EDID_CVT_SCALING_VERT_STRETCH; const _ = !0; } } impl From for CvtScaling { fn from(value: ffi::edid::di_edid_cvt_scaling) -> Self { CvtScaling::from_bits_retain(value) } } #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_display_range_limits_cvt)] pub struct DisplayRangeLimitsCvt { pub version: i32, pub revision: i32, #[optional(0i32)] pub max_horiz_px: Option, pub supported_aspect_ratio: CvtAspectRatio, pub preferred_aspect_ratio: CvtAspectRatio, pub standard_blanking: bool, pub reduced_blanking: bool, pub supported_scaling: CvtScaling, pub preferred_vert_refresh_hz: i32, } /// EDID display range limits, defined in section 3.10.3.3.1. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_display_range_limits)] pub struct DisplayRangeLimits { pub min_vert_rate_hz: i32, pub max_vert_rate_hz: i32, pub min_horiz_rate_hz: i32, pub max_horiz_rate_hz: i32, #[optional(0)] pub max_pixel_clock_hz: Option, pub type_: DisplayRangeLimitsType, #[ptr_deref] pub secondary_gtf: Option, #[ptr_deref] pub cvt: Option, } // EDID Color Points, defined in section 3.10.3.5. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_color_point)] pub struct ColorPoint { pub index: ::std::os::raw::c_int, pub white_x: f32, pub white_y: f32, #[optional(0f32)] pub gamma: Option, } /// EDID display Color Management Data, defined in section 3.10.3.7 /// /// Contains the coefficients for the function `L = a₃ × v³ + a₂ × v²` /// describing the luminance response L to some voltage v [0, 0.7] for each color /// channel. /// /// For more information see VESA DCM Standard, Version 1; January 6, 2003 #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_color_management_data)] pub struct ColorManagementData { pub version: i32, pub red_a3: f32, pub red_a2: f32, pub green_a3: f32, pub green_a2: f32, pub blue_a3: f32, pub blue_a2: f32, } /// Aspect ratio for an EDID CVT Timing Code. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::edid::di_edid_cvt_timing_code_aspect_ratio)] #[repr(u32)] pub enum CvtTimingCodeAspectRatio { _4_3 = ffi::edid::di_edid_cvt_timing_code_aspect_ratio_DI_EDID_CVT_TIMING_CODE_4_3, _16_9 = ffi::edid::di_edid_cvt_timing_code_aspect_ratio_DI_EDID_CVT_TIMING_CODE_16_9, _16_10 = ffi::edid::di_edid_cvt_timing_code_aspect_ratio_DI_EDID_CVT_TIMING_CODE_16_10, _15_9 = ffi::edid::di_edid_cvt_timing_code_aspect_ratio_DI_EDID_CVT_TIMING_CODE_15_9, } /// Preferred Vertical Rate for an EDID CVT Timing Code. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::edid::di_edid_cvt_timing_code_preferred_vrate)] #[repr(u32)] pub enum CvtTimingCodePreferredVrate { _50HZ = ffi::edid::di_edid_cvt_timing_code_preferred_vrate_DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_50HZ, _60HZ = ffi::edid::di_edid_cvt_timing_code_preferred_vrate_DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_60HZ, _75HZ = ffi::edid::di_edid_cvt_timing_code_preferred_vrate_DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_75HZ, _85HZ = ffi::edid::di_edid_cvt_timing_code_preferred_vrate_DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_85HZ, } /// EDID CVT Timing Code, defined in section 3.10.3.8 /// /// For more information see VESA Coordinated Video Timings (CVT) Standard. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::edid::di_edid_cvt_timing_code)] pub struct CvtTimingCode { pub addressable_lines_per_field: i32, pub aspect_ratio: CvtTimingCodeAspectRatio, pub supports_50hz_sb: bool, pub supports_60hz_sb: bool, pub supports_75hz_sb: bool, pub supports_85hz_sb: bool, pub supports_60hz_rb: bool, pub preferred_vertical_rate: CvtTimingCodePreferredVrate, } /// EDID extension block tags, defined in section 2.2.4. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::edid::di_edid_ext_tag)] #[repr(u32)] pub enum ExtensionTag { CEA = ffi::edid::di_edid_ext_tag_DI_EDID_EXT_CEA, VTB = ffi::edid::di_edid_ext_tag_DI_EDID_EXT_VTB, DI = ffi::edid::di_edid_ext_tag_DI_EDID_EXT_DI, LS = ffi::edid::di_edid_ext_tag_DI_EDID_EXT_LS, DPVL = ffi::edid::di_edid_ext_tag_DI_EDID_EXT_DPVL, BlockMap = ffi::edid::di_edid_ext_tag_DI_EDID_EXT_BLOCK_MAP, Vendor = ffi::edid::di_edid_ext_tag_DI_EDID_EXT_VENDOR, DisplayId = ffi::edid::di_edid_ext_tag_DI_EDID_EXT_DISPLAYID, #[other] Unknown, } #[derive(Debug)] #[repr(transparent)] pub struct ExtensionRef(*const ffi::edid::di_edid_ext); impl ExtensionRef { /// Get the tag of an EDID extension block. pub fn tag(&self) -> ExtensionTag { ExtensionTag::from(unsafe { ffi::edid::di_edid_ext_get_tag(self.0) }) } pub(crate) fn as_ptr(&self) -> *const ffi::edid::di_edid_ext { self.0 } } libdisplay-info-0.2.2/src/gtf.rs000064400000000000000000000054061046102023000146340ustar 00000000000000//! Low-level API for Generalized Timing Formula Standard version 1.1. use std::mem::MaybeUninit; use libdisplay_info_derive::FFIFrom; use crate::ffi; pub const DEFAULT_M: f64 = 600.0; pub const DEFAULT_C: f64 = 40.0; pub const DEFAULT_K: f64 = 128.0; pub const DEFAULT_J: f64 = 20.0; /// Type of frequency parameter used in di_gtf_options.ip_freq_rqd. #[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)] #[ffi(ffi::gtf::di_gtf_ip_param)] #[repr(u32)] pub enum IpParam { /// Vertical frame frequency (Hz) VFrameRate = ffi::gtf::di_gtf_ip_param_DI_GTF_IP_PARAM_V_FRAME_RATE, /// Horizontal frequency (kHz) HFreq = ffi::gtf::di_gtf_ip_param_DI_GTF_IP_PARAM_H_FREQ, /// Pixel clock rate (MHz) HPixels = ffi::gtf::di_gtf_ip_param_DI_GTF_IP_PARAM_H_PIXELS, } /// Input options for GTF. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::gtf::di_gtf_options)] pub struct Options { /// Number of active image pixels displayed on a line, not including any margin pub h_pixels: i32, /// Number of vertical lines in the displayed image pub v_lines: i32, /// Whether margins are required pub margins_rqd: bool, /// Indicates which frequency parameter is specified in ip_freq_rqd pub ip_param: IpParam, /// Vertical frame frequency (in Hz), horizontal frequency (in kHz) or pixel clock rate (in MHz) pub ip_freq_rqd: f64, /// Whether interlaced is required pub int_rqd: bool, /// Blanking formula gradient pub m: f64, /// Blanking formula offset pub c: f64, /// Blanking formula scaling factor pub k: f64, /// Blanking formula scaling factor weighting pub j: f64, } /// Output timing data for GTF. #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::gtf::di_gtf_timing)] pub struct Timing { pub h_pixels: i32, pub v_lines: i32, pub h_sync: i32, pub v_sync: i32, pub h_front_porch: i32, pub h_back_porch: i32, pub v_front_porch: i32, pub v_back_porch: i32, pub h_border: i32, pub v_border: i32, pub pixel_freq_mhz: f64, } impl Timing { /// Compute a timing via the GTF formula. pub fn compute(options: Options) -> Self { let mut timing = MaybeUninit::::uninit(); let options = ffi::gtf::di_gtf_options { h_pixels: options.h_pixels, v_lines: options.v_lines, margins_rqd: options.margins_rqd, ip_param: options.ip_param as u32, ip_freq_rqd: options.ip_freq_rqd, int_rqd: options.int_rqd, m: options.m, c: options.c, k: options.k, j: options.j, }; unsafe { ffi::gtf::di_gtf_compute(timing.as_mut_ptr(), &options); } Timing::from(unsafe { timing.assume_init() }) } } libdisplay-info-0.2.2/src/info.rs000064400000000000000000000206161046102023000150070ustar 00000000000000//! High-level API. use crate::{edid::Edid, ffi, string_from_owned_ffi_ptr}; #[cfg(feature = "v0_2")] use libdisplay_info_derive::FFIFrom; /// Display HDR static metadata #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::info::di_hdr_static_metadata)] #[cfg(feature = "v0_2")] pub struct HdrStaticMetadata { pub desired_content_max_luminance: f32, pub desired_content_max_frame_avg_luminance: f32, pub desired_content_min_luminance: f32, pub type1: bool, pub traditional_sdr: bool, pub traditional_hdr: bool, pub pq: bool, pub hlg: bool, } /// CIE 1931 2-degree observer chromaticity coordinates #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::info::di_chromaticity_cie1931)] #[cfg(feature = "v0_2")] pub struct ChromaticityCie1931 { pub x: f32, pub y: f32, } /// Display color primaries and default white point #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::info::di_color_primaries)] #[cfg(feature = "v0_2")] pub struct ColorPrimaries { pub has_primaries: bool, pub has_default_white_point: bool, pub primary: [ChromaticityCie1931; 3usize], pub default_white: ChromaticityCie1931, } /// Additional signal colorimetry encodings supported by the display #[derive(Debug, Copy, Clone, FFIFrom)] #[ffi(ffi::info::di_supported_signal_colorimetry)] #[cfg(feature = "v0_2")] pub struct SupportedSignalColorimetry { pub bt2020_cycc: bool, pub bt2020_ycc: bool, pub bt2020_rgb: bool, pub st2113_rgb: bool, pub ictcp: bool, } /// Information about a display device. /// /// This includes at least one EDID or DisplayID blob. /// /// Use [`Info::parse_edid`](Info::parse_edid) to create a [`Info`] from an EDID blob. /// DisplayID blobs are not yet supported. #[derive(Debug)] pub struct Info(*mut ffi::info::di_info); /// Parsing the EDID blob failed #[derive(Debug, thiserror::Error)] #[error("Parsing the EDID blob failed")] pub struct ParseFailed; impl Info { /// Parse an EDID blob. pub fn parse_edid(data: &[u8]) -> Result { let info = unsafe { ffi::info::di_info_parse_edid(data.as_ptr() as *const std::ffi::c_void, data.len()) }; if info.is_null() { return Err(ParseFailed); } Ok(Self(info)) } /// Get the failure messages for this blob. /// /// `None` is returned if the blob conforms to the relevant specifications. pub fn failure_msg(&self) -> Option<&std::ffi::CStr> { let failure_msg = unsafe { ffi::info::di_info_get_failure_msg(self.0) }; if failure_msg.is_null() { None } else { Some(unsafe { std::ffi::CStr::from_ptr(failure_msg) }) } } /// Returns the EDID the display device information was constructed with. /// /// The returned [`Edid`] can be used to query low-level EDID information, /// see [`edid`](crate::edid) module level docs. Users should prefer the high-level API if /// possible. /// /// `None` is returned if the [`Info`] doesn't contain an EDID. pub fn edid(&self) -> Option> { Edid::from_ptr(unsafe { ffi::info::di_info_get_edid(self.0) as *const ffi::edid::di_edid }) } /// Get the make of the display device. /// /// This is the manufacturer name, either company name or PNP ID. /// This string is informational and not meant to be used in programmatic /// decisions, configuration keys, etc. /// /// The string is in UTF-8 and may contain any characters except ASCII control /// codes. /// /// `None` is returned if the information is not available. pub fn make(&self) -> Option { string_from_owned_ffi_ptr(unsafe { ffi::info::di_info_get_make(self.0) }) } /// Get the model of the display device. /// /// This is the product name/model string or product number. /// This string is informational and not meant to be used in programmatic /// decisions, configuration keys, etc. /// /// The string is in UTF-8 and may contain any characters except ASCII control /// codes. /// /// `None` is returned if the information is not available. pub fn model(&self) -> Option { string_from_owned_ffi_ptr(unsafe { ffi::info::di_info_get_model(self.0) }) } /// Get the serial of the display device. /// /// This is the product serial string or the serial number. /// This string is informational and not meant to be used in programmatic /// decisions, configuration keys, etc. /// /// The string is in UTF-8 and may contain any characters except ASCII control /// codes. /// /// `None` is returned if the information is not available. pub fn serial(&self) -> Option { string_from_owned_ffi_ptr(unsafe { ffi::info::di_info_get_serial(self.0) }) } /// Get HDR static metadata support information as defined in ANSI/CTA-861-H /// as HDR Static Metadata Data Block. /// /// When HDR static metadata does not exist, /// all luminance fields are zero and only traditional_sdr is flagged as /// supported. #[cfg(feature = "v0_2")] pub fn hdr_static_metadata(&self) -> HdrStaticMetadata { // SAFETY: The returned pointer is owned by the struct di_info passed in. It remains // valid only as long as the di_info exists, and must not be freed by the // caller. // // This function does not return NULL. HdrStaticMetadata::from(unsafe { *ffi::info::di_info_get_hdr_static_metadata(self.0) }) } /// Get display color primaries and default white point /// /// Get the parameters of the default RGB colorimetry mode which is always /// supported. Primaries for monochrome displays might be all zeroes. /// /// These primaries might not be display's physical primaries, but only the /// primaries of the default RGB colorimetry signal when using IT Video Format /// (ANSI/CTA-861-H, Section 5). #[cfg(feature = "v0_2")] pub fn default_color_primaries(&self) -> ColorPrimaries { // SAFETY: The returned pointer is owned by the struct di_info passed in. It remains // valid only as long as the di_info exists, and must not be freed by the // caller. // // This function does not return NULL. ColorPrimaries::from(unsafe { *ffi::info::di_info_get_default_color_primaries(self.0) }) } /// Get signal colorimetry encodings supported by the display /// /// These signal colorimetry encodings are supported in addition to the /// display's default RGB colorimetry. When you wish to use one of the additional /// encodings, they need to be explicitly enabled in the video signal. How to /// do that is specific to the signalling used, e.g. HDMI. /// /// Signal colorimetry encoding provides the color space that the signal is /// encoded for. This includes primary and white point chromaticities, and the /// YCbCr-RGB conversion if necessary. Also the transfer function is implied /// unless explicitly set otherwise, e.g. with HDR static metadata. /// See ANSI/CTA-861-H for details. /// /// The signal color volume can be considerably larger than the physically /// displayable color volume. #[cfg(feature = "v0_2")] pub fn supported_signal_colorimetry(&self) -> SupportedSignalColorimetry { // SAFETY: The returned pointer is owned by the struct di_info passed in. It remains // valid only as long as the di_info exists, and must not be freed by the // caller. // // This function does not return NULL. SupportedSignalColorimetry::from(unsafe { *ffi::info::di_info_get_supported_signal_colorimetry(self.0) }) } /// Get display default transfer characteristic exponent (gamma) /// /// This should be the display gamma value when the display has been reset to /// its factory defaults, and it is driven with the default RGB colorimetry. /// /// Returns `None` when unknown. #[cfg(feature = "v0_2")] pub fn default_gamma(&self) -> Option { // SAFETY: The value is zero when unknown. let default_gamma = unsafe { ffi::info::di_info_get_default_gamma(self.0) }; if default_gamma == 0f32 { None } else { Some(default_gamma) } } } impl Drop for Info { fn drop(&mut self) { unsafe { ffi::info::di_info_destroy(self.0); } } } libdisplay-info-0.2.2/src/lib.rs000064400000000000000000000030421046102023000146140ustar 00000000000000#![cfg_attr(docsrs, feature(doc_auto_cfg))] use std::{ ffi::{c_char, CStr}, marker::PhantomData, }; pub use libdisplay_info_sys as ffi; pub mod cta; pub mod cvt; pub mod displayid; pub mod dmt; pub mod edid; pub mod gtf; pub mod info; /// Get the [`String`] from an owned ffi ptr /// /// This will automatically free the ptr fn string_from_ffi_ptr(ptr: *const c_char) -> Option { if ptr.is_null() { None } else { Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }) } } /// Get the [`String`] from an owned ffi ptr /// /// This will automatically free the ptr /// /// `None` is returned for NULL ptr fn string_from_owned_ffi_ptr(ptr: *mut c_char) -> Option { let res = string_from_ffi_ptr(ptr); if res.is_some() { unsafe { libc::free(ptr as *mut _); } } res } struct FFIIter<'a, T, F> { ptr: *const *const F, t: PhantomData, phantom: PhantomData<&'a ()>, } impl FFIIter<'_, T, F> { fn new(ptr: *const *const F) -> Self { Self { ptr, t: PhantomData, phantom: PhantomData, } } } impl Iterator for FFIIter<'_, T, F> where T: From, F: Copy, { type Item = T; fn next(&mut self) -> Option { if self.ptr.is_null() || unsafe { *self.ptr }.is_null() { None } else { let item = T::from(unsafe { *(*self.ptr) }); self.ptr = self.ptr.wrapping_add(1); Some(item) } } }