diff --git a/examples/raw_atomic_drm.rs b/examples/raw_atomic_drm.rs new file mode 100644 index 0000000..9877376 --- /dev/null +++ b/examples/raw_atomic_drm.rs @@ -0,0 +1,183 @@ +#![warn(rust_2018_idioms)] + +#[macro_use] +extern crate slog; + +use slog::Drain; +use smithay::{ + backend::drm::{ + atomic::{AtomicDrmDevice, AtomicDrmSurface}, + common::Error, + device_bind, Device, DeviceHandler, RawSurface, Surface, + }, + reexports::{ + calloop::EventLoop, + drm::{ + buffer::format::PixelFormat, + control::{ + connector::State as ConnectorState, crtc, dumbbuffer::DumbBuffer, framebuffer, property, + Device as ControlDevice, ResourceHandle, + }, + }, + }, +}; +use std::{ + fs::{File, OpenOptions}, + io::Error as IoError, + rc::Rc, + sync::Mutex, +}; + +fn get_property_by_name<'a, D: ControlDevice, T: ResourceHandle>( + dev: &'a D, + handle: T, + name: &'static str, +) -> Option<(property::ValueType, property::RawValue)> { + let props = dev.get_properties(handle).expect("Could not get props"); + let (ids, vals) = props.as_props_and_values(); + for (&id, &val) in ids.iter().zip(vals.iter()) { + let info = dev.get_property(id).unwrap(); + if info.name().to_str().map(|x| x == name).unwrap_or(false) { + let val_ty = info.value_type(); + return Some((val_ty, val)); + } + } + None +} + +fn main() { + let log = slog::Logger::root(Mutex::new(slog_term::term_full().fuse()).fuse(), o!()); + + /* + * Initialize the drm backend + */ + + // "Find" a suitable drm device + let mut options = OpenOptions::new(); + options.read(true); + options.write(true); + let mut device = AtomicDrmDevice::new(options.open("/dev/dri/card0").unwrap(), log.clone()).unwrap(); + + // Get a set of all modesetting resource handles (excluding planes): + let res_handles = Device::resource_handles(&device).unwrap(); + + // Use first connected connector + let connector_info = res_handles + .connectors() + .iter() + .map(|conn| device.get_connector_info(*conn).unwrap()) + .find(|conn| conn.state() == ConnectorState::Connected) + .unwrap(); + + // use the connected crtc if any + let (val_ty, raw) = get_property_by_name(&device, connector_info.handle(), "CRTC_ID").unwrap(); + let crtc = match val_ty.convert_value(raw) { + property::Value::CRTC(Some(handle)) => handle, + property::Value::CRTC(None) => { + // Use the first encoder + let encoder = connector_info + .encoders() + .iter() + .filter_map(|&e| e) + .next() + .unwrap(); + let encoder_info = device.get_encoder_info(encoder).unwrap(); + + *res_handles + .filter_crtcs(encoder_info.possible_crtcs()) + .iter() + .next() + .unwrap() + } + _ => unreachable!("CRTC_ID does not return another property type"), + }; + + // Assuming we found a good connector and loaded the info into `connector_info` + let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.) + + // Initialize the hardware backend + let surface = Rc::new(device.create_surface(crtc).unwrap()); + surface.set_connectors(&[connector_info.handle()]).unwrap(); + surface.use_mode(Some(mode)).unwrap(); + + for conn in surface.current_connectors().into_iter() { + if conn != connector_info.handle() { + surface.remove_connector(conn).unwrap(); + } + } + surface.add_connector(connector_info.handle()).unwrap(); + + /* + * Lets create buffers and framebuffers. + * We use drm-rs DumbBuffers, because they always work and require little to no setup. + * But they are very slow, this is just for demonstration purposes. + */ + let (w, h) = mode.size(); + let front_buffer = device + .create_dumb_buffer((w as u32, h as u32), PixelFormat::XRGB8888) + .unwrap(); + let front_framebuffer = device.add_framebuffer(&front_buffer).unwrap(); + let back_buffer = device + .create_dumb_buffer((w as u32, h as u32), PixelFormat::XRGB8888) + .unwrap(); + let back_framebuffer = device.add_framebuffer(&back_buffer).unwrap(); + + device.set_handler(DrmHandlerImpl { + current: front_framebuffer, + front: (front_buffer, front_framebuffer), + back: (back_buffer, back_framebuffer), + surface: surface.clone(), + }); + + /* + * Register the DrmDevice on the EventLoop + */ + let mut event_loop = EventLoop::<()>::new().unwrap(); + let _source = device_bind(&event_loop.handle(), device) + .map_err(|err| -> IoError { err.into() }) + .unwrap(); + + // Start rendering + if surface.commit_pending() { + surface.commit(front_framebuffer).unwrap(); + } + + // Run + event_loop.run(None, &mut (), |_| {}).unwrap(); +} + +pub struct DrmHandlerImpl { + front: (DumbBuffer, framebuffer::Handle), + back: (DumbBuffer, framebuffer::Handle), + current: framebuffer::Handle, + surface: Rc>, +} + +impl DeviceHandler for DrmHandlerImpl { + type Device = AtomicDrmDevice; + + fn vblank(&mut self, _crtc: crtc::Handle) { + { + // Swap and map buffer + let mut mapping = if self.current == self.front.1 { + self.current = self.back.1; + self.surface.map_dumb_buffer(&mut self.back.0).unwrap() + } else { + self.current = self.front.1; + self.surface.map_dumb_buffer(&mut self.front.0).unwrap() + }; + + // now we could render to the mapping via software rendering. + // this example just sets some grey color + + for x in mapping.as_mut() { + *x = 128; + } + } + RawSurface::page_flip(&*self.surface, self.current).unwrap(); + } + + fn error(&mut self, error: Error) { + panic!("{:?}", error); + } +}