|  | //! | 
|  | //! Bindings to the DRM's modesetting capabilities. | 
|  | //! | 
|  |  | 
|  | #![allow(clippy::too_many_arguments)] | 
|  |  | 
|  | use crate::ioctl; | 
|  | use drm_sys::*; | 
|  |  | 
|  | use std::{io, os::unix::io::BorrowedFd}; | 
|  |  | 
|  | /// Enumerate most card resources. | 
|  | pub fn get_resources( | 
|  | fd: BorrowedFd<'_>, | 
|  | mut fbs: Option<&mut Vec<u32>>, | 
|  | mut crtcs: Option<&mut Vec<u32>>, | 
|  | mut connectors: Option<&mut Vec<u32>>, | 
|  | mut encoders: Option<&mut Vec<u32>>, | 
|  | ) -> io::Result<drm_mode_card_res> { | 
|  | let mut sizes = drm_mode_card_res::default(); | 
|  | unsafe { | 
|  | ioctl::mode::get_resources(fd, &mut sizes)?; | 
|  | } | 
|  |  | 
|  | map_reserve!(fbs, sizes.count_fbs as usize); | 
|  | map_reserve!(crtcs, sizes.count_crtcs as usize); | 
|  | map_reserve!(connectors, sizes.count_connectors as usize); | 
|  | map_reserve!(encoders, sizes.count_encoders as usize); | 
|  |  | 
|  | let mut res = drm_mode_card_res { | 
|  | fb_id_ptr: map_ptr!(&fbs), | 
|  | crtc_id_ptr: map_ptr!(&crtcs), | 
|  | connector_id_ptr: map_ptr!(&connectors), | 
|  | encoder_id_ptr: map_ptr!(&encoders), | 
|  | count_fbs: map_len!(&fbs), | 
|  | count_crtcs: map_len!(&crtcs), | 
|  | count_connectors: map_len!(&connectors), | 
|  | count_encoders: map_len!(&encoders), | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::get_resources(fd, &mut res)?; | 
|  | } | 
|  |  | 
|  | map_set!(fbs, res.count_fbs as usize); | 
|  | map_set!(crtcs, res.count_crtcs as usize); | 
|  | map_set!(connectors, res.count_connectors as usize); | 
|  | map_set!(encoders, res.count_encoders as usize); | 
|  |  | 
|  | Ok(res) | 
|  | } | 
|  |  | 
|  | /// Enumerate plane resources. | 
|  | pub fn get_plane_resources( | 
|  | fd: BorrowedFd<'_>, | 
|  | mut planes: Option<&mut Vec<u32>>, | 
|  | ) -> io::Result<drm_mode_get_plane_res> { | 
|  | let mut sizes = drm_mode_get_plane_res::default(); | 
|  | unsafe { | 
|  | ioctl::mode::get_plane_resources(fd, &mut sizes)?; | 
|  | } | 
|  |  | 
|  | if planes.is_none() { | 
|  | return Ok(sizes); | 
|  | } | 
|  |  | 
|  | map_reserve!(planes, sizes.count_planes as usize); | 
|  |  | 
|  | let mut res = drm_mode_get_plane_res { | 
|  | plane_id_ptr: map_ptr!(&planes), | 
|  | count_planes: sizes.count_planes, | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::get_plane_resources(fd, &mut res)?; | 
|  | } | 
|  |  | 
|  | map_set!(planes, res.count_planes as usize); | 
|  |  | 
|  | Ok(res) | 
|  | } | 
|  |  | 
|  | /// Get info about a framebuffer. | 
|  | pub fn get_framebuffer(fd: BorrowedFd<'_>, fb_id: u32) -> io::Result<drm_mode_fb_cmd> { | 
|  | let mut info = drm_mode_fb_cmd { | 
|  | fb_id, | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::get_fb(fd, &mut info)?; | 
|  | } | 
|  |  | 
|  | Ok(info) | 
|  | } | 
|  |  | 
|  | /// Add a new framebuffer. | 
|  | pub fn add_fb( | 
|  | fd: BorrowedFd<'_>, | 
|  | width: u32, | 
|  | height: u32, | 
|  | pitch: u32, | 
|  | bpp: u32, | 
|  | depth: u32, | 
|  | handle: u32, | 
|  | ) -> io::Result<drm_mode_fb_cmd> { | 
|  | let mut fb = drm_mode_fb_cmd { | 
|  | width, | 
|  | height, | 
|  | pitch, | 
|  | bpp, | 
|  | depth, | 
|  | handle, | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::add_fb(fd, &mut fb)?; | 
|  | } | 
|  |  | 
|  | Ok(fb) | 
|  | } | 
|  |  | 
|  | /// Get info about a framebuffer (with modifiers). | 
|  | pub fn get_framebuffer2(fd: BorrowedFd<'_>, fb_id: u32) -> io::Result<drm_mode_fb_cmd2> { | 
|  | let mut info = drm_mode_fb_cmd2 { | 
|  | fb_id, | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::get_fb2(fd, &mut info)?; | 
|  | } | 
|  |  | 
|  | Ok(info) | 
|  | } | 
|  |  | 
|  | /// Add a new framebuffer (with modifiers) | 
|  | pub fn add_fb2( | 
|  | fd: BorrowedFd<'_>, | 
|  | width: u32, | 
|  | height: u32, | 
|  | fmt: u32, | 
|  | handles: &[u32; 4], | 
|  | pitches: &[u32; 4], | 
|  | offsets: &[u32; 4], | 
|  | modifier: &[u64; 4], | 
|  | flags: u32, | 
|  | ) -> io::Result<drm_mode_fb_cmd2> { | 
|  | let mut fb = drm_mode_fb_cmd2 { | 
|  | width, | 
|  | height, | 
|  | pixel_format: fmt, | 
|  | flags, | 
|  | handles: *handles, | 
|  | pitches: *pitches, | 
|  | offsets: *offsets, | 
|  | modifier: *modifier, | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::add_fb2(fd, &mut fb)?; | 
|  | } | 
|  |  | 
|  | Ok(fb) | 
|  | } | 
|  |  | 
|  | /// Remove a framebuffer. | 
|  | pub fn rm_fb(fd: BorrowedFd<'_>, mut id: u32) -> io::Result<()> { | 
|  | unsafe { | 
|  | ioctl::mode::rm_fb(fd, &mut id)?; | 
|  | } | 
|  |  | 
|  | Ok(()) | 
|  | } | 
|  |  | 
|  | /// Mark a framebuffer as dirty. | 
|  | pub fn dirty_fb( | 
|  | fd: BorrowedFd<'_>, | 
|  | fb_id: u32, | 
|  | clips: &[drm_clip_rect], | 
|  | ) -> io::Result<drm_mode_fb_dirty_cmd> { | 
|  | let mut dirty = drm_mode_fb_dirty_cmd { | 
|  | fb_id, | 
|  | num_clips: clips.len() as _, | 
|  | clips_ptr: clips.as_ptr() as _, | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::dirty_fb(fd, &mut dirty)?; | 
|  | } | 
|  |  | 
|  | Ok(dirty) | 
|  | } | 
|  |  | 
|  | /// Get info about a CRTC | 
|  | pub fn get_crtc(fd: BorrowedFd<'_>, crtc_id: u32) -> io::Result<drm_mode_crtc> { | 
|  | let mut info = drm_mode_crtc { | 
|  | crtc_id, | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::get_crtc(fd, &mut info)?; | 
|  | } | 
|  |  | 
|  | Ok(info) | 
|  | } | 
|  |  | 
|  | /// Set CRTC state | 
|  | pub fn set_crtc( | 
|  | fd: BorrowedFd<'_>, | 
|  | crtc_id: u32, | 
|  | fb_id: u32, | 
|  | x: u32, | 
|  | y: u32, | 
|  | conns: &[u32], | 
|  | mode: Option<drm_mode_modeinfo>, | 
|  | ) -> io::Result<drm_mode_crtc> { | 
|  | let mut crtc = drm_mode_crtc { | 
|  | set_connectors_ptr: conns.as_ptr() as _, | 
|  | count_connectors: conns.len() as _, | 
|  | crtc_id, | 
|  | fb_id, | 
|  | x, | 
|  | y, | 
|  | mode_valid: match mode { | 
|  | Some(_) => 1, | 
|  | None => 0, | 
|  | }, | 
|  | mode: mode.unwrap_or_default(), | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::set_crtc(fd, &mut crtc)?; | 
|  | } | 
|  |  | 
|  | Ok(crtc) | 
|  | } | 
|  |  | 
|  | /// Get CRTC gamma ramp | 
|  | pub fn get_gamma( | 
|  | fd: BorrowedFd<'_>, | 
|  | crtc_id: u32, | 
|  | size: usize, | 
|  | red: &mut [u16], | 
|  | green: &mut [u16], | 
|  | blue: &mut [u16], | 
|  | ) -> io::Result<drm_mode_crtc_lut> { | 
|  | let mut lut = drm_mode_crtc_lut { | 
|  | crtc_id, | 
|  | gamma_size: size as _, | 
|  | red: red.as_mut_ptr() as _, | 
|  | green: green.as_mut_ptr() as _, | 
|  | blue: blue.as_mut_ptr() as _, | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::get_gamma(fd, &mut lut)?; | 
|  | } | 
|  |  | 
|  | Ok(lut) | 
|  | } | 
|  |  | 
|  | /// Set CRTC gamma ramp | 
|  | pub fn set_gamma( | 
|  | fd: BorrowedFd<'_>, | 
|  | crtc_id: u32, | 
|  | size: usize, | 
|  | red: &[u16], | 
|  | green: &[u16], | 
|  | blue: &[u16], | 
|  | ) -> io::Result<drm_mode_crtc_lut> { | 
|  | let mut lut = drm_mode_crtc_lut { | 
|  | crtc_id, | 
|  | gamma_size: size as _, | 
|  | red: red.as_ptr() as _, | 
|  | green: green.as_ptr() as _, | 
|  | blue: blue.as_ptr() as _, | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::set_gamma(fd, &mut lut)?; | 
|  | } | 
|  |  | 
|  | Ok(lut) | 
|  | } | 
|  |  | 
|  | /// Set cursor state | 
|  | /// | 
|  | /// The buffer must be allocated using the buffer manager of the driver (GEM or TTM). It is not | 
|  | /// allowed to be a dumb buffer. | 
|  | #[deprecated = "use a cursor plane instead"] | 
|  | pub fn set_cursor( | 
|  | fd: BorrowedFd<'_>, | 
|  | crtc_id: u32, | 
|  | buf_id: u32, | 
|  | width: u32, | 
|  | height: u32, | 
|  | ) -> io::Result<drm_mode_cursor> { | 
|  | let mut cursor = drm_mode_cursor { | 
|  | flags: DRM_MODE_CURSOR_BO, | 
|  | crtc_id, | 
|  | width, | 
|  | height, | 
|  | handle: buf_id, | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::cursor(fd, &mut cursor)?; | 
|  | } | 
|  |  | 
|  | Ok(cursor) | 
|  | } | 
|  |  | 
|  | /// Set cursor state (with hotspot position) | 
|  | /// | 
|  | /// The buffer must be allocated using the buffer manager of the driver (GEM or TTM). It is not | 
|  | /// allowed to be a dumb buffer. | 
|  | /// | 
|  | /// The hotspot position is used to coordinate the guest and host cursor location in case of | 
|  | /// virtualization. | 
|  | #[deprecated = "use a cursor plane instead"] | 
|  | pub fn set_cursor2( | 
|  | fd: BorrowedFd<'_>, | 
|  | crtc_id: u32, | 
|  | buf_id: u32, | 
|  | width: u32, | 
|  | height: u32, | 
|  | hot_x: i32, | 
|  | hot_y: i32, | 
|  | ) -> io::Result<drm_mode_cursor2> { | 
|  | let mut cursor = drm_mode_cursor2 { | 
|  | flags: DRM_MODE_CURSOR_BO, | 
|  | crtc_id, | 
|  | width, | 
|  | height, | 
|  | handle: buf_id, | 
|  | hot_x, | 
|  | hot_y, | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::cursor2(fd, &mut cursor)?; | 
|  | } | 
|  |  | 
|  | Ok(cursor) | 
|  | } | 
|  |  | 
|  | /// Move cursor | 
|  | #[deprecated = "use a cursor plane instead"] | 
|  | pub fn move_cursor( | 
|  | fd: BorrowedFd<'_>, | 
|  | crtc_id: u32, | 
|  | x: i32, | 
|  | y: i32, | 
|  | ) -> io::Result<drm_mode_cursor> { | 
|  | let mut cursor = drm_mode_cursor { | 
|  | flags: DRM_MODE_CURSOR_MOVE, | 
|  | crtc_id, | 
|  | x, | 
|  | y, | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::cursor(fd, &mut cursor)?; | 
|  | } | 
|  |  | 
|  | Ok(cursor) | 
|  | } | 
|  |  | 
|  | /// Get info about a connector | 
|  | pub fn get_connector( | 
|  | fd: BorrowedFd<'_>, | 
|  | connector_id: u32, | 
|  | mut props: Option<&mut Vec<u32>>, | 
|  | mut prop_values: Option<&mut Vec<u64>>, | 
|  | mut modes: Option<&mut Vec<drm_mode_modeinfo>>, | 
|  | mut encoders: Option<&mut Vec<u32>>, | 
|  | force_probe: bool, | 
|  | ) -> io::Result<drm_mode_get_connector> { | 
|  | assert_eq!(props.is_some(), prop_values.is_some()); | 
|  |  | 
|  | let tmp_mode = drm_mode_modeinfo::default(); | 
|  | let mut sizes = drm_mode_get_connector { | 
|  | connector_id, | 
|  | modes_ptr: if force_probe { | 
|  | 0 | 
|  | } else { | 
|  | &tmp_mode as *const _ as _ | 
|  | }, | 
|  | count_modes: if force_probe { 0 } else { 1 }, | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::get_connector(fd, &mut sizes)?; | 
|  | } | 
|  |  | 
|  | let info = loop { | 
|  | map_reserve!(props, sizes.count_props as usize); | 
|  | map_reserve!(prop_values, sizes.count_props as usize); | 
|  | map_reserve!(modes, sizes.count_modes as usize); | 
|  | map_reserve!(encoders, sizes.count_encoders as usize); | 
|  |  | 
|  | let mut info = drm_mode_get_connector { | 
|  | connector_id, | 
|  | encoders_ptr: map_ptr!(&encoders), | 
|  | modes_ptr: match &mut modes { | 
|  | Some(b) => b.as_mut_ptr() as _, | 
|  | None => { | 
|  | if force_probe { | 
|  | 0 as _ | 
|  | } else { | 
|  | &tmp_mode as *const _ as _ | 
|  | } | 
|  | } | 
|  | }, | 
|  | props_ptr: map_ptr!(&props), | 
|  | prop_values_ptr: map_ptr!(&prop_values), | 
|  | count_modes: match &modes { | 
|  | Some(b) => b.capacity() as _, | 
|  | None => { | 
|  | if force_probe { | 
|  | 0 | 
|  | } else { | 
|  | 1 | 
|  | } | 
|  | } | 
|  | }, | 
|  | count_props: map_len!(&props), | 
|  | count_encoders: map_len!(&encoders), | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::get_connector(fd, &mut info)?; | 
|  | } | 
|  |  | 
|  | if info.count_modes == sizes.count_modes | 
|  | && info.count_encoders == sizes.count_encoders | 
|  | && info.count_props == sizes.count_props | 
|  | { | 
|  | break info; | 
|  | } else { | 
|  | sizes = info; | 
|  | } | 
|  | }; | 
|  |  | 
|  | map_set!(modes, info.count_modes as usize); | 
|  | map_set!(props, info.count_props as usize); | 
|  | map_set!(prop_values, info.count_props as usize); | 
|  | map_set!(encoders, info.count_encoders as usize); | 
|  |  | 
|  | Ok(info) | 
|  | } | 
|  |  | 
|  | /// Get info about an encoder | 
|  | pub fn get_encoder(fd: BorrowedFd<'_>, encoder_id: u32) -> io::Result<drm_mode_get_encoder> { | 
|  | let mut info = drm_mode_get_encoder { | 
|  | encoder_id, | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::get_encoder(fd, &mut info)?; | 
|  | } | 
|  |  | 
|  | Ok(info) | 
|  | } | 
|  |  | 
|  | /// Get info about a plane. | 
|  | pub fn get_plane( | 
|  | fd: BorrowedFd<'_>, | 
|  | plane_id: u32, | 
|  | mut formats: Option<&mut Vec<u32>>, | 
|  | ) -> io::Result<drm_mode_get_plane> { | 
|  | let mut sizes = drm_mode_get_plane { | 
|  | plane_id, | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::get_plane(fd, &mut sizes)?; | 
|  | } | 
|  |  | 
|  | if formats.is_none() { | 
|  | return Ok(sizes); | 
|  | } | 
|  |  | 
|  | map_reserve!(formats, sizes.count_format_types as usize); | 
|  |  | 
|  | let mut info = drm_mode_get_plane { | 
|  | plane_id, | 
|  | count_format_types: sizes.count_format_types, | 
|  | format_type_ptr: map_ptr!(&formats), | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::get_plane(fd, &mut info)?; | 
|  | } | 
|  |  | 
|  | map_set!(formats, info.count_format_types as usize); | 
|  |  | 
|  | Ok(info) | 
|  | } | 
|  |  | 
|  | /// Set plane state. | 
|  | pub fn set_plane( | 
|  | fd: BorrowedFd<'_>, | 
|  | plane_id: u32, | 
|  | crtc_id: u32, | 
|  | fb_id: u32, | 
|  | flags: u32, | 
|  | crtc_x: i32, | 
|  | crtc_y: i32, | 
|  | crtc_w: u32, | 
|  | crtc_h: u32, | 
|  | src_x: u32, | 
|  | src_y: u32, | 
|  | src_w: u32, | 
|  | src_h: u32, | 
|  | ) -> io::Result<drm_mode_set_plane> { | 
|  | let mut plane = drm_mode_set_plane { | 
|  | plane_id, | 
|  | crtc_id, | 
|  | fb_id, | 
|  | flags, | 
|  | crtc_x, | 
|  | crtc_y, | 
|  | crtc_w, | 
|  | crtc_h, | 
|  | src_x, | 
|  | src_y, | 
|  | src_h, | 
|  | src_w, | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::set_plane(fd, &mut plane)?; | 
|  | } | 
|  |  | 
|  | Ok(plane) | 
|  | } | 
|  |  | 
|  | /// Get property | 
|  | pub fn get_property( | 
|  | fd: BorrowedFd<'_>, | 
|  | prop_id: u32, | 
|  | mut values: Option<&mut Vec<u64>>, | 
|  | mut enums: Option<&mut Vec<drm_mode_property_enum>>, | 
|  | ) -> io::Result<drm_mode_get_property> { | 
|  | let mut prop = drm_mode_get_property { | 
|  | prop_id, | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::get_property(fd, &mut prop)?; | 
|  | } | 
|  |  | 
|  | // There is no need to call get_property() twice if there is nothing else to retrieve. | 
|  | if prop.count_values == 0 && prop.count_enum_blobs == 0 { | 
|  | return Ok(prop); | 
|  | } | 
|  |  | 
|  | map_reserve!(values, prop.count_values as usize); | 
|  | map_reserve!(enums, prop.count_enum_blobs as usize); | 
|  |  | 
|  | prop.values_ptr = map_ptr!(&values); | 
|  | prop.enum_blob_ptr = map_ptr!(&enums); | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::get_property(fd, &mut prop)?; | 
|  | } | 
|  |  | 
|  | map_set!(values, prop.count_values as usize); | 
|  | map_set!(enums, prop.count_enum_blobs as usize); | 
|  |  | 
|  | Ok(prop) | 
|  | } | 
|  |  | 
|  | /// Set property | 
|  | pub fn set_connector_property( | 
|  | fd: BorrowedFd<'_>, | 
|  | connector_id: u32, | 
|  | prop_id: u32, | 
|  | value: u64, | 
|  | ) -> io::Result<drm_mode_connector_set_property> { | 
|  | let mut prop = drm_mode_connector_set_property { | 
|  | value, | 
|  | prop_id, | 
|  | connector_id, | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::connector_set_property(fd, &mut prop)?; | 
|  | } | 
|  |  | 
|  | Ok(prop) | 
|  | } | 
|  |  | 
|  | /// Get the value of a property blob | 
|  | pub fn get_property_blob( | 
|  | fd: BorrowedFd<'_>, | 
|  | blob_id: u32, | 
|  | mut data: Option<&mut Vec<u8>>, | 
|  | ) -> io::Result<drm_mode_get_blob> { | 
|  | let mut sizes = drm_mode_get_blob { | 
|  | blob_id, | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::get_blob(fd, &mut sizes)?; | 
|  | } | 
|  |  | 
|  | if data.is_none() { | 
|  | return Ok(sizes); | 
|  | } | 
|  |  | 
|  | map_reserve!(data, sizes.length as usize); | 
|  |  | 
|  | let mut blob = drm_mode_get_blob { | 
|  | blob_id, | 
|  | length: sizes.length, | 
|  | data: map_ptr!(&data), | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::get_blob(fd, &mut blob)?; | 
|  | } | 
|  |  | 
|  | map_set!(data, blob.length as usize); | 
|  |  | 
|  | Ok(blob) | 
|  | } | 
|  |  | 
|  | /// Create a property blob | 
|  | pub fn create_property_blob( | 
|  | fd: BorrowedFd<'_>, | 
|  | data: &mut [u8], | 
|  | ) -> io::Result<drm_mode_create_blob> { | 
|  | let mut blob = drm_mode_create_blob { | 
|  | data: data.as_mut_ptr() as _, | 
|  | length: data.len() as _, | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::create_blob(fd, &mut blob)?; | 
|  | } | 
|  |  | 
|  | Ok(blob) | 
|  | } | 
|  |  | 
|  | /// Destroy a property blob | 
|  | pub fn destroy_property_blob(fd: BorrowedFd<'_>, id: u32) -> io::Result<drm_mode_destroy_blob> { | 
|  | let mut blob = drm_mode_destroy_blob { blob_id: id }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::destroy_blob(fd, &mut blob)?; | 
|  | } | 
|  |  | 
|  | Ok(blob) | 
|  | } | 
|  |  | 
|  | /// Get properties from an object | 
|  | pub fn get_properties( | 
|  | fd: BorrowedFd<'_>, | 
|  | obj_id: u32, | 
|  | obj_type: u32, | 
|  | mut props: Option<&mut Vec<u32>>, | 
|  | mut values: Option<&mut Vec<u64>>, | 
|  | ) -> io::Result<drm_mode_obj_get_properties> { | 
|  | assert_eq!(props.is_some(), values.is_some()); | 
|  |  | 
|  | let mut sizes = drm_mode_obj_get_properties { | 
|  | obj_id, | 
|  | obj_type, | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::obj_get_properties(fd, &mut sizes)?; | 
|  | } | 
|  |  | 
|  | map_reserve!(props, sizes.count_props as usize); | 
|  | map_reserve!(values, sizes.count_props as usize); | 
|  |  | 
|  | let mut info = drm_mode_obj_get_properties { | 
|  | props_ptr: map_ptr!(&props), | 
|  | prop_values_ptr: map_ptr!(&values), | 
|  | count_props: map_len!(&props), | 
|  | obj_id, | 
|  | obj_type, | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::obj_get_properties(fd, &mut info)?; | 
|  | } | 
|  |  | 
|  | map_set!(props, info.count_props as usize); | 
|  | map_set!(values, info.count_props as usize); | 
|  |  | 
|  | Ok(info) | 
|  | } | 
|  |  | 
|  | /// Set the properties of an object | 
|  | pub fn set_property( | 
|  | fd: BorrowedFd<'_>, | 
|  | prop_id: u32, | 
|  | obj_id: u32, | 
|  | obj_type: u32, | 
|  | value: u64, | 
|  | ) -> io::Result<()> { | 
|  | let mut prop = drm_mode_obj_set_property { | 
|  | value, | 
|  | prop_id, | 
|  | obj_id, | 
|  | obj_type, | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::obj_set_property(fd, &mut prop)?; | 
|  | } | 
|  |  | 
|  | Ok(()) | 
|  | } | 
|  |  | 
|  | /// Schedule a page flip | 
|  | pub fn page_flip( | 
|  | fd: BorrowedFd<'_>, | 
|  | crtc_id: u32, | 
|  | fb_id: u32, | 
|  | flags: u32, | 
|  | sequence: u32, | 
|  | ) -> io::Result<()> { | 
|  | let mut flip = drm_mode_crtc_page_flip { | 
|  | crtc_id, | 
|  | fb_id, | 
|  | flags, | 
|  | // Same struct as drm_mode_crtc_page_flip_target | 
|  | reserved: sequence, | 
|  | user_data: crtc_id as _, | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::crtc_page_flip(fd, &mut flip)?; | 
|  | } | 
|  |  | 
|  | Ok(()) | 
|  | } | 
|  |  | 
|  | /// Atomically set properties | 
|  | pub fn atomic_commit( | 
|  | fd: BorrowedFd<'_>, | 
|  | flags: u32, | 
|  | objs: &mut [u32], | 
|  | prop_counts: &mut [u32], | 
|  | props: &mut [u32], | 
|  | values: &mut [u64], | 
|  | ) -> io::Result<()> { | 
|  | let mut atomic = drm_mode_atomic { | 
|  | flags, | 
|  | count_objs: objs.len() as _, | 
|  | objs_ptr: objs.as_mut_ptr() as _, | 
|  | count_props_ptr: prop_counts.as_mut_ptr() as _, | 
|  | props_ptr: props.as_mut_ptr() as _, | 
|  | prop_values_ptr: values.as_mut_ptr() as _, | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::atomic(fd, &mut atomic)?; | 
|  | } | 
|  |  | 
|  | Ok(()) | 
|  | } | 
|  |  | 
|  | /// Create a drm lease | 
|  | pub fn create_lease( | 
|  | fd: BorrowedFd<'_>, | 
|  | objects: &[u32], | 
|  | flags: u32, | 
|  | ) -> io::Result<drm_mode_create_lease> { | 
|  | let mut data = drm_mode_create_lease { | 
|  | object_ids: objects.as_ptr() as _, | 
|  | object_count: objects.len() as u32, | 
|  | flags, | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::create_lease(fd, &mut data)?; | 
|  | } | 
|  |  | 
|  | Ok(data) | 
|  | } | 
|  |  | 
|  | /// List all active drm leases | 
|  | pub fn list_lessees( | 
|  | fd: BorrowedFd<'_>, | 
|  | mut lessees: Option<&mut Vec<u32>>, | 
|  | ) -> io::Result<drm_mode_list_lessees> { | 
|  | let mut sizes = drm_mode_list_lessees::default(); | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::list_lessees(fd, &mut sizes)?; | 
|  | }; | 
|  |  | 
|  | map_reserve!(lessees, sizes.count_lessees as usize); | 
|  |  | 
|  | let mut data = drm_mode_list_lessees { | 
|  | lessees_ptr: map_ptr!(&lessees), | 
|  | count_lessees: map_len!(&lessees), | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::list_lessees(fd, &mut data)?; | 
|  | }; | 
|  |  | 
|  | map_set!(lessees, data.count_lessees as usize); | 
|  |  | 
|  | Ok(data) | 
|  | } | 
|  |  | 
|  | /// Get leased objects for a lease file descriptor | 
|  | pub fn get_lease( | 
|  | fd: BorrowedFd<'_>, | 
|  | mut objects: Option<&mut Vec<u32>>, | 
|  | ) -> io::Result<drm_mode_get_lease> { | 
|  | let mut sizes = drm_mode_get_lease::default(); | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::get_lease(fd, &mut sizes)?; | 
|  | } | 
|  |  | 
|  | map_reserve!(objects, sizes.count_objects as usize); | 
|  |  | 
|  | let mut data = drm_mode_get_lease { | 
|  | count_objects: map_len!(&objects), | 
|  | objects_ptr: map_ptr!(&objects), | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::get_lease(fd, &mut data)?; | 
|  | } | 
|  |  | 
|  | map_set!(objects, data.count_objects as usize); | 
|  |  | 
|  | Ok(data) | 
|  | } | 
|  |  | 
|  | /// Revoke previously issued lease | 
|  | pub fn revoke_lease(fd: BorrowedFd<'_>, lessee_id: u32) -> io::Result<()> { | 
|  | let mut data = drm_mode_revoke_lease { lessee_id }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::revoke_lease(fd, &mut data)?; | 
|  | } | 
|  |  | 
|  | Ok(()) | 
|  | } | 
|  |  | 
|  | /// | 
|  | /// Dumbbuffers are basic buffers that can be used for scanout. | 
|  | /// | 
|  | pub mod dumbbuffer { | 
|  | use crate::ioctl; | 
|  | use drm_sys::*; | 
|  |  | 
|  | use std::{io, os::unix::io::BorrowedFd}; | 
|  |  | 
|  | /// Create a dumb buffer | 
|  | pub fn create( | 
|  | fd: BorrowedFd<'_>, | 
|  | width: u32, | 
|  | height: u32, | 
|  | bpp: u32, | 
|  | flags: u32, | 
|  | ) -> io::Result<drm_mode_create_dumb> { | 
|  | let mut db = drm_mode_create_dumb { | 
|  | height, | 
|  | width, | 
|  | bpp, | 
|  | flags, | 
|  | ..Default::default() | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::create_dumb(fd, &mut db)?; | 
|  | } | 
|  |  | 
|  | Ok(db) | 
|  | } | 
|  |  | 
|  | /// Destroy a dumb buffer | 
|  | pub fn destroy(fd: BorrowedFd<'_>, handle: u32) -> io::Result<drm_mode_destroy_dumb> { | 
|  | let mut db = drm_mode_destroy_dumb { handle }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::destroy_dumb(fd, &mut db)?; | 
|  | } | 
|  |  | 
|  | Ok(db) | 
|  | } | 
|  |  | 
|  | /// Map a dump buffer and prep it for an mmap | 
|  | pub fn map( | 
|  | fd: BorrowedFd<'_>, | 
|  | handle: u32, | 
|  | pad: u32, | 
|  | offset: u64, | 
|  | ) -> io::Result<drm_mode_map_dumb> { | 
|  | let mut map = drm_mode_map_dumb { | 
|  | handle, | 
|  | pad, | 
|  | offset, | 
|  | }; | 
|  |  | 
|  | unsafe { | 
|  | ioctl::mode::map_dumb(fd, &mut map)?; | 
|  | } | 
|  |  | 
|  | Ok(map) | 
|  | } | 
|  | } |