pub struct Card { /* private fields */ }
Implementations§
Source§impl Card
impl Card
Sourcepub fn open(path: &CStr) -> Result<Self, InitError>
pub fn open(path: &CStr) -> Result<Self, InitError>
Open the file at the given path and attempt to use it as a DRM card file descriptor.
Returns result::InitError::NotDrmCard
if the opened file
does not support the DRM_IOCTL_VERSION
ioctl request.
Examples found in repository?
11fn main() -> std::io::Result<()> {
12 let mut card = Card::open(c"/dev/dri/card0").map_err(map_init_err)?;
13 card.become_master().map_err(map_err)?;
14
15 {
16 let name = card.driver_name().map_err(map_err)?;
17 let name = String::from_utf8_lossy(&name);
18 println!("Driver name: {name}");
19 }
20
21 if card
22 .get_device_cap(DeviceCap::DumbBuffer)
23 .map_err(map_err)?
24 == 0
25 {
26 return Err(std::io::Error::other(
27 "device does not support 'dumb buffers'",
28 ));
29 } else {
30 println!("Device supports 'dumb buffers'");
31 }
32
33 display_demo(&mut card).map_err(map_err)
34}
More examples
9fn main() -> std::io::Result<()> {
10 let mut card = Card::open(c"/dev/dri/card0").map_err(map_init_err)?;
11
12 {
13 let name = card.driver_name().map_err(map_err)?;
14 let name = String::from_utf8_lossy(&name);
15 println!("Driver name: {name}");
16 }
17
18 if card
19 .get_device_cap(DeviceCap::DumbBuffer)
20 .map_err(map_err)?
21 == 0
22 {
23 return Err(std::io::Error::other(
24 "device does not support 'dumb buffers'",
25 ));
26 }
27
28 card.set_client_cap(ClientCap::UniversalPlanes, 1)
29 .map_err(map_err)?;
30 card.set_client_cap(ClientCap::Atomic, 1).map_err(map_err)?;
31
32 show_properties(&card).map_err(map_err)
33}
11fn main() -> std::io::Result<()> {
12 let mut card = Card::open(c"/dev/dri/card0").map_err(map_init_err)?;
13 card.become_master().map_err(map_err)?;
14
15 {
16 let name = card.driver_name().map_err(map_err)?;
17 let name = String::from_utf8_lossy(&name);
18 println!("Driver name: {name}");
19 }
20
21 if card
22 .get_device_cap(DeviceCap::DumbBuffer)
23 .map_err(map_err)?
24 == 0
25 {
26 return Err(std::io::Error::other(
27 "device does not support 'dumb buffers'",
28 ));
29 } else {
30 println!("Device supports 'dumb buffers'");
31 }
32
33 card.set_client_cap(ClientCap::UniversalPlanes, 1)
34 .map_err(map_err)?;
35 card.set_client_cap(ClientCap::Atomic, 1).map_err(map_err)?;
36
37 display_demo(&mut card).map_err(map_err)
38}
Sourcepub fn from_file<D>(f: File<D>) -> Result<Self, InitError>
pub fn from_file<D>(f: File<D>) -> Result<Self, InitError>
Attempt to use the given file as a DRM card device.
Returns result::InitError::NotDrmCard
if the opened file
does not support the DRM_IOCTL_VERSION
ioctl request.
Sourcepub unsafe fn from_file_unchecked<D>(f: File<D>) -> Self
pub unsafe fn from_file_unchecked<D>(f: File<D>) -> Self
Wraps the given file in Card
without checking whether
it supports any DRM card ioctl requests.
Sourcepub fn fd(&self) -> int
pub fn fd(&self) -> int
Get the open file descriptor for the card.
Interacting with this file descriptor outside of the Card
abstraction
may cause the abstraction to malfunction. It’s exposed primarily so
it can be used with system functions like poll
to wait for events
on multiple file descriptors at once.
Sourcepub fn api_version(&self) -> Result<ApiVersion, Error>
pub fn api_version(&self) -> Result<ApiVersion, Error>
Determine the DRM API version supported by this device.
Sourcepub fn read_driver_name<'a>(
&self,
into: &'a mut [u8],
) -> Result<&'a mut [u8], Error>
pub fn read_driver_name<'a>( &self, into: &'a mut [u8], ) -> Result<&'a mut [u8], Error>
Read the driver name into the given slice.
Sourcepub fn driver_name(&self) -> Result<Vec<u8>, Error>
pub fn driver_name(&self) -> Result<Vec<u8>, Error>
Read the driver name into a vector.
Examples found in repository?
11fn main() -> std::io::Result<()> {
12 let mut card = Card::open(c"/dev/dri/card0").map_err(map_init_err)?;
13 card.become_master().map_err(map_err)?;
14
15 {
16 let name = card.driver_name().map_err(map_err)?;
17 let name = String::from_utf8_lossy(&name);
18 println!("Driver name: {name}");
19 }
20
21 if card
22 .get_device_cap(DeviceCap::DumbBuffer)
23 .map_err(map_err)?
24 == 0
25 {
26 return Err(std::io::Error::other(
27 "device does not support 'dumb buffers'",
28 ));
29 } else {
30 println!("Device supports 'dumb buffers'");
31 }
32
33 display_demo(&mut card).map_err(map_err)
34}
More examples
9fn main() -> std::io::Result<()> {
10 let mut card = Card::open(c"/dev/dri/card0").map_err(map_init_err)?;
11
12 {
13 let name = card.driver_name().map_err(map_err)?;
14 let name = String::from_utf8_lossy(&name);
15 println!("Driver name: {name}");
16 }
17
18 if card
19 .get_device_cap(DeviceCap::DumbBuffer)
20 .map_err(map_err)?
21 == 0
22 {
23 return Err(std::io::Error::other(
24 "device does not support 'dumb buffers'",
25 ));
26 }
27
28 card.set_client_cap(ClientCap::UniversalPlanes, 1)
29 .map_err(map_err)?;
30 card.set_client_cap(ClientCap::Atomic, 1).map_err(map_err)?;
31
32 show_properties(&card).map_err(map_err)
33}
11fn main() -> std::io::Result<()> {
12 let mut card = Card::open(c"/dev/dri/card0").map_err(map_init_err)?;
13 card.become_master().map_err(map_err)?;
14
15 {
16 let name = card.driver_name().map_err(map_err)?;
17 let name = String::from_utf8_lossy(&name);
18 println!("Driver name: {name}");
19 }
20
21 if card
22 .get_device_cap(DeviceCap::DumbBuffer)
23 .map_err(map_err)?
24 == 0
25 {
26 return Err(std::io::Error::other(
27 "device does not support 'dumb buffers'",
28 ));
29 } else {
30 println!("Device supports 'dumb buffers'");
31 }
32
33 card.set_client_cap(ClientCap::UniversalPlanes, 1)
34 .map_err(map_err)?;
35 card.set_client_cap(ClientCap::Atomic, 1).map_err(map_err)?;
36
37 display_demo(&mut card).map_err(map_err)
38}
Sourcepub fn get_device_cap(&self, capability: DeviceCap) -> Result<u64, Error>
pub fn get_device_cap(&self, capability: DeviceCap) -> Result<u64, Error>
Read a device capability value.
Examples found in repository?
11fn main() -> std::io::Result<()> {
12 let mut card = Card::open(c"/dev/dri/card0").map_err(map_init_err)?;
13 card.become_master().map_err(map_err)?;
14
15 {
16 let name = card.driver_name().map_err(map_err)?;
17 let name = String::from_utf8_lossy(&name);
18 println!("Driver name: {name}");
19 }
20
21 if card
22 .get_device_cap(DeviceCap::DumbBuffer)
23 .map_err(map_err)?
24 == 0
25 {
26 return Err(std::io::Error::other(
27 "device does not support 'dumb buffers'",
28 ));
29 } else {
30 println!("Device supports 'dumb buffers'");
31 }
32
33 display_demo(&mut card).map_err(map_err)
34}
More examples
9fn main() -> std::io::Result<()> {
10 let mut card = Card::open(c"/dev/dri/card0").map_err(map_init_err)?;
11
12 {
13 let name = card.driver_name().map_err(map_err)?;
14 let name = String::from_utf8_lossy(&name);
15 println!("Driver name: {name}");
16 }
17
18 if card
19 .get_device_cap(DeviceCap::DumbBuffer)
20 .map_err(map_err)?
21 == 0
22 {
23 return Err(std::io::Error::other(
24 "device does not support 'dumb buffers'",
25 ));
26 }
27
28 card.set_client_cap(ClientCap::UniversalPlanes, 1)
29 .map_err(map_err)?;
30 card.set_client_cap(ClientCap::Atomic, 1).map_err(map_err)?;
31
32 show_properties(&card).map_err(map_err)
33}
11fn main() -> std::io::Result<()> {
12 let mut card = Card::open(c"/dev/dri/card0").map_err(map_init_err)?;
13 card.become_master().map_err(map_err)?;
14
15 {
16 let name = card.driver_name().map_err(map_err)?;
17 let name = String::from_utf8_lossy(&name);
18 println!("Driver name: {name}");
19 }
20
21 if card
22 .get_device_cap(DeviceCap::DumbBuffer)
23 .map_err(map_err)?
24 == 0
25 {
26 return Err(std::io::Error::other(
27 "device does not support 'dumb buffers'",
28 ));
29 } else {
30 println!("Device supports 'dumb buffers'");
31 }
32
33 card.set_client_cap(ClientCap::UniversalPlanes, 1)
34 .map_err(map_err)?;
35 card.set_client_cap(ClientCap::Atomic, 1).map_err(map_err)?;
36
37 display_demo(&mut card).map_err(map_err)
38}
Sourcepub fn get_device_cap_raw(&self, capability: DrmCap) -> Result<u64, Error>
pub fn get_device_cap_raw(&self, capability: DrmCap) -> Result<u64, Error>
Read a device capability value using a raw capability number.
Sourcepub fn set_client_cap(
&mut self,
capability: ClientCap,
value: u64,
) -> Result<(), Error>
pub fn set_client_cap( &mut self, capability: ClientCap, value: u64, ) -> Result<(), Error>
Read a device capability value using a raw capability number.
Examples found in repository?
9fn main() -> std::io::Result<()> {
10 let mut card = Card::open(c"/dev/dri/card0").map_err(map_init_err)?;
11
12 {
13 let name = card.driver_name().map_err(map_err)?;
14 let name = String::from_utf8_lossy(&name);
15 println!("Driver name: {name}");
16 }
17
18 if card
19 .get_device_cap(DeviceCap::DumbBuffer)
20 .map_err(map_err)?
21 == 0
22 {
23 return Err(std::io::Error::other(
24 "device does not support 'dumb buffers'",
25 ));
26 }
27
28 card.set_client_cap(ClientCap::UniversalPlanes, 1)
29 .map_err(map_err)?;
30 card.set_client_cap(ClientCap::Atomic, 1).map_err(map_err)?;
31
32 show_properties(&card).map_err(map_err)
33}
More examples
11fn main() -> std::io::Result<()> {
12 let mut card = Card::open(c"/dev/dri/card0").map_err(map_init_err)?;
13 card.become_master().map_err(map_err)?;
14
15 {
16 let name = card.driver_name().map_err(map_err)?;
17 let name = String::from_utf8_lossy(&name);
18 println!("Driver name: {name}");
19 }
20
21 if card
22 .get_device_cap(DeviceCap::DumbBuffer)
23 .map_err(map_err)?
24 == 0
25 {
26 return Err(std::io::Error::other(
27 "device does not support 'dumb buffers'",
28 ));
29 } else {
30 println!("Device supports 'dumb buffers'");
31 }
32
33 card.set_client_cap(ClientCap::UniversalPlanes, 1)
34 .map_err(map_err)?;
35 card.set_client_cap(ClientCap::Atomic, 1).map_err(map_err)?;
36
37 display_demo(&mut card).map_err(map_err)
38}
Sourcepub fn set_client_cap_raw(
&mut self,
capability: DrmClientCap,
value: u64,
) -> Result<(), Error>
pub fn set_client_cap_raw( &mut self, capability: DrmClientCap, value: u64, ) -> Result<(), Error>
Attempt to set a client capability, which might then change the behavior of other device functions.
Sourcepub fn become_master(&mut self) -> Result<(), Error>
pub fn become_master(&mut self) -> Result<(), Error>
Attempt to become the “master” of this device, which is required for modesetting.
Examples found in repository?
11fn main() -> std::io::Result<()> {
12 let mut card = Card::open(c"/dev/dri/card0").map_err(map_init_err)?;
13 card.become_master().map_err(map_err)?;
14
15 {
16 let name = card.driver_name().map_err(map_err)?;
17 let name = String::from_utf8_lossy(&name);
18 println!("Driver name: {name}");
19 }
20
21 if card
22 .get_device_cap(DeviceCap::DumbBuffer)
23 .map_err(map_err)?
24 == 0
25 {
26 return Err(std::io::Error::other(
27 "device does not support 'dumb buffers'",
28 ));
29 } else {
30 println!("Device supports 'dumb buffers'");
31 }
32
33 display_demo(&mut card).map_err(map_err)
34}
More examples
11fn main() -> std::io::Result<()> {
12 let mut card = Card::open(c"/dev/dri/card0").map_err(map_init_err)?;
13 card.become_master().map_err(map_err)?;
14
15 {
16 let name = card.driver_name().map_err(map_err)?;
17 let name = String::from_utf8_lossy(&name);
18 println!("Driver name: {name}");
19 }
20
21 if card
22 .get_device_cap(DeviceCap::DumbBuffer)
23 .map_err(map_err)?
24 == 0
25 {
26 return Err(std::io::Error::other(
27 "device does not support 'dumb buffers'",
28 ));
29 } else {
30 println!("Device supports 'dumb buffers'");
31 }
32
33 card.set_client_cap(ClientCap::UniversalPlanes, 1)
34 .map_err(map_err)?;
35 card.set_client_cap(ClientCap::Atomic, 1).map_err(map_err)?;
36
37 display_demo(&mut card).map_err(map_err)
38}
Sourcepub fn drop_master(&mut self) -> Result<(), Error>
pub fn drop_master(&mut self) -> Result<(), Error>
Release the “master” status of this device, thus allowing other processes to claim it.
Sourcepub fn property_meta(
&self,
prop_id: PropertyId,
) -> Result<ObjectPropMeta<'_>, Error>
pub fn property_meta( &self, prop_id: PropertyId, ) -> Result<ObjectPropMeta<'_>, Error>
Get metadata about a DRM property using its id.
Property ids are assigned dynamically and so must be detected at runtime.
Examples found in repository?
144fn property_meta<'a, 'card>(
145 prop_id: PropertyId,
146 prop_meta: &'a mut HashMap<u32, PropertyMeta>,
147 card: &Card,
148) -> Result<&'a PropertyMeta, Error> {
149 Ok(prop_meta.entry(prop_id.0).or_insert_with(|| {
150 card.property_meta(prop_id)
151 .map(|meta| {
152 let mut enum_names = BTreeMap::new();
153 for member in meta.enum_members().unwrap() {
154 enum_names.insert(member.value(), member.name().to_string());
155 }
156 PropertyMeta {
157 name: meta.name().to_string(),
158 typ: meta.property_type(),
159 immutable: meta.is_immutable(),
160 values: meta.values().unwrap(),
161 enum_names,
162 }
163 })
164 .unwrap_or(PropertyMeta {
165 name: String::from("<unknown>"),
166 typ: PropertyType::Unknown,
167 immutable: true,
168 values: Vec::new(),
169 enum_names: BTreeMap::new(),
170 })
171 }))
172}
Sourcepub fn object_properties(
&self,
obj_id: impl Into<ObjectId>,
) -> Result<Vec<ModeProp>, Error>
pub fn object_properties( &self, obj_id: impl Into<ObjectId>, ) -> Result<Vec<ModeProp>, Error>
Get the properties of the specified object in their raw form.
Use either Self::property_meta
or Self::each_object_property_meta
to discover the name and type information for each property id.
Sourcepub fn each_object_property_meta(
&self,
obj_id: impl Into<ObjectId>,
f: impl FnMut(ObjectPropMeta<'_>, u64),
) -> Result<(), Error>
pub fn each_object_property_meta( &self, obj_id: impl Into<ObjectId>, f: impl FnMut(ObjectPropMeta<'_>, u64), ) -> Result<(), Error>
Call f
with the metadata for each property of the object with the given id.
This is intended for use by callers that want to build a lookup table of property ids for later use in efficiently setting those properties. Pass a closure that mutates the lookup table only for the subset of properties that are interesting.
Examples found in repository?
267 pub fn new(conn_id: ConnectorId, card: &linux_drm::Card) -> Result<Self, Error> {
268 let mut ret: Self = unsafe { core::mem::zeroed() };
269 card.each_object_property_meta(conn_id, |meta, _| ret.populate_from(meta))?;
270 Ok(ret)
271 }
272
273 pub fn populate_from<'card>(&mut self, from: linux_drm::modeset::ObjectPropMeta<'card>) {
274 match from.name() {
275 "CRTC_ID" => self.crtc_id = from.property_id(),
276 _ => {}
277 }
278 }
279}
280
281#[derive(Debug)]
282struct CrtcPropIds {
283 active: PropertyId,
284}
285
286impl CrtcPropIds {
287 pub fn new(crtc_id: CrtcId, card: &linux_drm::Card) -> Result<Self, Error> {
288 let mut ret: Self = unsafe { core::mem::zeroed() };
289 card.each_object_property_meta(linux_drm::modeset::ObjectId::Crtc(crtc_id), |meta, _| {
290 ret.populate_from(meta)
291 })?;
292 Ok(ret)
293 }
294
295 pub fn populate_from<'card>(&mut self, from: linux_drm::modeset::ObjectPropMeta<'card>) {
296 match from.name() {
297 "ACTIVE" => self.active = from.property_id(),
298 _ => {}
299 }
300 }
301}
302
303#[derive(Debug)]
304struct PlanePropIds {
305 typ: PropertyId,
306 fb_id: PropertyId,
307 crtc_id: PropertyId,
308 crtc_x: PropertyId,
309 crtc_y: PropertyId,
310 crtc_w: PropertyId,
311 crtc_h: PropertyId,
312 src_x: PropertyId,
313 src_y: PropertyId,
314 src_w: PropertyId,
315 src_h: PropertyId,
316}
317
318impl PlanePropIds {
319 pub fn new(plane_id: PlaneId, card: &linux_drm::Card) -> Result<Self, Error> {
320 let mut ret: Self = unsafe { core::mem::zeroed() };
321 card.each_object_property_meta(plane_id, |meta, _| ret.populate_from(meta))?;
322 Ok(ret)
323 }
Sourcepub fn resources(&self) -> Result<CardResources, Error>
pub fn resources(&self) -> Result<CardResources, Error>
Read information about the modesetting resources available for this device.
The result includes ids for the available connectors, encoders, CRTCs, planes, and framebuffers.
Examples found in repository?
87fn prepare_outputs(card: &Card) -> Result<Vec<Output>, Error> {
88 let resources = card.resources()?;
89 let mut outputs = Vec::<Output>::new();
90
91 for id in resources.connector_ids.iter().copied() {
92 let conn = card.connector_state(id)?;
93 if conn.connection_state != ConnectionState::Connected {
94 println!("ignoring unconnected connector {id:?}");
95 continue;
96 }
97 if conn.current_encoder_id.0 == 0 {
98 println!("ignoring encoderless connector {id:?}");
99 continue;
100 }
101 if conn.modes.len() == 0 {
102 println!("ignoring modeless connector {id:?}");
103 continue;
104 }
105
106 let output = prepare_output(card, conn, &resources)?;
107 outputs.push(output);
108 }
109
110 Ok(outputs)
111}
More examples
159fn prepare_outputs(card: &Card) -> Result<Vec<Output>, Error> {
160 println!("preparing outputs");
161
162 let resources = card.resources()?;
163 let mut outputs = Vec::<Output>::new();
164
165 for id in resources.connector_ids.iter().copied() {
166 println!("preparing output for connector #{id:?}");
167
168 let conn = card.connector_state(id)?;
169 if conn.connection_state != ConnectionState::Connected {
170 println!("ignoring unconnected connector {id:?}");
171 continue;
172 }
173 if conn.current_encoder_id.0 == 0 {
174 println!("ignoring encoderless connector {id:?}");
175 continue;
176 }
177 if conn.modes.len() == 0 {
178 println!("ignoring modeless connector {id:?}");
179 continue;
180 }
181
182 let output = prepare_output(card, conn, &resources)?;
183 outputs.push(output);
184 }
185
186 Ok(outputs)
187}
35fn show_properties(card: &Card) -> Result<(), Error> {
36 let res = card.resources()?;
37
38 let mut prop_meta = HashMap::<u32, PropertyMeta>::new();
39
40 println!("");
41
42 for conn_id in res.connector_ids {
43 println!("Connector #{conn_id:?}:");
44 if let Err(e) = show_object_property_list(conn_id, &mut prop_meta, card) {
45 println!(" Error: {e:?}");
46 }
47 println!("");
48 }
49
50 for enc_id in res.encoder_ids {
51 println!("Encoder #{enc_id:?}:");
52 if let Err(e) = show_object_property_list(enc_id, &mut prop_meta, card) {
53 println!(" Error: {e:?}");
54 }
55 println!("");
56 }
57
58 for crtc_id in res.crtc_ids {
59 println!("CRTC #{crtc_id:?}:");
60 if let Err(e) = show_object_property_list(crtc_id, &mut prop_meta, card) {
61 println!(" Error: {e:?}");
62 }
63 println!("");
64 }
65
66 for fb_id in res.fb_ids {
67 println!("Framebuffer #{fb_id:?}:");
68 if let Err(e) = show_object_property_list(fb_id, &mut prop_meta, card) {
69 println!(" Error: {e:?}");
70 }
71 println!("");
72 }
73
74 for plane_id in res.plane_ids {
75 println!("Plane #{plane_id:?}:");
76 if let Err(e) = show_object_property_list(plane_id, &mut prop_meta, card) {
77 println!(" Error: {e:?}");
78 }
79 println!("");
80 }
81
82 Ok(())
83}
Sourcepub fn connector_state(
&self,
connector_id: ConnectorId,
) -> Result<ConnectorState, Error>
pub fn connector_state( &self, connector_id: ConnectorId, ) -> Result<ConnectorState, Error>
Read current state information for the connector with the given id.
Examples found in repository?
36fn display_demo(card: &mut Card) -> Result<(), Error> {
37 let mut outputs = prepare_outputs(&card)?;
38 for output in &mut outputs {
39 println!("preparing output {output:#?}");
40 let conn = card.connector_state(output.conn_id)?;
41
42 let mode = &output.mode;
43 let mode_name = String::from_utf8_lossy(&mode.name);
44 println!(
45 "{:?} connector uses {mode_name} ({}x{}@{}Hz)",
46 conn.connector_type, mode.hdisplay, mode.vdisplay, mode.vrefresh,
47 );
48
49 let rows = output.db.height() as usize;
50 let pitch = output.db.pitch() as usize;
51 let data = output.db.buffer_mut();
52 for i in 0..rows {
53 if (i % 8) > 3 {
54 let row = &mut data[(i * pitch)..(i * pitch) + pitch];
55 row.fill(0xff);
56 }
57 }
58
59 println!(
60 "configuring CRTC {:?} for framebuffer {:?} and mode {mode_name} on connection {:?}",
61 output.crtc_id,
62 output.db.framebuffer_id(),
63 conn.id
64 );
65 card.set_crtc_dumb_buffer(output.crtc_id, &output.db, mode, &[output.conn_id])?;
66 card.crtc_page_flip_dumb_buffer(output.crtc_id, &output.db, PageFlipFlags::EVENT)?;
67 }
68
69 let mut evt_buf = vec![0_u8; 1024];
70 loop {
71 println!("waiting for events (send SIGINT to exit)");
72 for evt in card.read_events(&mut evt_buf)? {
73 println!("event {evt:?}");
74 match evt {
75 DrmEvent::Generic(GenericDrmEvent::FlipComplete(_)) => {
76 // In a real program this would be a time place to draw the next frame
77 // for the reported crtc.
78 }
79 _ => {
80 // Ignore any unrecognized event types.
81 }
82 }
83 }
84 }
85}
86
87fn prepare_outputs(card: &Card) -> Result<Vec<Output>, Error> {
88 let resources = card.resources()?;
89 let mut outputs = Vec::<Output>::new();
90
91 for id in resources.connector_ids.iter().copied() {
92 let conn = card.connector_state(id)?;
93 if conn.connection_state != ConnectionState::Connected {
94 println!("ignoring unconnected connector {id:?}");
95 continue;
96 }
97 if conn.current_encoder_id.0 == 0 {
98 println!("ignoring encoderless connector {id:?}");
99 continue;
100 }
101 if conn.modes.len() == 0 {
102 println!("ignoring modeless connector {id:?}");
103 continue;
104 }
105
106 let output = prepare_output(card, conn, &resources)?;
107 outputs.push(output);
108 }
109
110 Ok(outputs)
111}
More examples
40fn display_demo(card: &mut Card) -> Result<(), Error> {
41 let mut outputs = prepare_outputs(&card)?;
42 let mut req = linux_drm::modeset::AtomicRequest::new();
43
44 for output in &mut outputs {
45 println!("preparing output {output:#?}");
46 let conn = card.connector_state(output.conn_id)?;
47
48 let mode = &output.mode;
49 let mode_name = String::from_utf8_lossy(&mode.name);
50 println!(
51 "{:?} connector uses {mode_name} ({}x{}@{}Hz)",
52 conn.connector_type, mode.hdisplay, mode.vdisplay, mode.vrefresh,
53 );
54
55 let rows = output.db.height() as usize;
56 let pitch = output.db.pitch() as usize;
57 let data = output.db.buffer_mut();
58 for i in 0..rows {
59 if (i % 8) > 3 {
60 let row = &mut data[(i * pitch)..(i * pitch) + pitch];
61 row.fill(0xff);
62 }
63 }
64
65 println!(
66 "configuring CRTC {:?} for framebuffer {:?} and mode {mode_name} on connector {:?}",
67 output.crtc_id,
68 output.db.framebuffer_id(),
69 conn.id
70 );
71
72 req.set_property(
73 ObjectId::Connector(output.conn_id),
74 output.conn_prop_ids.crtc_id,
75 output.crtc_id,
76 );
77 req.set_property(
78 ObjectId::Crtc(output.crtc_id),
79 output.crtc_prop_ids.active,
80 true,
81 );
82 req.set_property(
83 ObjectId::Plane(output.plane_id),
84 output.plane_prop_ids.fb_id,
85 output.db.framebuffer_id(),
86 );
87 req.set_property(
88 ObjectId::Plane(output.plane_id),
89 output.plane_prop_ids.crtc_id,
90 output.crtc_id,
91 );
92 req.set_property(
93 ObjectId::Plane(output.plane_id),
94 output.plane_prop_ids.crtc_x,
95 0,
96 );
97 req.set_property(
98 ObjectId::Plane(output.plane_id),
99 output.plane_prop_ids.crtc_y,
100 0,
101 );
102 req.set_property(
103 ObjectId::Plane(output.plane_id),
104 output.plane_prop_ids.crtc_w,
105 output.db.width(),
106 );
107 req.set_property(
108 ObjectId::Plane(output.plane_id),
109 output.plane_prop_ids.crtc_h,
110 output.db.height(),
111 );
112 req.set_property(
113 ObjectId::Plane(output.plane_id),
114 output.plane_prop_ids.src_x,
115 0,
116 );
117 req.set_property(
118 ObjectId::Plane(output.plane_id),
119 output.plane_prop_ids.src_y,
120 0,
121 );
122 req.set_property(
123 ObjectId::Plane(output.plane_id),
124 output.plane_prop_ids.src_w,
125 (output.db.width() as u64) << 16,
126 );
127 req.set_property(
128 ObjectId::Plane(output.plane_id),
129 output.plane_prop_ids.src_h,
130 (output.db.height() as u64) << 16,
131 );
132 }
133
134 println!("atomic commit {req:#?}");
135 card.atomic_commit(
136 &req,
137 AtomicCommitFlags::ALLOW_MODESET | AtomicCommitFlags::PAGE_FLIP_EVENT,
138 0,
139 )?;
140
141 let mut evt_buf = vec![0_u8; 1024];
142 loop {
143 println!("waiting for events (send SIGINT to exit)");
144 for evt in card.read_events(&mut evt_buf)? {
145 println!("event {evt:?}");
146 match evt {
147 DrmEvent::Generic(GenericDrmEvent::FlipComplete(_)) => {
148 // In a real program this would be a time place to draw the next frame
149 // for the reported crtc.
150 }
151 _ => {
152 // Ignore any unrecognized event types.
153 }
154 }
155 }
156 }
157}
158
159fn prepare_outputs(card: &Card) -> Result<Vec<Output>, Error> {
160 println!("preparing outputs");
161
162 let resources = card.resources()?;
163 let mut outputs = Vec::<Output>::new();
164
165 for id in resources.connector_ids.iter().copied() {
166 println!("preparing output for connector #{id:?}");
167
168 let conn = card.connector_state(id)?;
169 if conn.connection_state != ConnectionState::Connected {
170 println!("ignoring unconnected connector {id:?}");
171 continue;
172 }
173 if conn.current_encoder_id.0 == 0 {
174 println!("ignoring encoderless connector {id:?}");
175 continue;
176 }
177 if conn.modes.len() == 0 {
178 println!("ignoring modeless connector {id:?}");
179 continue;
180 }
181
182 let output = prepare_output(card, conn, &resources)?;
183 outputs.push(output);
184 }
185
186 Ok(outputs)
187}
Sourcepub fn encoder_state(
&self,
encoder_id: EncoderId,
) -> Result<EncoderState, Error>
pub fn encoder_state( &self, encoder_id: EncoderId, ) -> Result<EncoderState, Error>
Read current state information for the encoder with the given id.
Examples found in repository?
113fn prepare_output(
114 card: &Card,
115 conn: ConnectorState,
116 resources: &CardResources,
117) -> Result<Output, Error> {
118 if conn.current_encoder_id.0 == 0 {
119 // It could be reasonable to go hunting for a suitable encoder and
120 // CRTC to activate this connector, but for this simple example
121 // we'll just use whatever connectors are already producing some
122 // output and keep using whatever modes they are currently in.
123 return Err(Error::NotSupported);
124 }
125 let _ = resources; // (don't actually need this when we're just using the already-selected encoder/crtc)
126
127 let enc = card.encoder_state(conn.current_encoder_id)?;
128 let crtc_id = enc.current_crtc_id;
129 let crtc = card.crtc_state(crtc_id)?;
130 let mode = crtc.mode;
131 let db = card.create_dumb_buffer(DumbBufferRequest {
132 width: mode.hdisplay as u32,
133 height: mode.vdisplay as u32,
134 depth: 24,
135 bpp: 32,
136 })?;
137 Ok(Output {
138 conn_id: conn.id,
139 crtc_id,
140 mode,
141 db,
142 })
143}
More examples
189fn prepare_output(
190 card: &Card,
191 conn: ConnectorState,
192 resources: &CardResources,
193) -> Result<Output, Error> {
194 if conn.current_encoder_id.0 == 0 {
195 // It could be reasonable to go hunting for a suitable encoder and
196 // CRTC to activate this connector, but for this simple example
197 // we'll just use whatever connectors are already producing some
198 // output and keep using whatever modes they are currently in.
199 return Err(Error::NotSupported);
200 }
201 let _ = resources; // (don't actually need this when we're just using the already-selected encoder/crtc)
202
203 let enc = card.encoder_state(conn.current_encoder_id)?;
204 let crtc_id = enc.current_crtc_id;
205 let crtc = card.crtc_state(crtc_id)?;
206 let mode = crtc.mode;
207 let db = card.create_dumb_buffer(DumbBufferRequest {
208 width: mode.hdisplay as u32,
209 height: mode.vdisplay as u32,
210 depth: 24,
211 bpp: 32,
212 })?;
213
214 // We need to find the primary plane that's currently assigned to this CRTC.
215 // The following is not really a correct way to do it, but it'll work for
216 // now just to test if anything is working here at all. (This makes some
217 // assumptions about how the card is already configured which might not
218 // actually hold in practice.)
219 let mut chosen_plane_id: Option<PlaneId> = None;
220 for plane_id in resources.plane_ids.iter().copied() {
221 let plane = card.plane_state(plane_id)?;
222 if plane.crtc_id == crtc_id {
223 chosen_plane_id = Some(plane_id);
224 break;
225 }
226 }
227 let Some(chosen_plane_id) = chosen_plane_id else {
228 return Err(Error::NonExist);
229 };
230
231 println!("collecting properties");
232 let conn_prop_ids = ConnectorPropIds::new(conn.id, card)?;
233 let crtc_prop_ids = CrtcPropIds::new(crtc_id, card)?;
234 let plane_prop_ids = PlanePropIds::new(chosen_plane_id, card)?;
235
236 println!("collected properties");
237 Ok(Output {
238 conn_id: conn.id,
239 conn_prop_ids,
240 crtc_id,
241 crtc_prop_ids,
242 plane_id: chosen_plane_id,
243 plane_prop_ids,
244 mode,
245 db,
246 })
247}
Sourcepub fn crtc_state(&self, crtc_id: CrtcId) -> Result<CrtcState, Error>
pub fn crtc_state(&self, crtc_id: CrtcId) -> Result<CrtcState, Error>
Read current state information for the CRTC with the given id.
Examples found in repository?
113fn prepare_output(
114 card: &Card,
115 conn: ConnectorState,
116 resources: &CardResources,
117) -> Result<Output, Error> {
118 if conn.current_encoder_id.0 == 0 {
119 // It could be reasonable to go hunting for a suitable encoder and
120 // CRTC to activate this connector, but for this simple example
121 // we'll just use whatever connectors are already producing some
122 // output and keep using whatever modes they are currently in.
123 return Err(Error::NotSupported);
124 }
125 let _ = resources; // (don't actually need this when we're just using the already-selected encoder/crtc)
126
127 let enc = card.encoder_state(conn.current_encoder_id)?;
128 let crtc_id = enc.current_crtc_id;
129 let crtc = card.crtc_state(crtc_id)?;
130 let mode = crtc.mode;
131 let db = card.create_dumb_buffer(DumbBufferRequest {
132 width: mode.hdisplay as u32,
133 height: mode.vdisplay as u32,
134 depth: 24,
135 bpp: 32,
136 })?;
137 Ok(Output {
138 conn_id: conn.id,
139 crtc_id,
140 mode,
141 db,
142 })
143}
More examples
189fn prepare_output(
190 card: &Card,
191 conn: ConnectorState,
192 resources: &CardResources,
193) -> Result<Output, Error> {
194 if conn.current_encoder_id.0 == 0 {
195 // It could be reasonable to go hunting for a suitable encoder and
196 // CRTC to activate this connector, but for this simple example
197 // we'll just use whatever connectors are already producing some
198 // output and keep using whatever modes they are currently in.
199 return Err(Error::NotSupported);
200 }
201 let _ = resources; // (don't actually need this when we're just using the already-selected encoder/crtc)
202
203 let enc = card.encoder_state(conn.current_encoder_id)?;
204 let crtc_id = enc.current_crtc_id;
205 let crtc = card.crtc_state(crtc_id)?;
206 let mode = crtc.mode;
207 let db = card.create_dumb_buffer(DumbBufferRequest {
208 width: mode.hdisplay as u32,
209 height: mode.vdisplay as u32,
210 depth: 24,
211 bpp: 32,
212 })?;
213
214 // We need to find the primary plane that's currently assigned to this CRTC.
215 // The following is not really a correct way to do it, but it'll work for
216 // now just to test if anything is working here at all. (This makes some
217 // assumptions about how the card is already configured which might not
218 // actually hold in practice.)
219 let mut chosen_plane_id: Option<PlaneId> = None;
220 for plane_id in resources.plane_ids.iter().copied() {
221 let plane = card.plane_state(plane_id)?;
222 if plane.crtc_id == crtc_id {
223 chosen_plane_id = Some(plane_id);
224 break;
225 }
226 }
227 let Some(chosen_plane_id) = chosen_plane_id else {
228 return Err(Error::NonExist);
229 };
230
231 println!("collecting properties");
232 let conn_prop_ids = ConnectorPropIds::new(conn.id, card)?;
233 let crtc_prop_ids = CrtcPropIds::new(crtc_id, card)?;
234 let plane_prop_ids = PlanePropIds::new(chosen_plane_id, card)?;
235
236 println!("collected properties");
237 Ok(Output {
238 conn_id: conn.id,
239 conn_prop_ids,
240 crtc_id,
241 crtc_prop_ids,
242 plane_id: chosen_plane_id,
243 plane_prop_ids,
244 mode,
245 db,
246 })
247}
Sourcepub fn plane_state(&self, plane_id: PlaneId) -> Result<PlaneState, Error>
pub fn plane_state(&self, plane_id: PlaneId) -> Result<PlaneState, Error>
Read current state information for the plane with the given id.
Examples found in repository?
189fn prepare_output(
190 card: &Card,
191 conn: ConnectorState,
192 resources: &CardResources,
193) -> Result<Output, Error> {
194 if conn.current_encoder_id.0 == 0 {
195 // It could be reasonable to go hunting for a suitable encoder and
196 // CRTC to activate this connector, but for this simple example
197 // we'll just use whatever connectors are already producing some
198 // output and keep using whatever modes they are currently in.
199 return Err(Error::NotSupported);
200 }
201 let _ = resources; // (don't actually need this when we're just using the already-selected encoder/crtc)
202
203 let enc = card.encoder_state(conn.current_encoder_id)?;
204 let crtc_id = enc.current_crtc_id;
205 let crtc = card.crtc_state(crtc_id)?;
206 let mode = crtc.mode;
207 let db = card.create_dumb_buffer(DumbBufferRequest {
208 width: mode.hdisplay as u32,
209 height: mode.vdisplay as u32,
210 depth: 24,
211 bpp: 32,
212 })?;
213
214 // We need to find the primary plane that's currently assigned to this CRTC.
215 // The following is not really a correct way to do it, but it'll work for
216 // now just to test if anything is working here at all. (This makes some
217 // assumptions about how the card is already configured which might not
218 // actually hold in practice.)
219 let mut chosen_plane_id: Option<PlaneId> = None;
220 for plane_id in resources.plane_ids.iter().copied() {
221 let plane = card.plane_state(plane_id)?;
222 if plane.crtc_id == crtc_id {
223 chosen_plane_id = Some(plane_id);
224 break;
225 }
226 }
227 let Some(chosen_plane_id) = chosen_plane_id else {
228 return Err(Error::NonExist);
229 };
230
231 println!("collecting properties");
232 let conn_prop_ids = ConnectorPropIds::new(conn.id, card)?;
233 let crtc_prop_ids = CrtcPropIds::new(crtc_id, card)?;
234 let plane_prop_ids = PlanePropIds::new(chosen_plane_id, card)?;
235
236 println!("collected properties");
237 Ok(Output {
238 conn_id: conn.id,
239 conn_prop_ids,
240 crtc_id,
241 crtc_prop_ids,
242 plane_id: chosen_plane_id,
243 plane_prop_ids,
244 mode,
245 db,
246 })
247}
Sourcepub fn atomic_commit(
&mut self,
req: &AtomicRequest,
flags: AtomicCommitFlags,
user_data: u64,
) -> Result<(), Error>
pub fn atomic_commit( &mut self, req: &AtomicRequest, flags: AtomicCommitFlags, user_data: u64, ) -> Result<(), Error>
Attempt to commit an atomic modesetting request.
Callers which intend to perform frequent modesetting, such as modesetting on
every frame for double buffering, are encouraged to retain their
modeset::AtomicRequest
object and reset it to use again on a subsequent
request if that request will involve a similar set of objects and properties,
to minimize the need for reallocating the backing storage for the request
on every frame.
Examples found in repository?
40fn display_demo(card: &mut Card) -> Result<(), Error> {
41 let mut outputs = prepare_outputs(&card)?;
42 let mut req = linux_drm::modeset::AtomicRequest::new();
43
44 for output in &mut outputs {
45 println!("preparing output {output:#?}");
46 let conn = card.connector_state(output.conn_id)?;
47
48 let mode = &output.mode;
49 let mode_name = String::from_utf8_lossy(&mode.name);
50 println!(
51 "{:?} connector uses {mode_name} ({}x{}@{}Hz)",
52 conn.connector_type, mode.hdisplay, mode.vdisplay, mode.vrefresh,
53 );
54
55 let rows = output.db.height() as usize;
56 let pitch = output.db.pitch() as usize;
57 let data = output.db.buffer_mut();
58 for i in 0..rows {
59 if (i % 8) > 3 {
60 let row = &mut data[(i * pitch)..(i * pitch) + pitch];
61 row.fill(0xff);
62 }
63 }
64
65 println!(
66 "configuring CRTC {:?} for framebuffer {:?} and mode {mode_name} on connector {:?}",
67 output.crtc_id,
68 output.db.framebuffer_id(),
69 conn.id
70 );
71
72 req.set_property(
73 ObjectId::Connector(output.conn_id),
74 output.conn_prop_ids.crtc_id,
75 output.crtc_id,
76 );
77 req.set_property(
78 ObjectId::Crtc(output.crtc_id),
79 output.crtc_prop_ids.active,
80 true,
81 );
82 req.set_property(
83 ObjectId::Plane(output.plane_id),
84 output.plane_prop_ids.fb_id,
85 output.db.framebuffer_id(),
86 );
87 req.set_property(
88 ObjectId::Plane(output.plane_id),
89 output.plane_prop_ids.crtc_id,
90 output.crtc_id,
91 );
92 req.set_property(
93 ObjectId::Plane(output.plane_id),
94 output.plane_prop_ids.crtc_x,
95 0,
96 );
97 req.set_property(
98 ObjectId::Plane(output.plane_id),
99 output.plane_prop_ids.crtc_y,
100 0,
101 );
102 req.set_property(
103 ObjectId::Plane(output.plane_id),
104 output.plane_prop_ids.crtc_w,
105 output.db.width(),
106 );
107 req.set_property(
108 ObjectId::Plane(output.plane_id),
109 output.plane_prop_ids.crtc_h,
110 output.db.height(),
111 );
112 req.set_property(
113 ObjectId::Plane(output.plane_id),
114 output.plane_prop_ids.src_x,
115 0,
116 );
117 req.set_property(
118 ObjectId::Plane(output.plane_id),
119 output.plane_prop_ids.src_y,
120 0,
121 );
122 req.set_property(
123 ObjectId::Plane(output.plane_id),
124 output.plane_prop_ids.src_w,
125 (output.db.width() as u64) << 16,
126 );
127 req.set_property(
128 ObjectId::Plane(output.plane_id),
129 output.plane_prop_ids.src_h,
130 (output.db.height() as u64) << 16,
131 );
132 }
133
134 println!("atomic commit {req:#?}");
135 card.atomic_commit(
136 &req,
137 AtomicCommitFlags::ALLOW_MODESET | AtomicCommitFlags::PAGE_FLIP_EVENT,
138 0,
139 )?;
140
141 let mut evt_buf = vec![0_u8; 1024];
142 loop {
143 println!("waiting for events (send SIGINT to exit)");
144 for evt in card.read_events(&mut evt_buf)? {
145 println!("event {evt:?}");
146 match evt {
147 DrmEvent::Generic(GenericDrmEvent::FlipComplete(_)) => {
148 // In a real program this would be a time place to draw the next frame
149 // for the reported crtc.
150 }
151 _ => {
152 // Ignore any unrecognized event types.
153 }
154 }
155 }
156 }
157}
Sourcepub fn new_property_blob<'card, 'content>(
&'card self,
content: &'content [u8],
) -> Result<BlobHandle, Error>
pub fn new_property_blob<'card, 'content>( &'card self, content: &'content [u8], ) -> Result<BlobHandle, Error>
Send the given content to the kernel as a property blob, ready to use for assignment to a blob-typed object property.
The returned modeset::BlobHandle
must remain live long enough to
be assigned to the target property. The blob object in the kernel
will be destroyed when the blob handle is dropped.
Sourcepub fn reset_crtc(&mut self, crtc_id: u32) -> Result<CrtcState, Error>
pub fn reset_crtc(&mut self, crtc_id: u32) -> Result<CrtcState, Error>
Reset the given CRTC to its default (zeroed) settings.
Sourcepub fn set_crtc_dumb_buffer(
&mut self,
crtc_id: CrtcId,
buf: &DumbBuffer,
mode: &ModeInfo,
conn_ids: &[ConnectorId],
) -> Result<CrtcState, Error>
pub fn set_crtc_dumb_buffer( &mut self, crtc_id: CrtcId, buf: &DumbBuffer, mode: &ModeInfo, conn_ids: &[ConnectorId], ) -> Result<CrtcState, Error>
Set the given CRTC to display the image from the given “dumb buffer”, used for software rendering.
Examples found in repository?
36fn display_demo(card: &mut Card) -> Result<(), Error> {
37 let mut outputs = prepare_outputs(&card)?;
38 for output in &mut outputs {
39 println!("preparing output {output:#?}");
40 let conn = card.connector_state(output.conn_id)?;
41
42 let mode = &output.mode;
43 let mode_name = String::from_utf8_lossy(&mode.name);
44 println!(
45 "{:?} connector uses {mode_name} ({}x{}@{}Hz)",
46 conn.connector_type, mode.hdisplay, mode.vdisplay, mode.vrefresh,
47 );
48
49 let rows = output.db.height() as usize;
50 let pitch = output.db.pitch() as usize;
51 let data = output.db.buffer_mut();
52 for i in 0..rows {
53 if (i % 8) > 3 {
54 let row = &mut data[(i * pitch)..(i * pitch) + pitch];
55 row.fill(0xff);
56 }
57 }
58
59 println!(
60 "configuring CRTC {:?} for framebuffer {:?} and mode {mode_name} on connection {:?}",
61 output.crtc_id,
62 output.db.framebuffer_id(),
63 conn.id
64 );
65 card.set_crtc_dumb_buffer(output.crtc_id, &output.db, mode, &[output.conn_id])?;
66 card.crtc_page_flip_dumb_buffer(output.crtc_id, &output.db, PageFlipFlags::EVENT)?;
67 }
68
69 let mut evt_buf = vec![0_u8; 1024];
70 loop {
71 println!("waiting for events (send SIGINT to exit)");
72 for evt in card.read_events(&mut evt_buf)? {
73 println!("event {evt:?}");
74 match evt {
75 DrmEvent::Generic(GenericDrmEvent::FlipComplete(_)) => {
76 // In a real program this would be a time place to draw the next frame
77 // for the reported crtc.
78 }
79 _ => {
80 // Ignore any unrecognized event types.
81 }
82 }
83 }
84 }
85}
Sourcepub fn crtc_page_flip_dumb_buffer(
&mut self,
crtd_id: CrtcId,
buf: &DumbBuffer,
flags: PageFlipFlags,
) -> Result<(), Error>
pub fn crtc_page_flip_dumb_buffer( &mut self, crtd_id: CrtcId, buf: &DumbBuffer, flags: PageFlipFlags, ) -> Result<(), Error>
Use a page-flipping request to change the given CRTC to display the image from the given “dumb buffer”.
Examples found in repository?
36fn display_demo(card: &mut Card) -> Result<(), Error> {
37 let mut outputs = prepare_outputs(&card)?;
38 for output in &mut outputs {
39 println!("preparing output {output:#?}");
40 let conn = card.connector_state(output.conn_id)?;
41
42 let mode = &output.mode;
43 let mode_name = String::from_utf8_lossy(&mode.name);
44 println!(
45 "{:?} connector uses {mode_name} ({}x{}@{}Hz)",
46 conn.connector_type, mode.hdisplay, mode.vdisplay, mode.vrefresh,
47 );
48
49 let rows = output.db.height() as usize;
50 let pitch = output.db.pitch() as usize;
51 let data = output.db.buffer_mut();
52 for i in 0..rows {
53 if (i % 8) > 3 {
54 let row = &mut data[(i * pitch)..(i * pitch) + pitch];
55 row.fill(0xff);
56 }
57 }
58
59 println!(
60 "configuring CRTC {:?} for framebuffer {:?} and mode {mode_name} on connection {:?}",
61 output.crtc_id,
62 output.db.framebuffer_id(),
63 conn.id
64 );
65 card.set_crtc_dumb_buffer(output.crtc_id, &output.db, mode, &[output.conn_id])?;
66 card.crtc_page_flip_dumb_buffer(output.crtc_id, &output.db, PageFlipFlags::EVENT)?;
67 }
68
69 let mut evt_buf = vec![0_u8; 1024];
70 loop {
71 println!("waiting for events (send SIGINT to exit)");
72 for evt in card.read_events(&mut evt_buf)? {
73 println!("event {evt:?}");
74 match evt {
75 DrmEvent::Generic(GenericDrmEvent::FlipComplete(_)) => {
76 // In a real program this would be a time place to draw the next frame
77 // for the reported crtc.
78 }
79 _ => {
80 // Ignore any unrecognized event types.
81 }
82 }
83 }
84 }
85}
Sourcepub fn create_dumb_buffer(
&self,
req: DumbBufferRequest,
) -> Result<DumbBuffer, Error>
pub fn create_dumb_buffer( &self, req: DumbBufferRequest, ) -> Result<DumbBuffer, Error>
Create a new “dumb buffer” that can be used for portable (hardware-agnostic) software rendering.
Examples found in repository?
113fn prepare_output(
114 card: &Card,
115 conn: ConnectorState,
116 resources: &CardResources,
117) -> Result<Output, Error> {
118 if conn.current_encoder_id.0 == 0 {
119 // It could be reasonable to go hunting for a suitable encoder and
120 // CRTC to activate this connector, but for this simple example
121 // we'll just use whatever connectors are already producing some
122 // output and keep using whatever modes they are currently in.
123 return Err(Error::NotSupported);
124 }
125 let _ = resources; // (don't actually need this when we're just using the already-selected encoder/crtc)
126
127 let enc = card.encoder_state(conn.current_encoder_id)?;
128 let crtc_id = enc.current_crtc_id;
129 let crtc = card.crtc_state(crtc_id)?;
130 let mode = crtc.mode;
131 let db = card.create_dumb_buffer(DumbBufferRequest {
132 width: mode.hdisplay as u32,
133 height: mode.vdisplay as u32,
134 depth: 24,
135 bpp: 32,
136 })?;
137 Ok(Output {
138 conn_id: conn.id,
139 crtc_id,
140 mode,
141 db,
142 })
143}
More examples
189fn prepare_output(
190 card: &Card,
191 conn: ConnectorState,
192 resources: &CardResources,
193) -> Result<Output, Error> {
194 if conn.current_encoder_id.0 == 0 {
195 // It could be reasonable to go hunting for a suitable encoder and
196 // CRTC to activate this connector, but for this simple example
197 // we'll just use whatever connectors are already producing some
198 // output and keep using whatever modes they are currently in.
199 return Err(Error::NotSupported);
200 }
201 let _ = resources; // (don't actually need this when we're just using the already-selected encoder/crtc)
202
203 let enc = card.encoder_state(conn.current_encoder_id)?;
204 let crtc_id = enc.current_crtc_id;
205 let crtc = card.crtc_state(crtc_id)?;
206 let mode = crtc.mode;
207 let db = card.create_dumb_buffer(DumbBufferRequest {
208 width: mode.hdisplay as u32,
209 height: mode.vdisplay as u32,
210 depth: 24,
211 bpp: 32,
212 })?;
213
214 // We need to find the primary plane that's currently assigned to this CRTC.
215 // The following is not really a correct way to do it, but it'll work for
216 // now just to test if anything is working here at all. (This makes some
217 // assumptions about how the card is already configured which might not
218 // actually hold in practice.)
219 let mut chosen_plane_id: Option<PlaneId> = None;
220 for plane_id in resources.plane_ids.iter().copied() {
221 let plane = card.plane_state(plane_id)?;
222 if plane.crtc_id == crtc_id {
223 chosen_plane_id = Some(plane_id);
224 break;
225 }
226 }
227 let Some(chosen_plane_id) = chosen_plane_id else {
228 return Err(Error::NonExist);
229 };
230
231 println!("collecting properties");
232 let conn_prop_ids = ConnectorPropIds::new(conn.id, card)?;
233 let crtc_prop_ids = CrtcPropIds::new(crtc_id, card)?;
234 let plane_prop_ids = PlanePropIds::new(chosen_plane_id, card)?;
235
236 println!("collected properties");
237 Ok(Output {
238 conn_id: conn.id,
239 conn_prop_ids,
240 crtc_id,
241 crtc_prop_ids,
242 plane_id: chosen_plane_id,
243 plane_prop_ids,
244 mode,
245 db,
246 })
247}
Sourcepub fn read_events_raw<'a>(
&self,
buf: &'a mut [u8],
) -> Result<impl Iterator<Item = &'a DrmEvent> + 'a, Error>
pub fn read_events_raw<'a>( &self, buf: &'a mut [u8], ) -> Result<impl Iterator<Item = &'a DrmEvent> + 'a, Error>
Read raw events from the card’s file descriptor.
DRM deals with events by having clients read from the card file descriptor, at which point the driver writes as many whole pending events as will fit into the given buffer. To give callers control over the buffer size, this function takes a preallocated mutable buffer to use for the temporary storage and then interprets the data one event at a time as the resulting iterator is used. The buffer should be at least large enough to contain one instance of the largest event type the kernel might return.
If this function returns successfully then the caller must read the
resulting iterator until it produces None
, or otherwise any unread events
will be lost.
All objects returned from the iterator are views into portions of the provided buffer.
Sourcepub fn read_events<'a>(
&self,
buf: &'a mut [u8],
) -> Result<impl Iterator<Item = DrmEvent> + 'a, Error>
pub fn read_events<'a>( &self, buf: &'a mut [u8], ) -> Result<impl Iterator<Item = DrmEvent> + 'a, Error>
Read events from the card’s file descriptor.
If this function returns successfully then the caller must read the
resulting iterator until it produces None
, or otherwise any unread
events will be lost.
This uses buf
in the same way as Self::read_events_raw
, but
instead of returning direct references to parts of the buffer it
copies the event data into owned objects that can therefore outlive
the buffer. This is really just a convenience wrapper around
passing the Self::read_events_raw
results through
event::DrmEvent::from_raw
.
Unlike Self::read_events_raw
, this function’s iterator will
sometimes perform dynamic allocations to capture the bodies of
events with unrecognized types.
Examples found in repository?
36fn display_demo(card: &mut Card) -> Result<(), Error> {
37 let mut outputs = prepare_outputs(&card)?;
38 for output in &mut outputs {
39 println!("preparing output {output:#?}");
40 let conn = card.connector_state(output.conn_id)?;
41
42 let mode = &output.mode;
43 let mode_name = String::from_utf8_lossy(&mode.name);
44 println!(
45 "{:?} connector uses {mode_name} ({}x{}@{}Hz)",
46 conn.connector_type, mode.hdisplay, mode.vdisplay, mode.vrefresh,
47 );
48
49 let rows = output.db.height() as usize;
50 let pitch = output.db.pitch() as usize;
51 let data = output.db.buffer_mut();
52 for i in 0..rows {
53 if (i % 8) > 3 {
54 let row = &mut data[(i * pitch)..(i * pitch) + pitch];
55 row.fill(0xff);
56 }
57 }
58
59 println!(
60 "configuring CRTC {:?} for framebuffer {:?} and mode {mode_name} on connection {:?}",
61 output.crtc_id,
62 output.db.framebuffer_id(),
63 conn.id
64 );
65 card.set_crtc_dumb_buffer(output.crtc_id, &output.db, mode, &[output.conn_id])?;
66 card.crtc_page_flip_dumb_buffer(output.crtc_id, &output.db, PageFlipFlags::EVENT)?;
67 }
68
69 let mut evt_buf = vec![0_u8; 1024];
70 loop {
71 println!("waiting for events (send SIGINT to exit)");
72 for evt in card.read_events(&mut evt_buf)? {
73 println!("event {evt:?}");
74 match evt {
75 DrmEvent::Generic(GenericDrmEvent::FlipComplete(_)) => {
76 // In a real program this would be a time place to draw the next frame
77 // for the reported crtc.
78 }
79 _ => {
80 // Ignore any unrecognized event types.
81 }
82 }
83 }
84 }
85}
More examples
40fn display_demo(card: &mut Card) -> Result<(), Error> {
41 let mut outputs = prepare_outputs(&card)?;
42 let mut req = linux_drm::modeset::AtomicRequest::new();
43
44 for output in &mut outputs {
45 println!("preparing output {output:#?}");
46 let conn = card.connector_state(output.conn_id)?;
47
48 let mode = &output.mode;
49 let mode_name = String::from_utf8_lossy(&mode.name);
50 println!(
51 "{:?} connector uses {mode_name} ({}x{}@{}Hz)",
52 conn.connector_type, mode.hdisplay, mode.vdisplay, mode.vrefresh,
53 );
54
55 let rows = output.db.height() as usize;
56 let pitch = output.db.pitch() as usize;
57 let data = output.db.buffer_mut();
58 for i in 0..rows {
59 if (i % 8) > 3 {
60 let row = &mut data[(i * pitch)..(i * pitch) + pitch];
61 row.fill(0xff);
62 }
63 }
64
65 println!(
66 "configuring CRTC {:?} for framebuffer {:?} and mode {mode_name} on connector {:?}",
67 output.crtc_id,
68 output.db.framebuffer_id(),
69 conn.id
70 );
71
72 req.set_property(
73 ObjectId::Connector(output.conn_id),
74 output.conn_prop_ids.crtc_id,
75 output.crtc_id,
76 );
77 req.set_property(
78 ObjectId::Crtc(output.crtc_id),
79 output.crtc_prop_ids.active,
80 true,
81 );
82 req.set_property(
83 ObjectId::Plane(output.plane_id),
84 output.plane_prop_ids.fb_id,
85 output.db.framebuffer_id(),
86 );
87 req.set_property(
88 ObjectId::Plane(output.plane_id),
89 output.plane_prop_ids.crtc_id,
90 output.crtc_id,
91 );
92 req.set_property(
93 ObjectId::Plane(output.plane_id),
94 output.plane_prop_ids.crtc_x,
95 0,
96 );
97 req.set_property(
98 ObjectId::Plane(output.plane_id),
99 output.plane_prop_ids.crtc_y,
100 0,
101 );
102 req.set_property(
103 ObjectId::Plane(output.plane_id),
104 output.plane_prop_ids.crtc_w,
105 output.db.width(),
106 );
107 req.set_property(
108 ObjectId::Plane(output.plane_id),
109 output.plane_prop_ids.crtc_h,
110 output.db.height(),
111 );
112 req.set_property(
113 ObjectId::Plane(output.plane_id),
114 output.plane_prop_ids.src_x,
115 0,
116 );
117 req.set_property(
118 ObjectId::Plane(output.plane_id),
119 output.plane_prop_ids.src_y,
120 0,
121 );
122 req.set_property(
123 ObjectId::Plane(output.plane_id),
124 output.plane_prop_ids.src_w,
125 (output.db.width() as u64) << 16,
126 );
127 req.set_property(
128 ObjectId::Plane(output.plane_id),
129 output.plane_prop_ids.src_h,
130 (output.db.height() as u64) << 16,
131 );
132 }
133
134 println!("atomic commit {req:#?}");
135 card.atomic_commit(
136 &req,
137 AtomicCommitFlags::ALLOW_MODESET | AtomicCommitFlags::PAGE_FLIP_EVENT,
138 0,
139 )?;
140
141 let mut evt_buf = vec![0_u8; 1024];
142 loop {
143 println!("waiting for events (send SIGINT to exit)");
144 for evt in card.read_events(&mut evt_buf)? {
145 println!("event {evt:?}");
146 match evt {
147 DrmEvent::Generic(GenericDrmEvent::FlipComplete(_)) => {
148 // In a real program this would be a time place to draw the next frame
149 // for the reported crtc.
150 }
151 _ => {
152 // Ignore any unrecognized event types.
153 }
154 }
155 }
156 }
157}
Sourcepub fn take_file(self) -> Result<File<DrmCardDevice>>
pub fn take_file(self) -> Result<File<DrmCardDevice>>
Take the file from underneath this card object without closing it.
Sourcepub fn borrow_file(&self) -> &File<DrmCardDevice>
pub fn borrow_file(&self) -> &File<DrmCardDevice>
Borrow the file object that this card object wraps.