Skip to main content

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.
FlagDescription
PAGE_FLIP_EVENTDeliver a PageFlipEvent via receive_events() when the commit is applied on the next vblank
PAGE_FLIP_ASYNCApply the commit immediately without waiting for the vblank boundary
TEST_ONLYValidate the request against hardware constraints without making any changes. Returns Ok(()) if the configuration is valid
NONBLOCKReturn immediately after queuing the commit rather than blocking until vblank
ALLOW_MODESETPermit 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.

Build docs developers (and LLMs) love