denise_drm/lib.rs
1//! Linux DRM/KMS backend for Denise.
2//!
3//! Opens the display directly, sets a mode, and page-flips CPU-rendered dumb
4//! buffers straight to the scanout engine. No compositor, no window server, no
5//! GPU driver stack, no X.
6//!
7//! # What this is not doing, and why
8//!
9//! **No `gbm`.** GBM exists to allocate buffers a *GPU* renders into. Denise
10//! renders with the CPU, so DRM dumb buffers are exactly the right allocation:
11//! scanout-capable, CPU-mappable, and free of any C library. Adding GBM would drag
12//! in libgbm and Mesa, which would end both the single-static-binary goal and easy
13//! cross-compilation.
14//!
15//! **No atomic modesetting, yet.** Atomic buys three things: `FB_DAMAGE_CLIPS`,
16//! plane composition, and tear-free guarantees. The first is worth little here —
17//! a page flip swaps whole buffers, so damage saves rasterisation, not bandwidth,
18//! and most drivers ignore the property anyway. The second has a legacy equivalent
19//! for the one plane that matters, the hardware cursor. So the legacy path gets
20//! this milestone working on real hardware at a third of the code, behind a seam
21//! that atomic can take over when planes actually earn their keep.
22//!
23//! # Becoming DRM master
24//!
25//! Setting a mode requires being DRM master, and only one process can be. If a
26//! compositor or another Denise process holds it, [`Card::become_master`] fails
27//! with `EBUSY` or `EACCES` — that is the single most common reason a first run on
28//! a Pi does nothing. Three ways to have the right:
29//!
30//! - Run on a bare VT with no display server. The usual kiosk deployment.
31//! - Be handed a file descriptor by `libseat` or a systemd unit, via
32//! [`Card::from_fd`]. Preferred for anything that has to coexist.
33//! - Run as root. Works, and is a poor way to ship a product.
34//!
35//! # Testing
36//!
37//! [`mode`] and [`swapchain`] are platform-independent on purpose and are unit
38//! tested everywhere, including on machines with no DRM device. They hold the
39//! decisions that are hard to debug in the field and easy to check on a laptop.
40//! Everything else is a thin wrapper over ioctls and can only be proven on real
41//! hardware.
42
43// Off Linux, some of the items the documentation above links to are compiled
44// out, so those links resolve to nothing and `cargo doc` fails. CI documents on
45// Ubuntu and never sees it; a developer on a Mac cannot avoid it. Linux stays
46// the platform that checks these links, being the one with the items to check
47// them against.
48#![cfg_attr(not(target_os = "linux"), allow(rustdoc::broken_intra_doc_links))]
49
50pub mod mode;
51pub mod swapchain;
52
53pub use mode::{
54 ConnectorInfo, ConnectorKind, ModeInfo, ModePreference, OutputPreference, Selection,
55 SelectionError,
56};
57pub use swapchain::Swapchain;
58
59#[cfg(target_os = "linux")]
60mod cursor;
61#[cfg(target_os = "linux")]
62mod device;
63#[cfg(target_os = "linux")]
64mod error;
65#[cfg(target_os = "linux")]
66mod surface;
67
68#[cfg(target_os = "linux")]
69pub use device::Card;
70#[cfg(target_os = "linux")]
71pub use error::DrmError;
72#[cfg(target_os = "linux")]
73pub use surface::{DrmSurface, PresentMode, SurfaceConfig};
74
75/// Compiles the examples in this crate's README, so they cannot drift from the API
76/// they claim to demonstrate. Never built except under `cargo test --doc`.
77#[cfg(doctest)]
78#[doc = include_str!("../README.md")]
79struct Readme;