//! //! 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>, mut crtcs: Option<&mut Vec>, mut connectors: Option<&mut Vec>, mut encoders: Option<&mut Vec>, ) -> io::Result { 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>, ) -> io::Result { 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 { 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 { 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 { 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 { 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 { 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 { 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, ) -> io::Result { 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 { 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 { 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 { 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 { 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 { 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>, mut prop_values: Option<&mut Vec>, mut modes: Option<&mut Vec>, mut encoders: Option<&mut Vec>, force_probe: bool, ) -> io::Result { 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 { 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>, ) -> io::Result { 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 { 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>, mut enums: Option<&mut Vec>, ) -> io::Result { 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 { 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>, ) -> io::Result { 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 { 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 { 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>, mut values: Option<&mut Vec>, ) -> io::Result { 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 { 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>, ) -> io::Result { 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>, ) -> io::Result { 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 { 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 { 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 { let mut map = drm_mode_map_dumb { handle, pad, offset, }; unsafe { ioctl::mode::map_dumb(fd, &mut map)?; } Ok(map) } }