pub struct Card { /* private fields */ }
Implementations§
Source§impl Card
impl Card
Sourcepub fn open(path: impl AsRef<CStr>) -> Result<Self, InitError>
pub fn open(path: impl AsRef<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?
17fn main() -> std::io::Result<()> {
18 let card_path = card_path();
19 let mut card = Card::open(card_path).map_err(map_init_err)?;
20 card.become_master().map_err(map_err)?;
21
22 {
23 let name = card.driver_name().map_err(map_err)?;
24 let name = String::from_utf8_lossy(&name);
25 println!("Driver name: {name}");
26 }
27
28 if card
29 .get_device_cap(DeviceCap::DumbBuffer)
30 .map_err(map_err)?
31 == 0
32 {
33 return Err(std::io::Error::other(
34 "device does not support 'dumb buffers'",
35 ));
36 } else {
37 println!("Device supports 'dumb buffers'");
38 }
39
40 display_demo(&mut card).map_err(map_err)
41}
More examples
14fn main() -> std::io::Result<()> {
15 let card_path = card_path();
16 let mut card = Card::open(card_path).map_err(map_init_err)?;
17
18 {
19 let name = card.driver_name().map_err(map_err)?;
20 let name = String::from_utf8_lossy(&name);
21 println!("Driver name: {name}");
22 }
23
24 if card
25 .get_device_cap(DeviceCap::DumbBuffer)
26 .map_err(map_err)?
27 == 0
28 {
29 return Err(std::io::Error::other(
30 "device does not support 'dumb buffers'",
31 ));
32 }
33
34 card.set_client_cap(ClientCap::UniversalPlanes, 1)
35 .map_err(map_err)?;
36 card.set_client_cap(ClientCap::Atomic, 1).map_err(map_err)?;
37
38 show_properties(&card).map_err(map_err)
39}
16fn main() -> std::io::Result<()> {
17 let card_path = card_path();
18 let mut card = Card::open(card_path).map_err(map_init_err)?;
19 card.become_master().map_err(map_err)?;
20
21 {
22 let name = card.driver_name().map_err(map_err)?;
23 let name = String::from_utf8_lossy(&name);
24 println!("Driver name: {name}");
25 }
26
27 if card
28 .get_device_cap(DeviceCap::DumbBuffer)
29 .map_err(map_err)?
30 == 0
31 {
32 return Err(std::io::Error::other(
33 "device does not support 'dumb buffers'",
34 ));
35 } else {
36 println!("Device supports 'dumb buffers'");
37 }
38
39 card.set_client_cap(ClientCap::UniversalPlanes, 1)
40 .map_err(map_err)?;
41 card.set_client_cap(ClientCap::Atomic, 1).map_err(map_err)?;
42
43 display_demo(&mut card).map_err(map_err)
44}
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?
17fn main() -> std::io::Result<()> {
18 let card_path = card_path();
19 let mut card = Card::open(card_path).map_err(map_init_err)?;
20 card.become_master().map_err(map_err)?;
21
22 {
23 let name = card.driver_name().map_err(map_err)?;
24 let name = String::from_utf8_lossy(&name);
25 println!("Driver name: {name}");
26 }
27
28 if card
29 .get_device_cap(DeviceCap::DumbBuffer)
30 .map_err(map_err)?
31 == 0
32 {
33 return Err(std::io::Error::other(
34 "device does not support 'dumb buffers'",
35 ));
36 } else {
37 println!("Device supports 'dumb buffers'");
38 }
39
40 display_demo(&mut card).map_err(map_err)
41}
More examples
14fn main() -> std::io::Result<()> {
15 let card_path = card_path();
16 let mut card = Card::open(card_path).map_err(map_init_err)?;
17
18 {
19 let name = card.driver_name().map_err(map_err)?;
20 let name = String::from_utf8_lossy(&name);
21 println!("Driver name: {name}");
22 }
23
24 if card
25 .get_device_cap(DeviceCap::DumbBuffer)
26 .map_err(map_err)?
27 == 0
28 {
29 return Err(std::io::Error::other(
30 "device does not support 'dumb buffers'",
31 ));
32 }
33
34 card.set_client_cap(ClientCap::UniversalPlanes, 1)
35 .map_err(map_err)?;
36 card.set_client_cap(ClientCap::Atomic, 1).map_err(map_err)?;
37
38 show_properties(&card).map_err(map_err)
39}
16fn main() -> std::io::Result<()> {
17 let card_path = card_path();
18 let mut card = Card::open(card_path).map_err(map_init_err)?;
19 card.become_master().map_err(map_err)?;
20
21 {
22 let name = card.driver_name().map_err(map_err)?;
23 let name = String::from_utf8_lossy(&name);
24 println!("Driver name: {name}");
25 }
26
27 if card
28 .get_device_cap(DeviceCap::DumbBuffer)
29 .map_err(map_err)?
30 == 0
31 {
32 return Err(std::io::Error::other(
33 "device does not support 'dumb buffers'",
34 ));
35 } else {
36 println!("Device supports 'dumb buffers'");
37 }
38
39 card.set_client_cap(ClientCap::UniversalPlanes, 1)
40 .map_err(map_err)?;
41 card.set_client_cap(ClientCap::Atomic, 1).map_err(map_err)?;
42
43 display_demo(&mut card).map_err(map_err)
44}
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?
17fn main() -> std::io::Result<()> {
18 let card_path = card_path();
19 let mut card = Card::open(card_path).map_err(map_init_err)?;
20 card.become_master().map_err(map_err)?;
21
22 {
23 let name = card.driver_name().map_err(map_err)?;
24 let name = String::from_utf8_lossy(&name);
25 println!("Driver name: {name}");
26 }
27
28 if card
29 .get_device_cap(DeviceCap::DumbBuffer)
30 .map_err(map_err)?
31 == 0
32 {
33 return Err(std::io::Error::other(
34 "device does not support 'dumb buffers'",
35 ));
36 } else {
37 println!("Device supports 'dumb buffers'");
38 }
39
40 display_demo(&mut card).map_err(map_err)
41}
More examples
14fn main() -> std::io::Result<()> {
15 let card_path = card_path();
16 let mut card = Card::open(card_path).map_err(map_init_err)?;
17
18 {
19 let name = card.driver_name().map_err(map_err)?;
20 let name = String::from_utf8_lossy(&name);
21 println!("Driver name: {name}");
22 }
23
24 if card
25 .get_device_cap(DeviceCap::DumbBuffer)
26 .map_err(map_err)?
27 == 0
28 {
29 return Err(std::io::Error::other(
30 "device does not support 'dumb buffers'",
31 ));
32 }
33
34 card.set_client_cap(ClientCap::UniversalPlanes, 1)
35 .map_err(map_err)?;
36 card.set_client_cap(ClientCap::Atomic, 1).map_err(map_err)?;
37
38 show_properties(&card).map_err(map_err)
39}
16fn main() -> std::io::Result<()> {
17 let card_path = card_path();
18 let mut card = Card::open(card_path).map_err(map_init_err)?;
19 card.become_master().map_err(map_err)?;
20
21 {
22 let name = card.driver_name().map_err(map_err)?;
23 let name = String::from_utf8_lossy(&name);
24 println!("Driver name: {name}");
25 }
26
27 if card
28 .get_device_cap(DeviceCap::DumbBuffer)
29 .map_err(map_err)?
30 == 0
31 {
32 return Err(std::io::Error::other(
33 "device does not support 'dumb buffers'",
34 ));
35 } else {
36 println!("Device supports 'dumb buffers'");
37 }
38
39 card.set_client_cap(ClientCap::UniversalPlanes, 1)
40 .map_err(map_err)?;
41 card.set_client_cap(ClientCap::Atomic, 1).map_err(map_err)?;
42
43 display_demo(&mut card).map_err(map_err)
44}
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?
14fn main() -> std::io::Result<()> {
15 let card_path = card_path();
16 let mut card = Card::open(card_path).map_err(map_init_err)?;
17
18 {
19 let name = card.driver_name().map_err(map_err)?;
20 let name = String::from_utf8_lossy(&name);
21 println!("Driver name: {name}");
22 }
23
24 if card
25 .get_device_cap(DeviceCap::DumbBuffer)
26 .map_err(map_err)?
27 == 0
28 {
29 return Err(std::io::Error::other(
30 "device does not support 'dumb buffers'",
31 ));
32 }
33
34 card.set_client_cap(ClientCap::UniversalPlanes, 1)
35 .map_err(map_err)?;
36 card.set_client_cap(ClientCap::Atomic, 1).map_err(map_err)?;
37
38 show_properties(&card).map_err(map_err)
39}
More examples
16fn main() -> std::io::Result<()> {
17 let card_path = card_path();
18 let mut card = Card::open(card_path).map_err(map_init_err)?;
19 card.become_master().map_err(map_err)?;
20
21 {
22 let name = card.driver_name().map_err(map_err)?;
23 let name = String::from_utf8_lossy(&name);
24 println!("Driver name: {name}");
25 }
26
27 if card
28 .get_device_cap(DeviceCap::DumbBuffer)
29 .map_err(map_err)?
30 == 0
31 {
32 return Err(std::io::Error::other(
33 "device does not support 'dumb buffers'",
34 ));
35 } else {
36 println!("Device supports 'dumb buffers'");
37 }
38
39 card.set_client_cap(ClientCap::UniversalPlanes, 1)
40 .map_err(map_err)?;
41 card.set_client_cap(ClientCap::Atomic, 1).map_err(map_err)?;
42
43 display_demo(&mut card).map_err(map_err)
44}
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?
17fn main() -> std::io::Result<()> {
18 let card_path = card_path();
19 let mut card = Card::open(card_path).map_err(map_init_err)?;
20 card.become_master().map_err(map_err)?;
21
22 {
23 let name = card.driver_name().map_err(map_err)?;
24 let name = String::from_utf8_lossy(&name);
25 println!("Driver name: {name}");
26 }
27
28 if card
29 .get_device_cap(DeviceCap::DumbBuffer)
30 .map_err(map_err)?
31 == 0
32 {
33 return Err(std::io::Error::other(
34 "device does not support 'dumb buffers'",
35 ));
36 } else {
37 println!("Device supports 'dumb buffers'");
38 }
39
40 display_demo(&mut card).map_err(map_err)
41}
More examples
16fn main() -> std::io::Result<()> {
17 let card_path = card_path();
18 let mut card = Card::open(card_path).map_err(map_init_err)?;
19 card.become_master().map_err(map_err)?;
20
21 {
22 let name = card.driver_name().map_err(map_err)?;
23 let name = String::from_utf8_lossy(&name);
24 println!("Driver name: {name}");
25 }
26
27 if card
28 .get_device_cap(DeviceCap::DumbBuffer)
29 .map_err(map_err)?
30 == 0
31 {
32 return Err(std::io::Error::other(
33 "device does not support 'dumb buffers'",
34 ));
35 } else {
36 println!("Device supports 'dumb buffers'");
37 }
38
39 card.set_client_cap(ClientCap::UniversalPlanes, 1)
40 .map_err(map_err)?;
41 card.set_client_cap(ClientCap::Atomic, 1).map_err(map_err)?;
42
43 display_demo(&mut card).map_err(map_err)
44}
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?
164fn property_meta<'a, 'card>(
165 prop_id: PropertyId,
166 prop_meta: &'a mut HashMap<u32, PropertyMeta>,
167 card: &Card,
168) -> Result<&'a PropertyMeta, Error> {
169 Ok(prop_meta.entry(prop_id.0).or_insert_with(|| {
170 card.property_meta(prop_id)
171 .map(|meta| {
172 let mut enum_names = BTreeMap::new();
173 for member in meta.enum_members().unwrap() {
174 enum_names.insert(member.value(), member.name().to_string());
175 }
176 PropertyMeta {
177 name: meta.name().to_string(),
178 typ: meta.property_type(),
179 immutable: meta.is_immutable(),
180 values: meta.values().unwrap(),
181 enum_names,
182 }
183 })
184 .unwrap_or(PropertyMeta {
185 name: String::from("<unknown>"),
186 typ: PropertyType::Unknown,
187 immutable: true,
188 values: Vec::new(),
189 enum_names: BTreeMap::new(),
190 })
191 }))
192}
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?
287 pub fn new(conn_id: ConnectorId, card: &linux_drm::Card) -> Result<Self, Error> {
288 let mut ret: Self = unsafe { core::mem::zeroed() };
289 card.each_object_property_meta(conn_id, |meta, _| ret.populate_from(meta))?;
290 Ok(ret)
291 }
292
293 pub fn populate_from<'card>(&mut self, from: linux_drm::modeset::ObjectPropMeta<'card>) {
294 match from.name() {
295 "CRTC_ID" => self.crtc_id = from.property_id(),
296 _ => {}
297 }
298 }
299}
300
301#[derive(Debug)]
302struct CrtcPropIds {
303 active: PropertyId,
304}
305
306impl CrtcPropIds {
307 pub fn new(crtc_id: CrtcId, card: &linux_drm::Card) -> Result<Self, Error> {
308 let mut ret: Self = unsafe { core::mem::zeroed() };
309 card.each_object_property_meta(linux_drm::modeset::ObjectId::Crtc(crtc_id), |meta, _| {
310 ret.populate_from(meta)
311 })?;
312 Ok(ret)
313 }
314
315 pub fn populate_from<'card>(&mut self, from: linux_drm::modeset::ObjectPropMeta<'card>) {
316 match from.name() {
317 "ACTIVE" => self.active = from.property_id(),
318 _ => {}
319 }
320 }
321}
322
323#[derive(Debug)]
324struct PlanePropIds {
325 typ: PropertyId,
326 fb_id: PropertyId,
327 crtc_id: PropertyId,
328 crtc_x: PropertyId,
329 crtc_y: PropertyId,
330 crtc_w: PropertyId,
331 crtc_h: PropertyId,
332 src_x: PropertyId,
333 src_y: PropertyId,
334 src_w: PropertyId,
335 src_h: PropertyId,
336}
337
338impl PlanePropIds {
339 pub fn new(plane_id: PlaneId, card: &linux_drm::Card) -> Result<Self, Error> {
340 let mut ret: Self = unsafe { core::mem::zeroed() };
341 card.each_object_property_meta(plane_id, |meta, _| ret.populate_from(meta))?;
342 Ok(ret)
343 }
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?
108fn prepare_outputs(card: &Card) -> Result<Vec<Output>, Error> {
109 let resources = card.resources()?;
110 let mut outputs = Vec::<Output>::new();
111
112 for id in resources.connector_ids.iter().copied() {
113 let conn = card.connector_state(id)?;
114 if conn.connection_state != ConnectionState::Connected {
115 println!("ignoring unconnected connector {id:?}");
116 continue;
117 }
118 if conn.current_encoder_id.0 == 0 {
119 println!("ignoring encoderless connector {id:?}");
120 continue;
121 }
122 if conn.modes.len() == 0 {
123 println!("ignoring modeless connector {id:?}");
124 continue;
125 }
126
127 let output = prepare_output(card, conn, &resources)?;
128 outputs.push(output);
129 }
130
131 Ok(outputs)
132}
More examples
179fn prepare_outputs(card: &Card) -> Result<Vec<Output>, Error> {
180 println!("preparing outputs");
181
182 let resources = card.resources()?;
183 let mut outputs = Vec::<Output>::new();
184
185 for id in resources.connector_ids.iter().copied() {
186 println!("preparing output for connector #{id:?}");
187
188 let conn = card.connector_state(id)?;
189 if conn.connection_state != ConnectionState::Connected {
190 println!("ignoring unconnected connector {id:?}");
191 continue;
192 }
193 if conn.current_encoder_id.0 == 0 {
194 println!("ignoring encoderless connector {id:?}");
195 continue;
196 }
197 if conn.modes.len() == 0 {
198 println!("ignoring modeless connector {id:?}");
199 continue;
200 }
201
202 let output = prepare_output(card, conn, &resources)?;
203 outputs.push(output);
204 }
205
206 Ok(outputs)
207}
55fn show_properties(card: &Card) -> Result<(), Error> {
56 let res = card.resources()?;
57
58 let mut prop_meta = HashMap::<u32, PropertyMeta>::new();
59
60 println!("");
61
62 for conn_id in res.connector_ids {
63 println!("Connector #{conn_id:?}:");
64 if let Err(e) = show_object_property_list(conn_id, &mut prop_meta, card) {
65 println!(" Error: {e:?}");
66 }
67 println!("");
68 }
69
70 for enc_id in res.encoder_ids {
71 println!("Encoder #{enc_id:?}:");
72 if let Err(e) = show_object_property_list(enc_id, &mut prop_meta, card) {
73 println!(" Error: {e:?}");
74 }
75 println!("");
76 }
77
78 for crtc_id in res.crtc_ids {
79 println!("CRTC #{crtc_id:?}:");
80 if let Err(e) = show_object_property_list(crtc_id, &mut prop_meta, card) {
81 println!(" Error: {e:?}");
82 }
83 println!("");
84 }
85
86 for fb_id in res.fb_ids {
87 println!("Framebuffer #{fb_id:?}:");
88 if let Err(e) = show_object_property_list(fb_id, &mut prop_meta, card) {
89 println!(" Error: {e:?}");
90 }
91 println!("");
92 }
93
94 for plane_id in res.plane_ids {
95 println!("Plane #{plane_id:?}:");
96 if let Err(e) = show_object_property_list(plane_id, &mut prop_meta, card) {
97 println!(" Error: {e:?}");
98 }
99 println!("");
100 }
101
102 Ok(())
103}
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?
57fn display_demo(card: &mut Card) -> Result<(), Error> {
58 let mut outputs = prepare_outputs(&card)?;
59 for output in &mut outputs {
60 println!("preparing output {output:#?}");
61 let conn = card.connector_state(output.conn_id)?;
62
63 let mode = &output.mode;
64 let mode_name = String::from_utf8_lossy(&mode.name);
65 println!(
66 "{:?} connector uses {mode_name} ({}x{}@{}Hz)",
67 conn.connector_type, mode.hdisplay, mode.vdisplay, mode.vrefresh,
68 );
69
70 let rows = output.db.height() as usize;
71 let pitch = output.db.pitch() as usize;
72 let data = output.db.buffer_mut();
73 for i in 0..rows {
74 if (i % 8) > 3 {
75 let row = &mut data[(i * pitch)..(i * pitch) + pitch];
76 row.fill(0xff);
77 }
78 }
79
80 println!(
81 "configuring CRTC {:?} for framebuffer {:?} and mode {mode_name} on connection {:?}",
82 output.crtc_id,
83 output.db.framebuffer_id(),
84 conn.id
85 );
86 card.set_crtc_dumb_buffer(output.crtc_id, &output.db, mode, &[output.conn_id])?;
87 card.crtc_page_flip_dumb_buffer(output.crtc_id, &output.db, PageFlipFlags::EVENT)?;
88 }
89
90 let mut evt_buf = vec![0_u8; 1024];
91 loop {
92 println!("waiting for events (send SIGINT to exit)");
93 for evt in card.read_events(&mut evt_buf)? {
94 println!("event {evt:?}");
95 match evt {
96 DrmEvent::Generic(GenericDrmEvent::FlipComplete(_)) => {
97 // In a real program this would be a time place to draw the next frame
98 // for the reported crtc.
99 }
100 _ => {
101 // Ignore any unrecognized event types.
102 }
103 }
104 }
105 }
106}
107
108fn prepare_outputs(card: &Card) -> Result<Vec<Output>, Error> {
109 let resources = card.resources()?;
110 let mut outputs = Vec::<Output>::new();
111
112 for id in resources.connector_ids.iter().copied() {
113 let conn = card.connector_state(id)?;
114 if conn.connection_state != ConnectionState::Connected {
115 println!("ignoring unconnected connector {id:?}");
116 continue;
117 }
118 if conn.current_encoder_id.0 == 0 {
119 println!("ignoring encoderless connector {id:?}");
120 continue;
121 }
122 if conn.modes.len() == 0 {
123 println!("ignoring modeless connector {id:?}");
124 continue;
125 }
126
127 let output = prepare_output(card, conn, &resources)?;
128 outputs.push(output);
129 }
130
131 Ok(outputs)
132}
More examples
60fn display_demo(card: &mut Card) -> Result<(), Error> {
61 let mut outputs = prepare_outputs(&card)?;
62 let mut req = linux_drm::modeset::AtomicRequest::new();
63
64 for output in &mut outputs {
65 println!("preparing output {output:#?}");
66 let conn = card.connector_state(output.conn_id)?;
67
68 let mode = &output.mode;
69 let mode_name = String::from_utf8_lossy(&mode.name);
70 println!(
71 "{:?} connector uses {mode_name} ({}x{}@{}Hz)",
72 conn.connector_type, mode.hdisplay, mode.vdisplay, mode.vrefresh,
73 );
74
75 let rows = output.db.height() as usize;
76 let pitch = output.db.pitch() as usize;
77 let data = output.db.buffer_mut();
78 for i in 0..rows {
79 if (i % 8) > 3 {
80 let row = &mut data[(i * pitch)..(i * pitch) + pitch];
81 row.fill(0xff);
82 }
83 }
84
85 println!(
86 "configuring CRTC {:?} for framebuffer {:?} and mode {mode_name} on connector {:?}",
87 output.crtc_id,
88 output.db.framebuffer_id(),
89 conn.id
90 );
91
92 req.set_property(
93 ObjectId::Connector(output.conn_id),
94 output.conn_prop_ids.crtc_id,
95 output.crtc_id,
96 );
97 req.set_property(
98 ObjectId::Crtc(output.crtc_id),
99 output.crtc_prop_ids.active,
100 true,
101 );
102 req.set_property(
103 ObjectId::Plane(output.plane_id),
104 output.plane_prop_ids.fb_id,
105 output.db.framebuffer_id(),
106 );
107 req.set_property(
108 ObjectId::Plane(output.plane_id),
109 output.plane_prop_ids.crtc_id,
110 output.crtc_id,
111 );
112 req.set_property(
113 ObjectId::Plane(output.plane_id),
114 output.plane_prop_ids.crtc_x,
115 0,
116 );
117 req.set_property(
118 ObjectId::Plane(output.plane_id),
119 output.plane_prop_ids.crtc_y,
120 0,
121 );
122 req.set_property(
123 ObjectId::Plane(output.plane_id),
124 output.plane_prop_ids.crtc_w,
125 output.db.width(),
126 );
127 req.set_property(
128 ObjectId::Plane(output.plane_id),
129 output.plane_prop_ids.crtc_h,
130 output.db.height(),
131 );
132 req.set_property(
133 ObjectId::Plane(output.plane_id),
134 output.plane_prop_ids.src_x,
135 0,
136 );
137 req.set_property(
138 ObjectId::Plane(output.plane_id),
139 output.plane_prop_ids.src_y,
140 0,
141 );
142 req.set_property(
143 ObjectId::Plane(output.plane_id),
144 output.plane_prop_ids.src_w,
145 (output.db.width() as u64) << 16,
146 );
147 req.set_property(
148 ObjectId::Plane(output.plane_id),
149 output.plane_prop_ids.src_h,
150 (output.db.height() as u64) << 16,
151 );
152 }
153
154 println!("atomic commit {req:#?}");
155 card.atomic_commit(
156 &req,
157 AtomicCommitFlags::ALLOW_MODESET | AtomicCommitFlags::PAGE_FLIP_EVENT,
158 0,
159 )?;
160
161 let mut evt_buf = vec![0_u8; 1024];
162 loop {
163 println!("waiting for events (send SIGINT to exit)");
164 for evt in card.read_events(&mut evt_buf)? {
165 println!("event {evt:?}");
166 match evt {
167 DrmEvent::Generic(GenericDrmEvent::FlipComplete(_)) => {
168 // In a real program this would be a time place to draw the next frame
169 // for the reported crtc.
170 }
171 _ => {
172 // Ignore any unrecognized event types.
173 }
174 }
175 }
176 }
177}
178
179fn prepare_outputs(card: &Card) -> Result<Vec<Output>, Error> {
180 println!("preparing outputs");
181
182 let resources = card.resources()?;
183 let mut outputs = Vec::<Output>::new();
184
185 for id in resources.connector_ids.iter().copied() {
186 println!("preparing output for connector #{id:?}");
187
188 let conn = card.connector_state(id)?;
189 if conn.connection_state != ConnectionState::Connected {
190 println!("ignoring unconnected connector {id:?}");
191 continue;
192 }
193 if conn.current_encoder_id.0 == 0 {
194 println!("ignoring encoderless connector {id:?}");
195 continue;
196 }
197 if conn.modes.len() == 0 {
198 println!("ignoring modeless connector {id:?}");
199 continue;
200 }
201
202 let output = prepare_output(card, conn, &resources)?;
203 outputs.push(output);
204 }
205
206 Ok(outputs)
207}
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?
134fn prepare_output(
135 card: &Card,
136 conn: ConnectorState,
137 resources: &CardResources,
138) -> Result<Output, Error> {
139 if conn.current_encoder_id.0 == 0 {
140 // It could be reasonable to go hunting for a suitable encoder and
141 // CRTC to activate this connector, but for this simple example
142 // we'll just use whatever connectors are already producing some
143 // output and keep using whatever modes they are currently in.
144 return Err(Error::NotSupported);
145 }
146 let _ = resources; // (don't actually need this when we're just using the already-selected encoder/crtc)
147
148 let enc = card.encoder_state(conn.current_encoder_id)?;
149 let crtc_id = enc.current_crtc_id;
150 let crtc = card.crtc_state(crtc_id)?;
151 let mode = crtc.mode;
152 let db = card.create_dumb_buffer(DumbBufferRequest {
153 width: mode.hdisplay as u32,
154 height: mode.vdisplay as u32,
155 depth: 24,
156 bpp: 32,
157 })?;
158 Ok(Output {
159 conn_id: conn.id,
160 crtc_id,
161 mode,
162 db,
163 })
164}
More examples
209fn prepare_output(
210 card: &Card,
211 conn: ConnectorState,
212 resources: &CardResources,
213) -> Result<Output, Error> {
214 if conn.current_encoder_id.0 == 0 {
215 // It could be reasonable to go hunting for a suitable encoder and
216 // CRTC to activate this connector, but for this simple example
217 // we'll just use whatever connectors are already producing some
218 // output and keep using whatever modes they are currently in.
219 return Err(Error::NotSupported);
220 }
221 let _ = resources; // (don't actually need this when we're just using the already-selected encoder/crtc)
222
223 let enc = card.encoder_state(conn.current_encoder_id)?;
224 let crtc_id = enc.current_crtc_id;
225 let crtc = card.crtc_state(crtc_id)?;
226 let mode = crtc.mode;
227 let db = card.create_dumb_buffer(DumbBufferRequest {
228 width: mode.hdisplay as u32,
229 height: mode.vdisplay as u32,
230 depth: 24,
231 bpp: 32,
232 })?;
233
234 // We need to find the primary plane that's currently assigned to this CRTC.
235 // The following is not really a correct way to do it, but it'll work for
236 // now just to test if anything is working here at all. (This makes some
237 // assumptions about how the card is already configured which might not
238 // actually hold in practice.)
239 let mut chosen_plane_id: Option<PlaneId> = None;
240 for plane_id in resources.plane_ids.iter().copied() {
241 let plane = card.plane_state(plane_id)?;
242 if plane.crtc_id == crtc_id {
243 chosen_plane_id = Some(plane_id);
244 break;
245 }
246 }
247 let Some(chosen_plane_id) = chosen_plane_id else {
248 return Err(Error::NonExist);
249 };
250
251 println!("collecting properties");
252 let conn_prop_ids = ConnectorPropIds::new(conn.id, card)?;
253 let crtc_prop_ids = CrtcPropIds::new(crtc_id, card)?;
254 let plane_prop_ids = PlanePropIds::new(chosen_plane_id, card)?;
255
256 println!("collected properties");
257 Ok(Output {
258 conn_id: conn.id,
259 conn_prop_ids,
260 crtc_id,
261 crtc_prop_ids,
262 plane_id: chosen_plane_id,
263 plane_prop_ids,
264 mode,
265 db,
266 })
267}
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?
134fn prepare_output(
135 card: &Card,
136 conn: ConnectorState,
137 resources: &CardResources,
138) -> Result<Output, Error> {
139 if conn.current_encoder_id.0 == 0 {
140 // It could be reasonable to go hunting for a suitable encoder and
141 // CRTC to activate this connector, but for this simple example
142 // we'll just use whatever connectors are already producing some
143 // output and keep using whatever modes they are currently in.
144 return Err(Error::NotSupported);
145 }
146 let _ = resources; // (don't actually need this when we're just using the already-selected encoder/crtc)
147
148 let enc = card.encoder_state(conn.current_encoder_id)?;
149 let crtc_id = enc.current_crtc_id;
150 let crtc = card.crtc_state(crtc_id)?;
151 let mode = crtc.mode;
152 let db = card.create_dumb_buffer(DumbBufferRequest {
153 width: mode.hdisplay as u32,
154 height: mode.vdisplay as u32,
155 depth: 24,
156 bpp: 32,
157 })?;
158 Ok(Output {
159 conn_id: conn.id,
160 crtc_id,
161 mode,
162 db,
163 })
164}
More examples
209fn prepare_output(
210 card: &Card,
211 conn: ConnectorState,
212 resources: &CardResources,
213) -> Result<Output, Error> {
214 if conn.current_encoder_id.0 == 0 {
215 // It could be reasonable to go hunting for a suitable encoder and
216 // CRTC to activate this connector, but for this simple example
217 // we'll just use whatever connectors are already producing some
218 // output and keep using whatever modes they are currently in.
219 return Err(Error::NotSupported);
220 }
221 let _ = resources; // (don't actually need this when we're just using the already-selected encoder/crtc)
222
223 let enc = card.encoder_state(conn.current_encoder_id)?;
224 let crtc_id = enc.current_crtc_id;
225 let crtc = card.crtc_state(crtc_id)?;
226 let mode = crtc.mode;
227 let db = card.create_dumb_buffer(DumbBufferRequest {
228 width: mode.hdisplay as u32,
229 height: mode.vdisplay as u32,
230 depth: 24,
231 bpp: 32,
232 })?;
233
234 // We need to find the primary plane that's currently assigned to this CRTC.
235 // The following is not really a correct way to do it, but it'll work for
236 // now just to test if anything is working here at all. (This makes some
237 // assumptions about how the card is already configured which might not
238 // actually hold in practice.)
239 let mut chosen_plane_id: Option<PlaneId> = None;
240 for plane_id in resources.plane_ids.iter().copied() {
241 let plane = card.plane_state(plane_id)?;
242 if plane.crtc_id == crtc_id {
243 chosen_plane_id = Some(plane_id);
244 break;
245 }
246 }
247 let Some(chosen_plane_id) = chosen_plane_id else {
248 return Err(Error::NonExist);
249 };
250
251 println!("collecting properties");
252 let conn_prop_ids = ConnectorPropIds::new(conn.id, card)?;
253 let crtc_prop_ids = CrtcPropIds::new(crtc_id, card)?;
254 let plane_prop_ids = PlanePropIds::new(chosen_plane_id, card)?;
255
256 println!("collected properties");
257 Ok(Output {
258 conn_id: conn.id,
259 conn_prop_ids,
260 crtc_id,
261 crtc_prop_ids,
262 plane_id: chosen_plane_id,
263 plane_prop_ids,
264 mode,
265 db,
266 })
267}
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?
209fn prepare_output(
210 card: &Card,
211 conn: ConnectorState,
212 resources: &CardResources,
213) -> Result<Output, Error> {
214 if conn.current_encoder_id.0 == 0 {
215 // It could be reasonable to go hunting for a suitable encoder and
216 // CRTC to activate this connector, but for this simple example
217 // we'll just use whatever connectors are already producing some
218 // output and keep using whatever modes they are currently in.
219 return Err(Error::NotSupported);
220 }
221 let _ = resources; // (don't actually need this when we're just using the already-selected encoder/crtc)
222
223 let enc = card.encoder_state(conn.current_encoder_id)?;
224 let crtc_id = enc.current_crtc_id;
225 let crtc = card.crtc_state(crtc_id)?;
226 let mode = crtc.mode;
227 let db = card.create_dumb_buffer(DumbBufferRequest {
228 width: mode.hdisplay as u32,
229 height: mode.vdisplay as u32,
230 depth: 24,
231 bpp: 32,
232 })?;
233
234 // We need to find the primary plane that's currently assigned to this CRTC.
235 // The following is not really a correct way to do it, but it'll work for
236 // now just to test if anything is working here at all. (This makes some
237 // assumptions about how the card is already configured which might not
238 // actually hold in practice.)
239 let mut chosen_plane_id: Option<PlaneId> = None;
240 for plane_id in resources.plane_ids.iter().copied() {
241 let plane = card.plane_state(plane_id)?;
242 if plane.crtc_id == crtc_id {
243 chosen_plane_id = Some(plane_id);
244 break;
245 }
246 }
247 let Some(chosen_plane_id) = chosen_plane_id else {
248 return Err(Error::NonExist);
249 };
250
251 println!("collecting properties");
252 let conn_prop_ids = ConnectorPropIds::new(conn.id, card)?;
253 let crtc_prop_ids = CrtcPropIds::new(crtc_id, card)?;
254 let plane_prop_ids = PlanePropIds::new(chosen_plane_id, card)?;
255
256 println!("collected properties");
257 Ok(Output {
258 conn_id: conn.id,
259 conn_prop_ids,
260 crtc_id,
261 crtc_prop_ids,
262 plane_id: chosen_plane_id,
263 plane_prop_ids,
264 mode,
265 db,
266 })
267}
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?
60fn display_demo(card: &mut Card) -> Result<(), Error> {
61 let mut outputs = prepare_outputs(&card)?;
62 let mut req = linux_drm::modeset::AtomicRequest::new();
63
64 for output in &mut outputs {
65 println!("preparing output {output:#?}");
66 let conn = card.connector_state(output.conn_id)?;
67
68 let mode = &output.mode;
69 let mode_name = String::from_utf8_lossy(&mode.name);
70 println!(
71 "{:?} connector uses {mode_name} ({}x{}@{}Hz)",
72 conn.connector_type, mode.hdisplay, mode.vdisplay, mode.vrefresh,
73 );
74
75 let rows = output.db.height() as usize;
76 let pitch = output.db.pitch() as usize;
77 let data = output.db.buffer_mut();
78 for i in 0..rows {
79 if (i % 8) > 3 {
80 let row = &mut data[(i * pitch)..(i * pitch) + pitch];
81 row.fill(0xff);
82 }
83 }
84
85 println!(
86 "configuring CRTC {:?} for framebuffer {:?} and mode {mode_name} on connector {:?}",
87 output.crtc_id,
88 output.db.framebuffer_id(),
89 conn.id
90 );
91
92 req.set_property(
93 ObjectId::Connector(output.conn_id),
94 output.conn_prop_ids.crtc_id,
95 output.crtc_id,
96 );
97 req.set_property(
98 ObjectId::Crtc(output.crtc_id),
99 output.crtc_prop_ids.active,
100 true,
101 );
102 req.set_property(
103 ObjectId::Plane(output.plane_id),
104 output.plane_prop_ids.fb_id,
105 output.db.framebuffer_id(),
106 );
107 req.set_property(
108 ObjectId::Plane(output.plane_id),
109 output.plane_prop_ids.crtc_id,
110 output.crtc_id,
111 );
112 req.set_property(
113 ObjectId::Plane(output.plane_id),
114 output.plane_prop_ids.crtc_x,
115 0,
116 );
117 req.set_property(
118 ObjectId::Plane(output.plane_id),
119 output.plane_prop_ids.crtc_y,
120 0,
121 );
122 req.set_property(
123 ObjectId::Plane(output.plane_id),
124 output.plane_prop_ids.crtc_w,
125 output.db.width(),
126 );
127 req.set_property(
128 ObjectId::Plane(output.plane_id),
129 output.plane_prop_ids.crtc_h,
130 output.db.height(),
131 );
132 req.set_property(
133 ObjectId::Plane(output.plane_id),
134 output.plane_prop_ids.src_x,
135 0,
136 );
137 req.set_property(
138 ObjectId::Plane(output.plane_id),
139 output.plane_prop_ids.src_y,
140 0,
141 );
142 req.set_property(
143 ObjectId::Plane(output.plane_id),
144 output.plane_prop_ids.src_w,
145 (output.db.width() as u64) << 16,
146 );
147 req.set_property(
148 ObjectId::Plane(output.plane_id),
149 output.plane_prop_ids.src_h,
150 (output.db.height() as u64) << 16,
151 );
152 }
153
154 println!("atomic commit {req:#?}");
155 card.atomic_commit(
156 &req,
157 AtomicCommitFlags::ALLOW_MODESET | AtomicCommitFlags::PAGE_FLIP_EVENT,
158 0,
159 )?;
160
161 let mut evt_buf = vec![0_u8; 1024];
162 loop {
163 println!("waiting for events (send SIGINT to exit)");
164 for evt in card.read_events(&mut evt_buf)? {
165 println!("event {evt:?}");
166 match evt {
167 DrmEvent::Generic(GenericDrmEvent::FlipComplete(_)) => {
168 // In a real program this would be a time place to draw the next frame
169 // for the reported crtc.
170 }
171 _ => {
172 // Ignore any unrecognized event types.
173 }
174 }
175 }
176 }
177}
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?
57fn display_demo(card: &mut Card) -> Result<(), Error> {
58 let mut outputs = prepare_outputs(&card)?;
59 for output in &mut outputs {
60 println!("preparing output {output:#?}");
61 let conn = card.connector_state(output.conn_id)?;
62
63 let mode = &output.mode;
64 let mode_name = String::from_utf8_lossy(&mode.name);
65 println!(
66 "{:?} connector uses {mode_name} ({}x{}@{}Hz)",
67 conn.connector_type, mode.hdisplay, mode.vdisplay, mode.vrefresh,
68 );
69
70 let rows = output.db.height() as usize;
71 let pitch = output.db.pitch() as usize;
72 let data = output.db.buffer_mut();
73 for i in 0..rows {
74 if (i % 8) > 3 {
75 let row = &mut data[(i * pitch)..(i * pitch) + pitch];
76 row.fill(0xff);
77 }
78 }
79
80 println!(
81 "configuring CRTC {:?} for framebuffer {:?} and mode {mode_name} on connection {:?}",
82 output.crtc_id,
83 output.db.framebuffer_id(),
84 conn.id
85 );
86 card.set_crtc_dumb_buffer(output.crtc_id, &output.db, mode, &[output.conn_id])?;
87 card.crtc_page_flip_dumb_buffer(output.crtc_id, &output.db, PageFlipFlags::EVENT)?;
88 }
89
90 let mut evt_buf = vec![0_u8; 1024];
91 loop {
92 println!("waiting for events (send SIGINT to exit)");
93 for evt in card.read_events(&mut evt_buf)? {
94 println!("event {evt:?}");
95 match evt {
96 DrmEvent::Generic(GenericDrmEvent::FlipComplete(_)) => {
97 // In a real program this would be a time place to draw the next frame
98 // for the reported crtc.
99 }
100 _ => {
101 // Ignore any unrecognized event types.
102 }
103 }
104 }
105 }
106}
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?
57fn display_demo(card: &mut Card) -> Result<(), Error> {
58 let mut outputs = prepare_outputs(&card)?;
59 for output in &mut outputs {
60 println!("preparing output {output:#?}");
61 let conn = card.connector_state(output.conn_id)?;
62
63 let mode = &output.mode;
64 let mode_name = String::from_utf8_lossy(&mode.name);
65 println!(
66 "{:?} connector uses {mode_name} ({}x{}@{}Hz)",
67 conn.connector_type, mode.hdisplay, mode.vdisplay, mode.vrefresh,
68 );
69
70 let rows = output.db.height() as usize;
71 let pitch = output.db.pitch() as usize;
72 let data = output.db.buffer_mut();
73 for i in 0..rows {
74 if (i % 8) > 3 {
75 let row = &mut data[(i * pitch)..(i * pitch) + pitch];
76 row.fill(0xff);
77 }
78 }
79
80 println!(
81 "configuring CRTC {:?} for framebuffer {:?} and mode {mode_name} on connection {:?}",
82 output.crtc_id,
83 output.db.framebuffer_id(),
84 conn.id
85 );
86 card.set_crtc_dumb_buffer(output.crtc_id, &output.db, mode, &[output.conn_id])?;
87 card.crtc_page_flip_dumb_buffer(output.crtc_id, &output.db, PageFlipFlags::EVENT)?;
88 }
89
90 let mut evt_buf = vec![0_u8; 1024];
91 loop {
92 println!("waiting for events (send SIGINT to exit)");
93 for evt in card.read_events(&mut evt_buf)? {
94 println!("event {evt:?}");
95 match evt {
96 DrmEvent::Generic(GenericDrmEvent::FlipComplete(_)) => {
97 // In a real program this would be a time place to draw the next frame
98 // for the reported crtc.
99 }
100 _ => {
101 // Ignore any unrecognized event types.
102 }
103 }
104 }
105 }
106}
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?
134fn prepare_output(
135 card: &Card,
136 conn: ConnectorState,
137 resources: &CardResources,
138) -> Result<Output, Error> {
139 if conn.current_encoder_id.0 == 0 {
140 // It could be reasonable to go hunting for a suitable encoder and
141 // CRTC to activate this connector, but for this simple example
142 // we'll just use whatever connectors are already producing some
143 // output and keep using whatever modes they are currently in.
144 return Err(Error::NotSupported);
145 }
146 let _ = resources; // (don't actually need this when we're just using the already-selected encoder/crtc)
147
148 let enc = card.encoder_state(conn.current_encoder_id)?;
149 let crtc_id = enc.current_crtc_id;
150 let crtc = card.crtc_state(crtc_id)?;
151 let mode = crtc.mode;
152 let db = card.create_dumb_buffer(DumbBufferRequest {
153 width: mode.hdisplay as u32,
154 height: mode.vdisplay as u32,
155 depth: 24,
156 bpp: 32,
157 })?;
158 Ok(Output {
159 conn_id: conn.id,
160 crtc_id,
161 mode,
162 db,
163 })
164}
More examples
209fn prepare_output(
210 card: &Card,
211 conn: ConnectorState,
212 resources: &CardResources,
213) -> Result<Output, Error> {
214 if conn.current_encoder_id.0 == 0 {
215 // It could be reasonable to go hunting for a suitable encoder and
216 // CRTC to activate this connector, but for this simple example
217 // we'll just use whatever connectors are already producing some
218 // output and keep using whatever modes they are currently in.
219 return Err(Error::NotSupported);
220 }
221 let _ = resources; // (don't actually need this when we're just using the already-selected encoder/crtc)
222
223 let enc = card.encoder_state(conn.current_encoder_id)?;
224 let crtc_id = enc.current_crtc_id;
225 let crtc = card.crtc_state(crtc_id)?;
226 let mode = crtc.mode;
227 let db = card.create_dumb_buffer(DumbBufferRequest {
228 width: mode.hdisplay as u32,
229 height: mode.vdisplay as u32,
230 depth: 24,
231 bpp: 32,
232 })?;
233
234 // We need to find the primary plane that's currently assigned to this CRTC.
235 // The following is not really a correct way to do it, but it'll work for
236 // now just to test if anything is working here at all. (This makes some
237 // assumptions about how the card is already configured which might not
238 // actually hold in practice.)
239 let mut chosen_plane_id: Option<PlaneId> = None;
240 for plane_id in resources.plane_ids.iter().copied() {
241 let plane = card.plane_state(plane_id)?;
242 if plane.crtc_id == crtc_id {
243 chosen_plane_id = Some(plane_id);
244 break;
245 }
246 }
247 let Some(chosen_plane_id) = chosen_plane_id else {
248 return Err(Error::NonExist);
249 };
250
251 println!("collecting properties");
252 let conn_prop_ids = ConnectorPropIds::new(conn.id, card)?;
253 let crtc_prop_ids = CrtcPropIds::new(crtc_id, card)?;
254 let plane_prop_ids = PlanePropIds::new(chosen_plane_id, card)?;
255
256 println!("collected properties");
257 Ok(Output {
258 conn_id: conn.id,
259 conn_prop_ids,
260 crtc_id,
261 crtc_prop_ids,
262 plane_id: chosen_plane_id,
263 plane_prop_ids,
264 mode,
265 db,
266 })
267}
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?
57fn display_demo(card: &mut Card) -> Result<(), Error> {
58 let mut outputs = prepare_outputs(&card)?;
59 for output in &mut outputs {
60 println!("preparing output {output:#?}");
61 let conn = card.connector_state(output.conn_id)?;
62
63 let mode = &output.mode;
64 let mode_name = String::from_utf8_lossy(&mode.name);
65 println!(
66 "{:?} connector uses {mode_name} ({}x{}@{}Hz)",
67 conn.connector_type, mode.hdisplay, mode.vdisplay, mode.vrefresh,
68 );
69
70 let rows = output.db.height() as usize;
71 let pitch = output.db.pitch() as usize;
72 let data = output.db.buffer_mut();
73 for i in 0..rows {
74 if (i % 8) > 3 {
75 let row = &mut data[(i * pitch)..(i * pitch) + pitch];
76 row.fill(0xff);
77 }
78 }
79
80 println!(
81 "configuring CRTC {:?} for framebuffer {:?} and mode {mode_name} on connection {:?}",
82 output.crtc_id,
83 output.db.framebuffer_id(),
84 conn.id
85 );
86 card.set_crtc_dumb_buffer(output.crtc_id, &output.db, mode, &[output.conn_id])?;
87 card.crtc_page_flip_dumb_buffer(output.crtc_id, &output.db, PageFlipFlags::EVENT)?;
88 }
89
90 let mut evt_buf = vec![0_u8; 1024];
91 loop {
92 println!("waiting for events (send SIGINT to exit)");
93 for evt in card.read_events(&mut evt_buf)? {
94 println!("event {evt:?}");
95 match evt {
96 DrmEvent::Generic(GenericDrmEvent::FlipComplete(_)) => {
97 // In a real program this would be a time place to draw the next frame
98 // for the reported crtc.
99 }
100 _ => {
101 // Ignore any unrecognized event types.
102 }
103 }
104 }
105 }
106}
More examples
60fn display_demo(card: &mut Card) -> Result<(), Error> {
61 let mut outputs = prepare_outputs(&card)?;
62 let mut req = linux_drm::modeset::AtomicRequest::new();
63
64 for output in &mut outputs {
65 println!("preparing output {output:#?}");
66 let conn = card.connector_state(output.conn_id)?;
67
68 let mode = &output.mode;
69 let mode_name = String::from_utf8_lossy(&mode.name);
70 println!(
71 "{:?} connector uses {mode_name} ({}x{}@{}Hz)",
72 conn.connector_type, mode.hdisplay, mode.vdisplay, mode.vrefresh,
73 );
74
75 let rows = output.db.height() as usize;
76 let pitch = output.db.pitch() as usize;
77 let data = output.db.buffer_mut();
78 for i in 0..rows {
79 if (i % 8) > 3 {
80 let row = &mut data[(i * pitch)..(i * pitch) + pitch];
81 row.fill(0xff);
82 }
83 }
84
85 println!(
86 "configuring CRTC {:?} for framebuffer {:?} and mode {mode_name} on connector {:?}",
87 output.crtc_id,
88 output.db.framebuffer_id(),
89 conn.id
90 );
91
92 req.set_property(
93 ObjectId::Connector(output.conn_id),
94 output.conn_prop_ids.crtc_id,
95 output.crtc_id,
96 );
97 req.set_property(
98 ObjectId::Crtc(output.crtc_id),
99 output.crtc_prop_ids.active,
100 true,
101 );
102 req.set_property(
103 ObjectId::Plane(output.plane_id),
104 output.plane_prop_ids.fb_id,
105 output.db.framebuffer_id(),
106 );
107 req.set_property(
108 ObjectId::Plane(output.plane_id),
109 output.plane_prop_ids.crtc_id,
110 output.crtc_id,
111 );
112 req.set_property(
113 ObjectId::Plane(output.plane_id),
114 output.plane_prop_ids.crtc_x,
115 0,
116 );
117 req.set_property(
118 ObjectId::Plane(output.plane_id),
119 output.plane_prop_ids.crtc_y,
120 0,
121 );
122 req.set_property(
123 ObjectId::Plane(output.plane_id),
124 output.plane_prop_ids.crtc_w,
125 output.db.width(),
126 );
127 req.set_property(
128 ObjectId::Plane(output.plane_id),
129 output.plane_prop_ids.crtc_h,
130 output.db.height(),
131 );
132 req.set_property(
133 ObjectId::Plane(output.plane_id),
134 output.plane_prop_ids.src_x,
135 0,
136 );
137 req.set_property(
138 ObjectId::Plane(output.plane_id),
139 output.plane_prop_ids.src_y,
140 0,
141 );
142 req.set_property(
143 ObjectId::Plane(output.plane_id),
144 output.plane_prop_ids.src_w,
145 (output.db.width() as u64) << 16,
146 );
147 req.set_property(
148 ObjectId::Plane(output.plane_id),
149 output.plane_prop_ids.src_h,
150 (output.db.height() as u64) << 16,
151 );
152 }
153
154 println!("atomic commit {req:#?}");
155 card.atomic_commit(
156 &req,
157 AtomicCommitFlags::ALLOW_MODESET | AtomicCommitFlags::PAGE_FLIP_EVENT,
158 0,
159 )?;
160
161 let mut evt_buf = vec![0_u8; 1024];
162 loop {
163 println!("waiting for events (send SIGINT to exit)");
164 for evt in card.read_events(&mut evt_buf)? {
165 println!("event {evt:?}");
166 match evt {
167 DrmEvent::Generic(GenericDrmEvent::FlipComplete(_)) => {
168 // In a real program this would be a time place to draw the next frame
169 // for the reported crtc.
170 }
171 _ => {
172 // Ignore any unrecognized event types.
173 }
174 }
175 }
176 }
177}
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.