#[cfg(all(
feature = "kms",
not(any(
target_os = "android",
target_vendor = "apple",
target_os = "redox",
target_family = "wasm",
target_os = "windows"
))
))]
mod imple {
use drm::control::{connector, Device as CtrlDevice, Event, ModeTypeFlags, PlaneType};
use drm::Device;
use raw_window_handle::{DisplayHandle, DrmDisplayHandle, DrmWindowHandle, WindowHandle};
use softbuffer::{Context, Surface};
use std::num::NonZeroU32;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd};
use std::path::Path;
use std::time::{Duration, Instant};
pub(super) fn entry() -> Result<(), Box<dyn std::error::Error>> {
let device = Card::find()?;
let context = unsafe {
Context::new(DisplayHandle::borrow_raw({
let handle = DrmDisplayHandle::new(device.as_fd().as_raw_fd());
handle.into()
}))
}?;
let handles = device.resource_handles()?;
let connectors = handles
.connectors()
.iter()
.map(|&con| device.get_connector(con, true))
.collect::<Result<Vec<_>, _>>()?;
let crtcs = handles
.crtcs()
.iter()
.map(|&crtc| device.get_crtc(crtc))
.collect::<Result<Vec<_>, _>>()?;
let con = connectors
.iter()
.find(|con| con.state() == connector::State::Connected)
.ok_or("No connected connectors")?;
let crtc = crtcs.first().ok_or("No CRTCs")?;
let mode = con
.modes()
.iter()
.find(|mode| mode.mode_type().contains(ModeTypeFlags::PREFERRED))
.or_else(|| con.modes().first())
.ok_or("No modes")?;
let planes = device.plane_handles()?;
let planes = planes
.iter()
.filter(|&&plane| {
device.get_plane(plane).is_ok_and(|plane| {
let crtcs = handles.filter_crtcs(plane.possible_crtcs());
crtcs.contains(&crtc.handle())
})
})
.collect::<Vec<_>>();
let plane = planes
.iter()
.find(|&&&plane| {
if let Ok(props) = device.get_properties(plane) {
let (ids, vals) = props.as_props_and_values();
for (&id, &val) in ids.iter().zip(vals.iter()) {
if let Ok(info) = device.get_property(id) {
if info.name().to_str() == Ok("type") {
return val == PlaneType::Primary as u32 as u64;
}
}
}
}
false
})
.or(planes.first())
.ok_or("No planes")?;
let mut surface = unsafe {
Surface::new(
&context,
WindowHandle::borrow_raw({
let handle = DrmWindowHandle::new((**plane).into());
handle.into()
}),
)
}?;
let (width, height) = mode.size();
surface.resize(
NonZeroU32::new(width as u32).unwrap(),
NonZeroU32::new(height as u32).unwrap(),
)?;
let start = Instant::now();
let mut tick = 0;
while Instant::now().duration_since(start) < Duration::from_secs(2) {
tick += 1;
println!("Drawing tick {tick}");
let mut buffer = surface.buffer_mut()?;
draw_to_buffer(&mut buffer, tick);
buffer.present()?;
rustix::event::poll(
&mut [rustix::event::PollFd::new(
&device,
rustix::event::PollFlags::IN,
)],
None,
)?;
let events = device.receive_events()?;
println!("Got some events...");
for event in events {
match event {
Event::PageFlip(_) => {
println!("Page flip event.");
}
Event::Vblank(_) => {
println!("Vblank event.");
}
_ => {
println!("Unknown event.");
}
}
}
}
Ok(())
}
fn draw_to_buffer(buf: &mut [u32], tick: usize) {
let scale = colorous::SINEBOW;
let mut i = (tick as f64) / 20.0;
while i > 1.0 {
i -= 1.0;
}
let color = scale.eval_continuous(i);
let pixel = ((color.r as u32) << 16) | ((color.g as u32) << 8) | (color.b as u32);
buf.fill(pixel);
}
struct Card(std::fs::File);
impl Card {
fn find() -> Result<Card, Box<dyn std::error::Error>> {
for i in 0..10 {
let path = format!("/dev/dri/card{i}");
let Ok(device) = Card::open(path) else {
continue;
};
let Ok(handles) = device.resource_handles() else {
continue;
};
if handles
.connectors
.iter()
.filter_map(|c| device.get_connector(*c, false).ok())
.any(|c| c.state() == connector::State::Connected)
{
return Ok(device);
}
}
Err("No DRM device found".into())
}
fn open(path: impl AsRef<Path>) -> Result<Card, Box<dyn std::error::Error>> {
let file = std::fs::OpenOptions::new()
.read(true)
.write(true)
.open(path)?;
Ok(Card(file))
}
}
impl AsFd for Card {
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
}
impl Device for Card {}
impl CtrlDevice for Card {}
}
#[cfg(not(all(
feature = "kms",
not(any(
target_os = "android",
target_vendor = "apple",
target_os = "redox",
target_family = "wasm",
target_os = "windows"
))
)))]
mod imple {
pub(super) fn entry() -> Result<(), Box<dyn std::error::Error>> {
eprintln!("This example requires the `kms` feature.");
Ok(())
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
imple::entry()
}