Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Smithay/drm-rs/llms.txt
Use this file to discover all available pages before exploring further.
AtomicModeReq is the builder type for DRM atomic modesetting commits. Rather than manipulating each KMS object independently, you accumulate property-value pairs for any number of connectors, CRTCs, and planes into a single request, then submit it in one atomic operation via Device::atomic_commit(). The kernel validates the entire configuration before applying any part of it, guaranteeing that displays never enter an intermediate invalid state.
Atomic modesetting is the preferred API for all modern DRM user-space. It is required for features such as format modifiers, HDR metadata, variable refresh rate (VRR), and display synchronisation with GPU command submission.
AtomicModeReq
#[derive(Debug, Clone, Default)]
pub struct AtomicModeReq { /* ... */ }
The struct is Clone, so you can build a base configuration, clone it, modify the clone, and submit both independently (e.g. to test before applying).
AtomicModeReq::new
pub fn new() -> AtomicModeReq
Creates an empty atomic request. Equivalent to AtomicModeReq::default().
add_property
pub fn add_property<H>(
&mut self,
handle: H,
property: property::Handle,
value: property::Value<'_>,
) where
H: ResourceHandle,
Adds or updates a property-value pair for the KMS object identified by handle. value is a typed property::Value<'_> which is converted to a RawValue (u64) internally.
If the same object-property pair is added more than once, the later value replaces the earlier one. Objects are stored in sorted order internally so that the kernel receives them efficiently.
add_raw_property
pub fn add_raw_property(
&mut self,
obj_id: RawResourceHandle,
prop_id: property::Handle,
value: property::RawValue,
)
Lower-level variant of add_property that accepts a RawResourceHandle (NonZeroU32) and a u64 value directly, bypassing the typed Value conversion. Useful when working with property IDs and values obtained from other APIs or when writing generic infrastructure.
is_empty
pub fn is_empty(&self) -> bool
Returns true if no property-value pairs have been added. Use to guard against submitting a no-op commit.
AtomicCommitFlags
Bitflags passed to Device::atomic_commit() to control commit behaviour.
| Flag | Description |
|---|
PAGE_FLIP_EVENT | Deliver a PageFlipEvent via receive_events() when the commit is applied on the next vblank |
PAGE_FLIP_ASYNC | Apply the commit immediately without waiting for the vblank boundary |
TEST_ONLY | Validate the request against hardware constraints without making any changes. Returns Ok(()) if the configuration is valid |
NONBLOCK | Return immediately after queuing the commit rather than blocking until vblank |
ALLOW_MODESET | Permit full modeset operations (clock reprogramming, link training). Required whenever you change the display mode or enable a new output. Commits that need a modeset are rejected without this flag. |
Flags are combined with the bitwise OR operator:
let flags = AtomicCommitFlags::NONBLOCK | AtomicCommitFlags::PAGE_FLIP_EVENT;
Plane source coordinates (SRC_X, SRC_Y, SRC_W, SRC_H) use Q16.16 fixed-point format. Multiply integer pixel values by 65536 when writing them to the request.
Complete Example
The following example performs a full atomic modeset: activating a CRTC with a display mode, connecting a connector, and configuring the primary plane with a framebuffer.
use drm::control::{
atomic::AtomicModeReq,
AtomicCommitFlags,
Device as ControlDevice,
};
// --- Resolve handles and properties (omitted: see property reference) ---
// crtc_handle: crtc::Handle
// connector_handle: connector::Handle
// plane_handle: plane::Handle (primary plane for the CRTC)
// fb_handle: framebuffer::Handle
// mode: drm::control::Mode (from connector.modes()[0])
//
// Property handles (looked up by name via get_properties + get_property):
// active_prop: "active" on the CRTC
// mode_id_prop: "MODE_ID" on the CRTC
// crtc_id_prop: "CRTC_ID" on the connector and plane
// fb_id_prop: "FB_ID" on the plane
// src_x/y/w/h: "SRC_X" etc. on the plane
// crtc_x/y/w/h: "CRTC_X" etc. on the plane
// Create a mode blob
let mode_blob = card.create_property_blob(&mode).unwrap();
let mode_blob_id: u64 = match mode_blob {
drm::control::property::Value::Blob(id) => id,
_ => panic!("expected blob"),
};
let mut req = AtomicModeReq::new();
// --- CRTC ---
// Enable the CRTC
req.add_property(
crtc_handle,
active_prop,
drm::control::property::Value::Boolean(true),
);
// Assign the display mode via its blob ID
req.add_property(
crtc_handle,
mode_id_prop,
drm::control::property::Value::Blob(mode_blob_id),
);
// --- Connector ---
// Route the connector to this CRTC
req.add_property(
connector_handle,
crtc_id_prop,
drm::control::property::Value::CRTC(Some(crtc_handle)),
);
// --- Primary Plane ---
req.add_property(
plane_handle,
fb_id_prop,
drm::control::property::Value::Framebuffer(Some(fb_handle)),
);
req.add_property(
plane_handle,
crtc_id_prop,
drm::control::property::Value::CRTC(Some(crtc_handle)),
);
// Source rectangle (Q16.16 fixed-point: pixels × 65536)
let (width, height) = (1920u32, 1080u32);
req.add_property(plane_handle, src_x_prop, drm::control::property::Value::UnsignedRange(0));
req.add_property(plane_handle, src_y_prop, drm::control::property::Value::UnsignedRange(0));
req.add_property(plane_handle, src_w_prop, drm::control::property::Value::UnsignedRange((width as u64) << 16));
req.add_property(plane_handle, src_h_prop, drm::control::property::Value::UnsignedRange((height as u64) << 16));
// Destination rectangle (pixels, signed for CRTC_X/Y, unsigned for W/H)
req.add_property(plane_handle, crtc_x_prop, drm::control::property::Value::SignedRange(0));
req.add_property(plane_handle, crtc_y_prop, drm::control::property::Value::SignedRange(0));
req.add_property(plane_handle, crtc_w_prop, drm::control::property::Value::UnsignedRange(width as u64));
req.add_property(plane_handle, crtc_h_prop, drm::control::property::Value::UnsignedRange(height as u64));
// --- Validate first ---
card.atomic_commit(AtomicCommitFlags::TEST_ONLY, req.clone()).unwrap();
// --- Apply ---
card.atomic_commit(
AtomicCommitFlags::ALLOW_MODESET | AtomicCommitFlags::PAGE_FLIP_EVENT,
req,
).unwrap();
Page Flipping with AtomicModeReq
For steady-state rendering (after the initial modeset), only the framebuffer assignment needs to change:
let mut req = AtomicModeReq::new();
req.add_property(
plane_handle,
fb_id_prop,
drm::control::property::Value::Framebuffer(Some(new_fb_handle)),
);
card.atomic_commit(
AtomicCommitFlags::NONBLOCK | AtomicCommitFlags::PAGE_FLIP_EVENT,
req,
).unwrap();
// Wait for the flip event before submitting the next frame
let events = card.receive_events().unwrap();
for event in events {
if let drm::control::Event::PageFlip(_) = event {
// Safe to reuse the old framebuffer now
}
}
Always test new configurations with AtomicCommitFlags::TEST_ONLY before applying them. This is especially important when changing modes or enabling new outputs, as some hardware combinations are valid in isolation but incompatible together.