libdisplay_info/
displayid.rs

1//! Low-level API for VESA Display Identification Data (DisplayID).
2//!
3//! The library implements DisplayID version 1.3, available at:
4//! <https://vesa.org/vesa-standards/>
5use std::marker::PhantomData;
6
7use libdisplay_info_derive::FFIFrom;
8
9use crate::{edid::ExtensionRef, ffi, FFIIter};
10
11pub struct DisplayId<'ext> {
12    display_id: *const ffi::displayid::di_displayid,
13    phantom: PhantomData<&'ext ()>,
14}
15
16impl<'ext> DisplayId<'ext> {
17    /// Get a DisplayID extension block.
18    ///
19    /// Returns `None` if the extension block tag is not [DisplayId](crate::edid::ExtensionTag::DisplayId).
20    pub fn from_extension(extensions: &'ext ExtensionRef) -> Option<DisplayId<'ext>> {
21        let display_id = unsafe { ffi::edid::di_edid_ext_get_displayid(extensions.as_ptr()) };
22
23        if display_id.is_null() {
24            None
25        } else {
26            Some(Self {
27                display_id: display_id as *const ffi::displayid::di_displayid,
28                phantom: PhantomData,
29            })
30        }
31    }
32
33    /// Get the DisplayID version.
34    pub fn version(&self) -> i32 {
35        unsafe { ffi::displayid::di_displayid_get_version(self.display_id) }
36    }
37
38    /// Get the DisplayID revision.
39    pub fn revision(&self) -> i32 {
40        unsafe { ffi::displayid::di_displayid_get_revision(self.display_id) }
41    }
42
43    /// Get the DisplayID product type.
44    pub fn product_type(&self) -> ProductType {
45        ProductType::from(unsafe { ffi::displayid::di_displayid_get_product_type(self.display_id) })
46    }
47
48    /// Get DisplayID data blocks
49    pub fn data_blocks(&self) -> &[DataBlockRef] {
50        let data_blocks = unsafe { ffi::displayid::di_displayid_get_data_blocks(self.display_id) };
51
52        let mut len = 0;
53        while !unsafe { *data_blocks.offset(len) }.is_null() {
54            len += 1;
55        }
56
57        unsafe { std::slice::from_raw_parts(data_blocks as *const DataBlockRef, len as usize) }
58    }
59}
60
61/// Product type identifier, defined in section 2.3.
62#[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)]
63#[ffi(ffi::displayid::di_displayid_product_type)]
64#[repr(u32)]
65pub enum ProductType {
66    Extension = ffi::displayid::di_displayid_product_type_DI_DISPLAYID_PRODUCT_TYPE_EXTENSION,
67    Test = ffi::displayid::di_displayid_product_type_DI_DISPLAYID_PRODUCT_TYPE_TEST,
68    DisplayPanel =
69        ffi::displayid::di_displayid_product_type_DI_DISPLAYID_PRODUCT_TYPE_DISPLAY_PANEL,
70    StandaloneDisplay =
71        ffi::displayid::di_displayid_product_type_DI_DISPLAYID_PRODUCT_TYPE_STANDALONE_DISPLAY,
72    TvReceiver = ffi::displayid::di_displayid_product_type_DI_DISPLAYID_PRODUCT_TYPE_TV_RECEIVER,
73    Repeater = ffi::displayid::di_displayid_product_type_DI_DISPLAYID_PRODUCT_TYPE_REPEATER,
74    DirectDrive = ffi::displayid::di_displayid_product_type_DI_DISPLAYID_PRODUCT_TYPE_DIRECT_DRIVE,
75}
76
77/// DisplayID data block tag.
78#[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)]
79#[ffi(ffi::displayid::di_displayid_product_type)]
80#[repr(u32)]
81pub enum DataBlockTag {
82    ProductId = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_PRODUCT_ID,
83    DisplayParams =
84        ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_DISPLAY_PARAMS,
85    ColorCharact =
86        ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_COLOR_CHARACT,
87    TypeITiming = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_TYPE_I_TIMING,
88    TypeIITiming =
89        ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_TYPE_II_TIMING,
90    TypeIIITiming =
91        ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING,
92    TypIVTiming =
93        ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_TYPE_IV_TIMING,
94    VesaTiming = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_VESA_TIMING,
95    CeaTiming = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_CEA_TIMING,
96    TimingRangeLimits =
97        ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_TIMING_RANGE_LIMITS,
98    ProductSerial =
99        ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_PRODUCT_SERIAL,
100    AsciiString = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_ASCII_STRING,
101    DisplayDeviceData =
102        ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_DISPLAY_DEVICE_DATA,
103    InterfacePowerSeq =
104        ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_INTERFACE_POWER_SEQ,
105    TransferCharact =
106        ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_TRANSFER_CHARACT,
107    DisplayInterface =
108        ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_DISPLAY_INTERFACE,
109    StereoDisplayInterface = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_STEREO_DISPLAY_INTERFACE,
110    TypeVTiming = ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_TYPE_V_TIMING,
111    TiledDisplayTopo =
112        ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_TILED_DISPLAY_TOPO,
113    TypeVITiming =
114        ffi::displayid::di_displayid_data_block_tag_DI_DISPLAYID_DATA_BLOCK_TYPE_VI_TIMING,
115}
116
117/// A DisplayID data block.
118#[repr(transparent)]
119pub struct DataBlockRef(*const ffi::displayid::di_displayid_data_block);
120
121impl DataBlockRef {
122    /// Get a DisplayID data block tag.
123    pub fn tag(&self) -> DataBlockTag {
124        DataBlockTag::from(unsafe { ffi::displayid::di_displayid_data_block_get_tag(self.0) })
125    }
126
127    /// Get display parameters from a DisplayID data block.
128    ///
129    /// Returns `None` if the data block tag isn't
130    /// DI_DISPLAYID_DATA_BLOCK_DISPLAY_PARAMS.
131    pub fn display_params(&self) -> Option<DisplayParams> {
132        DisplayParams::from_ptr(unsafe {
133            ffi::displayid::di_displayid_data_block_get_display_params(self.0)
134        })
135    }
136
137    /// Get type I timings from a DisplayID data block.
138    /// Returns `None` if the data block tag isn't
139    /// DI_DISPLAYID_DATA_BLOCK_TYPE_I_TIMING.
140    pub fn type_i_timings(&self) -> impl Iterator<Item = TypeIIIVIITiming> {
141        FFIIter::new(unsafe { ffi::displayid::di_displayid_data_block_get_type_i_timings(self.0) })
142    }
143
144    /// Get type II timings from a DisplayID data block.
145    /// Returns `None` if the data block tag isn't
146    /// DI_DISPLAYID_DATA_BLOCK_TYPE_II_TIMING.
147    #[cfg(any(feature = "v0_2", feature = "v0_3"))]
148    pub fn type_ii_timings(&self) -> impl Iterator<Item = TypeIIIVIITiming> {
149        FFIIter::new(unsafe { ffi::displayid::di_displayid_data_block_get_type_ii_timings(self.0) })
150    }
151
152    /// Get type III timings from a DisplayID data block.
153    /// Returns `None` if the data block tag isn't
154    /// DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING.
155    #[cfg(any(feature = "v0_2", feature = "v0_3"))]
156    pub fn type_iii_timings(&self) -> impl Iterator<Item = TypeIIITiming> {
157        FFIIter::new(unsafe {
158            ffi::displayid::di_displayid_data_block_get_type_iii_timings(self.0)
159        })
160    }
161
162    /// Get tiled display topology from a DisplayID data block.
163    ///
164    /// Returns `None` if the data block tag isn't
165    /// DI_DISPLAYID_DATA_BLOCK_TILED_DISPLAY_TOPO.
166    pub fn tiled_topo(&self) -> Option<TiledTopo> {
167        TiledTopo::from_ptr(unsafe {
168            ffi::displayid::di_displayid_data_block_get_tiled_topo(self.0)
169        })
170    }
171}
172
173/// Display parameters feature support flags, defined in section 4.2.3.
174#[derive(Debug, Copy, Clone, FFIFrom)]
175#[ffi(ffi::displayid::di_displayid_display_params_features)]
176pub struct DisplayParamsFeatures {
177    pub audio: bool,
178    pub separate_audio_inputs: bool,
179    pub audio_input_override: bool,
180    pub power_management: bool,
181    pub fixed_timing: bool,
182    pub fixed_pixel_format: bool,
183    pub ai: bool,
184    pub deinterlacing: bool,
185}
186
187/// Display parameters data block, defined in section 4.2.
188#[derive(Debug, Copy, Clone, FFIFrom)]
189#[ffi(ffi::displayid::di_displayid_display_params)]
190pub struct DisplayParams {
191    pub horiz_image_mm: f32,
192    pub vert_image_mm: f32,
193    pub horiz_pixels: i32,
194    pub vert_pixels: i32,
195    #[ptr_deref]
196    pub features: Option<DisplayParamsFeatures>,
197    #[optional(0f32)]
198    pub gamma: Option<f32>,
199    pub aspect_ratio: f32,
200    pub bits_per_color_overall: i32,
201    pub bits_per_color_native: i32,
202}
203
204#[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)]
205#[ffi(ffi::displayid::di_displayid_type_i_ii_vii_timing_stereo_3d)]
206#[repr(u32)]
207pub enum TypeIIIVIITimingStereo3d {
208    Never = ffi::displayid::di_displayid_type_i_ii_vii_timing_stereo_3d_DI_DISPLAYID_TYPE_I_II_VII_TIMING_STEREO_3D_NEVER,
209    Always = ffi::displayid::di_displayid_type_i_ii_vii_timing_stereo_3d_DI_DISPLAYID_TYPE_I_II_VII_TIMING_STEREO_3D_ALWAYS,
210    User = ffi::displayid::di_displayid_type_i_ii_vii_timing_stereo_3d_DI_DISPLAYID_TYPE_I_II_VII_TIMING_STEREO_3D_USER,
211}
212
213#[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)]
214#[ffi(ffi::displayid::di_displayid_timing_aspect_ratio)]
215#[repr(u32)]
216pub enum TimingAspectRatio {
217    _1_1 = ffi::displayid::di_displayid_timing_aspect_ratio_DI_DISPLAYID_TIMING_ASPECT_RATIO_1_1,
218    _5_4 = ffi::displayid::di_displayid_timing_aspect_ratio_DI_DISPLAYID_TIMING_ASPECT_RATIO_5_4,
219    _4_3 = ffi::displayid::di_displayid_timing_aspect_ratio_DI_DISPLAYID_TIMING_ASPECT_RATIO_4_3,
220    _15_9 = ffi::displayid::di_displayid_timing_aspect_ratio_DI_DISPLAYID_TIMING_ASPECT_RATIO_15_9,
221    _16_9 = ffi::displayid::di_displayid_timing_aspect_ratio_DI_DISPLAYID_TIMING_ASPECT_RATIO_16_9,
222    _16_10 =
223        ffi::displayid::di_displayid_timing_aspect_ratio_DI_DISPLAYID_TIMING_ASPECT_RATIO_16_10,
224    _64_27 =
225        ffi::displayid::di_displayid_timing_aspect_ratio_DI_DISPLAYID_TIMING_ASPECT_RATIO_64_27,
226    _256_135 =
227        ffi::displayid::di_displayid_timing_aspect_ratio_DI_DISPLAYID_TIMING_ASPECT_RATIO_256_135,
228    Undefined =
229        ffi::displayid::di_displayid_timing_aspect_ratio_DI_DISPLAYID_TIMING_ASPECT_RATIO_UNDEFINED,
230}
231
232#[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)]
233#[ffi(ffi::displayid::di_displayid_type_i_ii_vii_timing_sync_polarity)]
234#[repr(u32)]
235pub enum TypeIIIVIITimingSyncPolarity {
236    Negative = ffi::displayid::di_displayid_type_i_ii_vii_timing_sync_polarity_DI_DISPLAYID_TYPE_I_II_VII_TIMING_SYNC_NEGATIVE,
237    Positive = ffi::displayid::di_displayid_type_i_ii_vii_timing_sync_polarity_DI_DISPLAYID_TYPE_I_II_VII_TIMING_SYNC_POSITIVE,
238}
239
240/// Type I timing, defined in section 4.4.1.
241#[derive(Debug, Copy, Clone, FFIFrom)]
242#[ffi(ffi::displayid::di_displayid_type_i_ii_vii_timing)]
243pub struct TypeIIIVIITiming {
244    pub pixel_clock_mhz: f64,
245    pub preferred: bool,
246    pub stereo_3d: TypeIIIVIITimingStereo3d,
247    pub interlaced: bool,
248    pub aspect_ratio: TimingAspectRatio,
249    pub horiz_active: i32,
250    pub vert_active: i32,
251    pub horiz_blank: i32,
252    pub vert_blank: i32,
253    pub horiz_offset: i32,
254    pub vert_offset: i32,
255    pub horiz_sync_width: i32,
256    pub vert_sync_width: i32,
257    pub horiz_sync_polarity: TypeIIIVIITimingSyncPolarity,
258    pub vert_sync_polarity: TypeIIIVIITimingSyncPolarity,
259}
260
261/// Formula/algorithm for type III timings.
262#[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)]
263#[ffi(ffi::displayid::di_displayid_type_iii_timing_algo)]
264#[repr(u32)]
265#[cfg(any(feature = "v0_2", feature = "v0_3"))]
266pub enum TyoeIIITimingAlgo {
267    CvtStandardBlanking = ffi::displayid::di_displayid_type_iii_timing_algo_DI_DISPLAYID_TYPE_III_TIMING_CVT_STANDARD_BLANKING,
268    CvtReducedBlacking = ffi::displayid::di_displayid_type_iii_timing_algo_DI_DISPLAYID_TYPE_III_TIMING_CVT_REDUCED_BLANKING,
269}
270
271/// Type I timing, defined in section 4.4.1.
272#[derive(Debug, Copy, Clone, FFIFrom)]
273#[ffi(ffi::displayid::di_displayid_type_iii_timing)]
274#[cfg(any(feature = "v0_2", feature = "v0_3"))]
275pub struct TypeIIITiming {
276    pub preferred: bool,
277    pub algo: TyoeIIITimingAlgo,
278    pub aspect_ratio: TimingAspectRatio,
279    pub horiz_active: i32,
280    pub interlaced: bool,
281    pub refresh_rate_hz: i32,
282}
283
284/// Behavior when more than 1 tile and less than total number of tiles are driven
285/// by the source.
286#[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)]
287#[ffi(ffi::displayid::di_displayid_tiled_topo_missing_recv_behavior)]
288#[repr(u32)]
289pub enum TiledTopoMissingRecvBehavior {
290    Undef = ffi::displayid::di_displayid_tiled_topo_missing_recv_behavior_DI_DISPLAYID_TILED_TOPO_MISSING_RECV_UNDEF,
291    TileOnly = ffi::displayid::di_displayid_tiled_topo_missing_recv_behavior_DI_DISPLAYID_TILED_TOPO_MISSING_RECV_TILE_ONLY,
292}
293
294/// Behavior of this tile when it is the only tile receiving an image from the
295/// source.
296#[derive(Debug, Clone, Copy, PartialEq, Eq, FFIFrom)]
297#[ffi(ffi::displayid::di_displayid_tiled_topo_single_recv_behavior)]
298#[repr(u32)]
299pub enum TiledTopoSingleRecvBehavior {
300    Undef = ffi::displayid::di_displayid_tiled_topo_single_recv_behavior_DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_UNDEF,
301    TileOnly = ffi::displayid::di_displayid_tiled_topo_single_recv_behavior_DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_TILE_ONLY,
302    Scaled = ffi::displayid::di_displayid_tiled_topo_single_recv_behavior_DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_SCALED,
303    Cloned = ffi::displayid::di_displayid_tiled_topo_single_recv_behavior_DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_CLONED,
304}
305
306/// Tiled display capabilities.
307#[derive(Debug, Copy, Clone, FFIFrom)]
308#[ffi(ffi::displayid::di_displayid_tiled_topo_caps)]
309pub struct TiledTopoCaps {
310    pub single_enclosure: bool,
311    pub missing_recv_behavior: TiledTopoMissingRecvBehavior,
312    pub single_recv_behavior: TiledTopoSingleRecvBehavior,
313}
314
315/// Tiled display bezel information.
316///
317/// The lengths are measured in pixels, accurate to the tenths place.
318#[derive(Debug, Copy, Clone, FFIFrom)]
319#[ffi(ffi::displayid::di_displayid_tiled_topo_bezel)]
320pub struct TiledTopoBezel {
321    pub top_px: f32,
322    pub bottom_px: f32,
323    pub right_px: f32,
324    pub left_px: f32,
325}
326
327/// Tiled display topology, defined in section 4.14.
328#[derive(Debug, Copy, Clone, FFIFrom)]
329#[ffi(ffi::displayid::di_displayid_tiled_topo)]
330pub struct TiledTopo {
331    #[ptr_deref]
332    pub caps: Option<TiledTopoCaps>,
333    pub total_horiz_tiles: i32,
334    pub total_vert_tiles: i32,
335    pub horiz_tile_location: i32,
336    pub vert_tile_location: i32,
337    pub horiz_tile_pixels: i32,
338    pub vert_tile_lines: i32,
339    #[ptr_deref]
340    pub bezel: Option<TiledTopoBezel>,
341    #[cast_as(u8)]
342    pub vendor_id: [char; 3usize],
343    pub product_code: u16,
344    pub serial_number: u32,
345}