| mod utils; |
| use crate::utils::*; |
| |
| use drm::control::Device as ControlDevice; |
| use drm::Device as BasicDevice; |
| |
| use drm::buffer::DrmFourcc; |
| |
| use drm::control::{self, atomic, connector, crtc, property, AtomicCommitFlags}; |
| |
| pub fn main() { |
| let card = Card::open_global(); |
| |
| card.set_client_capability(drm::ClientCapability::UniversalPlanes, true) |
| .expect("Unable to request UniversalPlanes capability"); |
| card.set_client_capability(drm::ClientCapability::Atomic, true) |
| .expect("Unable to request Atomic capability"); |
| |
| // Load the information. |
| let res = card |
| .resource_handles() |
| .expect("Could not load normal resource ids."); |
| let coninfo: Vec<connector::Info> = res |
| .connectors() |
| .iter() |
| .flat_map(|con| card.get_connector(*con, true)) |
| .collect(); |
| let crtcinfo: Vec<crtc::Info> = res |
| .crtcs() |
| .iter() |
| .flat_map(|crtc| card.get_crtc(*crtc)) |
| .collect(); |
| |
| // Filter each connector until we find one that's connected. |
| let con = coninfo |
| .iter() |
| .find(|&i| i.state() == connector::State::Connected) |
| .expect("No connected connectors"); |
| |
| // Get the first (usually best) mode |
| let &mode = con.modes().first().expect("No modes found on connector"); |
| |
| let (disp_width, disp_height) = mode.size(); |
| |
| // Find a crtc and FB |
| let crtc = crtcinfo.first().expect("No crtcs found"); |
| |
| // Select the pixel format |
| let fmt = DrmFourcc::Xrgb8888; |
| |
| // Create a DB |
| // If buffer resolution is above display resolution, a ENOSPC (not enough GPU memory) error may |
| // occur |
| let mut db = card |
| .create_dumb_buffer((disp_width.into(), disp_height.into()), fmt, 32) |
| .expect("Could not create dumb buffer"); |
| |
| // Map it and grey it out. |
| { |
| let mut map = card |
| .map_dumb_buffer(&mut db) |
| .expect("Could not map dumbbuffer"); |
| for b in map.as_mut() { |
| *b = 128; |
| } |
| } |
| |
| // Create an FB: |
| let fb = card |
| .add_framebuffer(&db, 24, 32) |
| .expect("Could not create FB"); |
| |
| let planes = card.plane_handles().expect("Could not list planes"); |
| let (better_planes, compatible_planes): ( |
| Vec<control::plane::Handle>, |
| Vec<control::plane::Handle>, |
| ) = planes |
| .iter() |
| .filter(|&&plane| { |
| card.get_plane(plane) |
| .map(|plane_info| { |
| let compatible_crtcs = res.filter_crtcs(plane_info.possible_crtcs()); |
| compatible_crtcs.contains(&crtc.handle()) |
| }) |
| .unwrap_or(false) |
| }) |
| .partition(|&&plane| { |
| if let Ok(props) = card.get_properties(plane) { |
| for (&id, &val) in props.iter() { |
| if let Ok(info) = card.get_property(id) { |
| if info.name().to_str().map(|x| x == "type").unwrap_or(false) { |
| return val == (drm::control::PlaneType::Primary as u32).into(); |
| } |
| } |
| } |
| } |
| false |
| }); |
| let plane = *better_planes.first().unwrap_or(&compatible_planes[0]); |
| |
| println!("{:#?}", mode); |
| println!("{:#?}", fb); |
| println!("{:#?}", db); |
| println!("{:#?}", plane); |
| |
| let con_props = card |
| .get_properties(con.handle()) |
| .expect("Could not get props of connector") |
| .as_hashmap(&card) |
| .expect("Could not get a prop from connector"); |
| let crtc_props = card |
| .get_properties(crtc.handle()) |
| .expect("Could not get props of crtc") |
| .as_hashmap(&card) |
| .expect("Could not get a prop from crtc"); |
| let plane_props = card |
| .get_properties(plane) |
| .expect("Could not get props of plane") |
| .as_hashmap(&card) |
| .expect("Could not get a prop from plane"); |
| |
| let mut atomic_req = atomic::AtomicModeReq::new(); |
| atomic_req.add_property( |
| con.handle(), |
| con_props["CRTC_ID"].handle(), |
| property::Value::CRTC(Some(crtc.handle())), |
| ); |
| let blob = card |
| .create_property_blob(&mode) |
| .expect("Failed to create blob"); |
| atomic_req.add_property(crtc.handle(), crtc_props["MODE_ID"].handle(), blob); |
| atomic_req.add_property( |
| crtc.handle(), |
| crtc_props["ACTIVE"].handle(), |
| property::Value::Boolean(true), |
| ); |
| atomic_req.add_property( |
| plane, |
| plane_props["FB_ID"].handle(), |
| property::Value::Framebuffer(Some(fb)), |
| ); |
| atomic_req.add_property( |
| plane, |
| plane_props["CRTC_ID"].handle(), |
| property::Value::CRTC(Some(crtc.handle())), |
| ); |
| atomic_req.add_property( |
| plane, |
| plane_props["SRC_X"].handle(), |
| property::Value::UnsignedRange(0), |
| ); |
| atomic_req.add_property( |
| plane, |
| plane_props["SRC_Y"].handle(), |
| property::Value::UnsignedRange(0), |
| ); |
| atomic_req.add_property( |
| plane, |
| plane_props["SRC_W"].handle(), |
| property::Value::UnsignedRange((mode.size().0 as u64) << 16), |
| ); |
| atomic_req.add_property( |
| plane, |
| plane_props["SRC_H"].handle(), |
| property::Value::UnsignedRange((mode.size().1 as u64) << 16), |
| ); |
| atomic_req.add_property( |
| plane, |
| plane_props["CRTC_X"].handle(), |
| property::Value::SignedRange(0), |
| ); |
| atomic_req.add_property( |
| plane, |
| plane_props["CRTC_Y"].handle(), |
| property::Value::SignedRange(0), |
| ); |
| atomic_req.add_property( |
| plane, |
| plane_props["CRTC_W"].handle(), |
| property::Value::UnsignedRange(mode.size().0 as u64), |
| ); |
| atomic_req.add_property( |
| plane, |
| plane_props["CRTC_H"].handle(), |
| property::Value::UnsignedRange(mode.size().1 as u64), |
| ); |
| |
| // Set the crtc |
| // On many setups, this requires root access. |
| card.atomic_commit(AtomicCommitFlags::ALLOW_MODESET, atomic_req) |
| .expect("Failed to set mode"); |
| |
| let five_seconds = ::std::time::Duration::from_millis(5000); |
| ::std::thread::sleep(five_seconds); |
| |
| card.destroy_framebuffer(fb).unwrap(); |
| card.destroy_dumb_buffer(db).unwrap(); |
| } |