1 | //! Modesetting operations that the DRM subsystem exposes. |
2 | //! |
3 | //! # Summary |
4 | //! |
5 | //! The DRM subsystem provides Kernel Modesetting (KMS) functionality by |
6 | //! exposing the following resource types: |
7 | //! |
8 | //! * FrameBuffer - Specific to an individual process, these wrap around generic |
9 | //! GPU buffers so that they can be attached to a Plane. |
10 | //! |
11 | //! * Planes - Dedicated memory objects which contain a buffer that can then be |
12 | //! scanned out by a CRTC. There exist a few different types of planes depending |
13 | //! on the use case. |
14 | //! |
15 | //! * CRTC - Scanout engines that read pixel data from a Plane and sends it to |
16 | //! a Connector. Each CRTC has at least one Primary Plane. |
17 | //! |
18 | //! * Connector - Represents the physical output, such as a DisplayPort or |
19 | //! VGA connector. |
20 | //! |
21 | //! * Encoder - Encodes pixel data from a CRTC into something a Connector can |
22 | //! understand. |
23 | //! |
24 | //! Further details on each resource can be found in their respective modules. |
25 | //! |
26 | //! # Usage |
27 | //! |
28 | //! To begin using modesetting functionality, the [`Device`] trait |
29 | //! must be implemented on top of the basic [`super::Device`] trait. |
30 | |
31 | use drm_ffi as ffi; |
32 | use drm_ffi::result::SystemError; |
33 | use drm_fourcc::{DrmFourcc, DrmModifier, UnrecognizedFourcc}; |
34 | |
35 | use bytemuck::allocation::TransparentWrapperAlloc; |
36 | |
37 | pub mod atomic; |
38 | pub mod connector; |
39 | pub mod crtc; |
40 | pub mod dumbbuffer; |
41 | pub mod encoder; |
42 | pub mod framebuffer; |
43 | pub mod plane; |
44 | |
45 | pub mod property; |
46 | |
47 | use self::dumbbuffer::*; |
48 | use buffer; |
49 | |
50 | use super::util::*; |
51 | |
52 | use std::convert::TryFrom; |
53 | use std::iter::Zip; |
54 | use std::mem; |
55 | use std::num::NonZeroUsize; |
56 | use std::ops::RangeBounds; |
57 | use std::os::unix::io::{AsRawFd, RawFd}; |
58 | use std::time::Duration; |
59 | |
60 | use core::num::NonZeroU32; |
61 | |
62 | /// Raw handle for a drm resource |
63 | pub type RawResourceHandle = NonZeroU32; |
64 | |
65 | /// Handle for a drm resource |
66 | pub trait ResourceHandle: |
67 | From<RawResourceHandle> + Into<RawResourceHandle> + Into<u32> + Copy + Sized |
68 | { |
69 | /// Associated encoded object type |
70 | const FFI_TYPE: u32; |
71 | } |
72 | |
73 | /// Convert from a raw drm object value to a typed Handle |
74 | /// |
75 | /// Note: This does no verification on the validity of the original value |
76 | pub fn from_u32<T: From<RawResourceHandle>>(raw: u32) -> Option<T> { |
77 | RawResourceHandle::new(raw).map(T::from) |
78 | } |
79 | |
80 | /// This trait should be implemented by any object that acts as a DRM device and |
81 | /// provides modesetting functionality. |
82 | /// |
83 | /// Like the parent [`super::Device`] trait, this crate does not |
84 | /// provide a concrete object for this trait. |
85 | /// |
86 | /// # Example |
87 | /// ```ignore |
88 | /// use drm::control::Device as ControlDevice; |
89 | /// |
90 | /// /// Assuming the [`Card`] wrapper already implements [`drm::Device`] |
91 | /// impl ControlDevice for Card {} |
92 | /// ``` |
93 | pub trait Device: super::Device { |
94 | /// Gets the set of resource handles that this device currently controls |
95 | fn resource_handles(&self) -> Result<ResourceHandles, SystemError> { |
96 | let mut fbs = Vec::new(); |
97 | let mut crtcs = Vec::new(); |
98 | let mut connectors = Vec::new(); |
99 | let mut encoders = Vec::new(); |
100 | |
101 | let ffi_res = ffi::mode::get_resources( |
102 | self.as_fd().as_raw_fd(), |
103 | Some(&mut fbs), |
104 | Some(&mut crtcs), |
105 | Some(&mut connectors), |
106 | Some(&mut encoders), |
107 | )?; |
108 | |
109 | let res = unsafe { |
110 | ResourceHandles { |
111 | fbs: transmute_vec_from_u32(fbs), |
112 | crtcs: transmute_vec_from_u32(crtcs), |
113 | connectors: transmute_vec_from_u32(connectors), |
114 | encoders: transmute_vec_from_u32(encoders), |
115 | width: (ffi_res.min_width, ffi_res.max_width), |
116 | height: (ffi_res.min_height, ffi_res.max_height), |
117 | } |
118 | }; |
119 | |
120 | Ok(res) |
121 | } |
122 | |
123 | /// Gets the set of plane handles that this device currently has |
124 | fn plane_handles(&self) -> Result<Vec<plane::Handle>, SystemError> { |
125 | let mut planes = Vec::new(); |
126 | let _ = ffi::mode::get_plane_resources(self.as_fd().as_raw_fd(), Some(&mut planes))?; |
127 | Ok(unsafe { transmute_vec_from_u32(planes) }) |
128 | } |
129 | |
130 | /// Returns information about a specific connector |
131 | /// |
132 | /// ## Force-probing |
133 | /// |
134 | /// If `force_probe` is set to `true` and the DRM client is the current DRM master, |
135 | /// the kernel will perform a forced probe on the connector to refresh the connector status, modes and EDID. |
136 | /// A forced-probe can be slow, might cause flickering and the ioctl will block. |
137 | /// |
138 | /// - User needs to force-probe connectors to ensure their metadata is up-to-date at startup and after receiving a hot-plug event. |
139 | /// - User may perform a forced-probe when the user explicitly requests it. |
140 | /// - User shouldn’t perform a forced-probe in other situations. |
141 | fn get_connector( |
142 | &self, |
143 | handle: connector::Handle, |
144 | force_probe: bool, |
145 | ) -> Result<connector::Info, SystemError> { |
146 | // Maximum number of encoders is 3 due to kernel restrictions |
147 | let mut encoders = Vec::new(); |
148 | let mut modes = Vec::new(); |
149 | |
150 | let ffi_info = ffi::mode::get_connector( |
151 | self.as_fd().as_raw_fd(), |
152 | handle.into(), |
153 | None, |
154 | None, |
155 | Some(&mut modes), |
156 | Some(&mut encoders), |
157 | force_probe, |
158 | )?; |
159 | |
160 | let connector = connector::Info { |
161 | handle, |
162 | interface: connector::Interface::from(ffi_info.connector_type), |
163 | interface_id: ffi_info.connector_type_id, |
164 | connection: connector::State::from(ffi_info.connection), |
165 | size: match (ffi_info.mm_width, ffi_info.mm_height) { |
166 | (0, 0) => None, |
167 | (x, y) => Some((x, y)), |
168 | }, |
169 | modes: Mode::wrap_vec(modes), |
170 | encoders: unsafe { transmute_vec_from_u32(encoders) }, |
171 | curr_enc: unsafe { mem::transmute(ffi_info.encoder_id) }, |
172 | }; |
173 | |
174 | Ok(connector) |
175 | } |
176 | |
177 | /// Returns information about a specific encoder |
178 | fn get_encoder(&self, handle: encoder::Handle) -> Result<encoder::Info, SystemError> { |
179 | let info = ffi::mode::get_encoder(self.as_fd().as_raw_fd(), handle.into())?; |
180 | |
181 | let enc = encoder::Info { |
182 | handle, |
183 | enc_type: encoder::Kind::from(info.encoder_type), |
184 | crtc: from_u32(info.crtc_id), |
185 | pos_crtcs: info.possible_crtcs, |
186 | pos_clones: info.possible_clones, |
187 | }; |
188 | |
189 | Ok(enc) |
190 | } |
191 | |
192 | /// Returns information about a specific CRTC |
193 | fn get_crtc(&self, handle: crtc::Handle) -> Result<crtc::Info, SystemError> { |
194 | let info = ffi::mode::get_crtc(self.as_fd().as_raw_fd(), handle.into())?; |
195 | |
196 | let crtc = crtc::Info { |
197 | handle, |
198 | position: (info.x, info.y), |
199 | mode: match info.mode_valid { |
200 | 0 => None, |
201 | _ => Some(Mode::from(info.mode)), |
202 | }, |
203 | fb: from_u32(info.fb_id), |
204 | gamma_length: info.gamma_size, |
205 | }; |
206 | |
207 | Ok(crtc) |
208 | } |
209 | |
210 | /// Set CRTC state |
211 | fn set_crtc( |
212 | &self, |
213 | handle: crtc::Handle, |
214 | framebuffer: Option<framebuffer::Handle>, |
215 | pos: (u32, u32), |
216 | conns: &[connector::Handle], |
217 | mode: Option<Mode>, |
218 | ) -> Result<(), SystemError> { |
219 | let _info = ffi::mode::set_crtc( |
220 | self.as_fd().as_raw_fd(), |
221 | handle.into(), |
222 | framebuffer.map(Into::into).unwrap_or(0), |
223 | pos.0, |
224 | pos.1, |
225 | unsafe { &*(conns as *const _ as *const [u32]) }, |
226 | mode.map(|m| m.into()), |
227 | )?; |
228 | |
229 | Ok(()) |
230 | } |
231 | |
232 | /// Returns information about a specific framebuffer |
233 | fn get_framebuffer( |
234 | &self, |
235 | handle: framebuffer::Handle, |
236 | ) -> Result<framebuffer::Info, SystemError> { |
237 | let info = ffi::mode::get_framebuffer(self.as_fd().as_raw_fd(), handle.into())?; |
238 | |
239 | let fb = framebuffer::Info { |
240 | handle, |
241 | size: (info.width, info.height), |
242 | pitch: info.pitch, |
243 | bpp: info.bpp, |
244 | depth: info.depth, |
245 | buffer: from_u32(info.handle), |
246 | }; |
247 | |
248 | Ok(fb) |
249 | } |
250 | |
251 | /// Returns information about a specific framebuffer (with modifiers) |
252 | fn get_planar_framebuffer( |
253 | &self, |
254 | handle: framebuffer::Handle, |
255 | ) -> Result<framebuffer::PlanarInfo, SystemError> { |
256 | let info = ffi::mode::get_framebuffer2(self.as_fd().as_raw_fd(), handle.into())?; |
257 | |
258 | let pixel_format = match DrmFourcc::try_from(info.pixel_format) { |
259 | Ok(pixel_format) => pixel_format, |
260 | Err(UnrecognizedFourcc(_)) => return Err(SystemError::UnknownFourcc), |
261 | }; |
262 | |
263 | let fb = framebuffer::PlanarInfo { |
264 | handle, |
265 | size: (info.width, info.height), |
266 | pixel_format, |
267 | flags: info.flags, |
268 | buffers: bytemuck::cast(info.handles), |
269 | pitches: info.pitches, |
270 | offsets: info.offsets, |
271 | modifier: info.modifier.map(DrmModifier::from), |
272 | }; |
273 | |
274 | Ok(fb) |
275 | } |
276 | |
277 | /// Add a new framebuffer |
278 | fn add_framebuffer<B>( |
279 | &self, |
280 | buffer: &B, |
281 | depth: u32, |
282 | bpp: u32, |
283 | ) -> Result<framebuffer::Handle, SystemError> |
284 | where |
285 | B: buffer::Buffer + ?Sized, |
286 | { |
287 | let (w, h) = buffer.size(); |
288 | let info = ffi::mode::add_fb( |
289 | self.as_fd().as_raw_fd(), |
290 | w, |
291 | h, |
292 | buffer.pitch(), |
293 | bpp, |
294 | depth, |
295 | buffer.handle().into(), |
296 | )?; |
297 | |
298 | Ok(from_u32(info.fb_id).unwrap()) |
299 | } |
300 | |
301 | /// Add framebuffer (with modifiers) |
302 | fn add_planar_framebuffer<B>( |
303 | &self, |
304 | planar_buffer: &B, |
305 | modifiers: &[Option<DrmModifier>; 4], |
306 | flags: u32, |
307 | ) -> Result<framebuffer::Handle, SystemError> |
308 | where |
309 | B: buffer::PlanarBuffer + ?Sized, |
310 | { |
311 | let (w, h) = planar_buffer.size(); |
312 | let opt_handles = planar_buffer.handles(); |
313 | |
314 | let handles = bytemuck::cast(opt_handles); |
315 | let mods = [ |
316 | modifiers[0].map(Into::<u64>::into).unwrap_or(0), |
317 | modifiers[1].map(Into::<u64>::into).unwrap_or(0), |
318 | modifiers[2].map(Into::<u64>::into).unwrap_or(0), |
319 | modifiers[3].map(Into::<u64>::into).unwrap_or(0), |
320 | ]; |
321 | |
322 | let info = ffi::mode::add_fb2( |
323 | self.as_fd().as_raw_fd(), |
324 | w, |
325 | h, |
326 | planar_buffer.format() as u32, |
327 | &handles, |
328 | &planar_buffer.pitches(), |
329 | &planar_buffer.offsets(), |
330 | &mods, |
331 | flags, |
332 | )?; |
333 | |
334 | Ok(from_u32(info.fb_id).unwrap()) |
335 | } |
336 | |
337 | /// Mark parts of a framebuffer dirty |
338 | fn dirty_framebuffer( |
339 | &self, |
340 | handle: framebuffer::Handle, |
341 | clips: &[ClipRect], |
342 | ) -> Result<(), SystemError> { |
343 | ffi::mode::dirty_fb(self.as_fd().as_raw_fd(), handle.into(), clips)?; |
344 | Ok(()) |
345 | } |
346 | |
347 | /// Destroy a framebuffer |
348 | fn destroy_framebuffer(&self, handle: framebuffer::Handle) -> Result<(), SystemError> { |
349 | ffi::mode::rm_fb(self.as_fd().as_raw_fd(), handle.into()) |
350 | } |
351 | |
352 | /// Returns information about a specific plane |
353 | fn get_plane(&self, handle: plane::Handle) -> Result<plane::Info, SystemError> { |
354 | let mut formats = Vec::new(); |
355 | |
356 | let info = |
357 | ffi::mode::get_plane(self.as_fd().as_raw_fd(), handle.into(), Some(&mut formats))?; |
358 | |
359 | let plane = plane::Info { |
360 | handle, |
361 | crtc: from_u32(info.crtc_id), |
362 | fb: from_u32(info.fb_id), |
363 | pos_crtcs: info.possible_crtcs, |
364 | formats: unsafe { transmute_vec_from_u32(formats) }, |
365 | }; |
366 | |
367 | Ok(plane) |
368 | } |
369 | |
370 | /// Set plane state. |
371 | /// |
372 | /// Providing no framebuffer clears the plane. |
373 | fn set_plane( |
374 | &self, |
375 | handle: plane::Handle, |
376 | crtc: crtc::Handle, |
377 | framebuffer: Option<framebuffer::Handle>, |
378 | flags: u32, |
379 | crtc_rect: (i32, i32, u32, u32), |
380 | src_rect: (u32, u32, u32, u32), |
381 | ) -> Result<(), SystemError> { |
382 | let _info = ffi::mode::set_plane( |
383 | self.as_fd().as_raw_fd(), |
384 | handle.into(), |
385 | crtc.into(), |
386 | framebuffer.map(Into::into).unwrap_or(0), |
387 | flags, |
388 | crtc_rect.0, |
389 | crtc_rect.1, |
390 | crtc_rect.2, |
391 | crtc_rect.3, |
392 | src_rect.0, |
393 | src_rect.1, |
394 | src_rect.2, |
395 | src_rect.3, |
396 | )?; |
397 | |
398 | Ok(()) |
399 | } |
400 | |
401 | /// Returns information about a specific property. |
402 | fn get_property(&self, handle: property::Handle) -> Result<property::Info, SystemError> { |
403 | let mut values = Vec::new(); |
404 | let mut enums = Vec::new(); |
405 | |
406 | let info = ffi::mode::get_property( |
407 | self.as_fd().as_raw_fd(), |
408 | handle.into(), |
409 | Some(&mut values), |
410 | Some(&mut enums), |
411 | )?; |
412 | |
413 | let flags = ModePropFlags::from_bits_truncate(info.flags); |
414 | |
415 | let val_type = { |
416 | use self::property::ValueType; |
417 | |
418 | if flags.contains(ModePropFlags::RANGE) { |
419 | let min = values[0]; |
420 | let max = values[1]; |
421 | |
422 | match (min, max) { |
423 | (0, 1) => ValueType::Boolean, |
424 | (min, max) => ValueType::UnsignedRange(min, max), |
425 | } |
426 | } else if flags.contains(ModePropFlags::SIGNED_RANGE) { |
427 | let min = values[0]; |
428 | let max = values[1]; |
429 | |
430 | ValueType::SignedRange(min as i64, max as i64) |
431 | } else if flags.contains(ModePropFlags::ENUM) { |
432 | let enum_values = self::property::EnumValues { |
433 | values, |
434 | enums: property::EnumValue::wrap_vec(enums), |
435 | }; |
436 | |
437 | ValueType::Enum(enum_values) |
438 | } else if flags.contains(ModePropFlags::BLOB) { |
439 | ValueType::Blob |
440 | } else if flags.contains(ModePropFlags::BITMASK) { |
441 | ValueType::Bitmask |
442 | } else if flags.contains(ModePropFlags::OBJECT) { |
443 | match values[0] as u32 { |
444 | ffi::DRM_MODE_OBJECT_CRTC => ValueType::CRTC, |
445 | ffi::DRM_MODE_OBJECT_CONNECTOR => ValueType::Connector, |
446 | ffi::DRM_MODE_OBJECT_ENCODER => ValueType::Encoder, |
447 | ffi::DRM_MODE_OBJECT_FB => ValueType::Framebuffer, |
448 | ffi::DRM_MODE_OBJECT_PLANE => ValueType::Plane, |
449 | ffi::DRM_MODE_OBJECT_PROPERTY => ValueType::Property, |
450 | ffi::DRM_MODE_OBJECT_BLOB => ValueType::Blob, |
451 | ffi::DRM_MODE_OBJECT_ANY => ValueType::Object, |
452 | _ => ValueType::Unknown, |
453 | } |
454 | } else { |
455 | ValueType::Unknown |
456 | } |
457 | }; |
458 | |
459 | let property = property::Info { |
460 | handle, |
461 | val_type, |
462 | mutable: !flags.contains(ModePropFlags::IMMUTABLE), |
463 | atomic: flags.contains(ModePropFlags::ATOMIC), |
464 | info, |
465 | }; |
466 | |
467 | Ok(property) |
468 | } |
469 | |
470 | /// Sets a property for a specific resource. |
471 | fn set_property<T: ResourceHandle>( |
472 | &self, |
473 | handle: T, |
474 | prop: property::Handle, |
475 | value: property::RawValue, |
476 | ) -> Result<(), SystemError> { |
477 | ffi::mode::set_property( |
478 | self.as_fd().as_raw_fd(), |
479 | prop.into(), |
480 | handle.into(), |
481 | T::FFI_TYPE, |
482 | value, |
483 | )?; |
484 | |
485 | Ok(()) |
486 | } |
487 | |
488 | /// Create a property blob value from a given data blob |
489 | fn create_property_blob<T>(&self, data: &T) -> Result<property::Value<'static>, SystemError> { |
490 | let data = unsafe { |
491 | std::slice::from_raw_parts_mut(data as *const _ as *mut u8, mem::size_of::<T>()) |
492 | }; |
493 | let blob = ffi::mode::create_property_blob(self.as_fd().as_raw_fd(), data)?; |
494 | |
495 | Ok(property::Value::Blob(blob.blob_id.into())) |
496 | } |
497 | |
498 | /// Get a property blob's data |
499 | fn get_property_blob(&self, blob: u64) -> Result<Vec<u8>, SystemError> { |
500 | let mut data = Vec::new(); |
501 | let _ = |
502 | ffi::mode::get_property_blob(self.as_fd().as_raw_fd(), blob as u32, Some(&mut data))?; |
503 | Ok(data) |
504 | } |
505 | |
506 | /// Destroy a given property blob value |
507 | fn destroy_property_blob(&self, blob: u64) -> Result<(), SystemError> { |
508 | ffi::mode::destroy_property_blob(self.as_fd().as_raw_fd(), blob as u32)?; |
509 | |
510 | Ok(()) |
511 | } |
512 | |
513 | /// Returns the set of [`Mode`]s that a particular connector supports. |
514 | fn get_modes(&self, handle: connector::Handle) -> Result<Vec<Mode>, SystemError> { |
515 | let mut modes = Vec::new(); |
516 | |
517 | let _ffi_info = ffi::mode::get_connector( |
518 | self.as_fd().as_raw_fd(), |
519 | handle.into(), |
520 | None, |
521 | None, |
522 | Some(&mut modes), |
523 | None, |
524 | false, |
525 | )?; |
526 | |
527 | Ok(Mode::wrap_vec(modes)) |
528 | } |
529 | |
530 | /// Gets a list of property handles and values for this resource. |
531 | fn get_properties<T: ResourceHandle>( |
532 | &self, |
533 | handle: T, |
534 | ) -> Result<PropertyValueSet, SystemError> { |
535 | let mut prop_ids = Vec::new(); |
536 | let mut prop_vals = Vec::new(); |
537 | |
538 | ffi::mode::get_properties( |
539 | self.as_fd().as_raw_fd(), |
540 | handle.into(), |
541 | T::FFI_TYPE, |
542 | Some(&mut prop_ids), |
543 | Some(&mut prop_vals), |
544 | )?; |
545 | |
546 | let prop_val_set = PropertyValueSet { |
547 | prop_ids: unsafe { transmute_vec_from_u32(prop_ids) }, |
548 | prop_vals, |
549 | }; |
550 | |
551 | Ok(prop_val_set) |
552 | } |
553 | |
554 | /// Receive the currently set gamma ramp of a crtc |
555 | fn get_gamma( |
556 | &self, |
557 | crtc: crtc::Handle, |
558 | red: &mut [u16], |
559 | green: &mut [u16], |
560 | blue: &mut [u16], |
561 | ) -> Result<(), SystemError> { |
562 | let crtc_info = self.get_crtc(crtc)?; |
563 | if crtc_info.gamma_length as usize > red.len() |
564 | || crtc_info.gamma_length as usize > green.len() |
565 | || crtc_info.gamma_length as usize > blue.len() |
566 | { |
567 | return Err(SystemError::InvalidArgument); |
568 | } |
569 | |
570 | ffi::mode::get_gamma( |
571 | self.as_fd().as_raw_fd(), |
572 | crtc.into(), |
573 | crtc_info.gamma_length as usize, |
574 | red, |
575 | green, |
576 | blue, |
577 | )?; |
578 | |
579 | Ok(()) |
580 | } |
581 | |
582 | /// Set a gamma ramp for the given crtc |
583 | fn set_gamma( |
584 | &self, |
585 | crtc: crtc::Handle, |
586 | red: &[u16], |
587 | green: &[u16], |
588 | blue: &[u16], |
589 | ) -> Result<(), SystemError> { |
590 | let crtc_info = self.get_crtc(crtc)?; |
591 | if crtc_info.gamma_length as usize > red.len() |
592 | || crtc_info.gamma_length as usize > green.len() |
593 | || crtc_info.gamma_length as usize > blue.len() |
594 | { |
595 | return Err(SystemError::InvalidArgument); |
596 | } |
597 | |
598 | ffi::mode::set_gamma( |
599 | self.as_fd().as_raw_fd(), |
600 | crtc.into(), |
601 | crtc_info.gamma_length as usize, |
602 | red, |
603 | green, |
604 | blue, |
605 | )?; |
606 | |
607 | Ok(()) |
608 | } |
609 | |
610 | /// Open a GEM buffer handle by name |
611 | fn open_buffer(&self, name: buffer::Name) -> Result<buffer::Handle, SystemError> { |
612 | let info = drm_ffi::gem::open(self.as_fd().as_raw_fd(), name.into())?; |
613 | Ok(from_u32(info.handle).unwrap()) |
614 | } |
615 | |
616 | /// Close a GEM buffer handle |
617 | fn close_buffer(&self, handle: buffer::Handle) -> Result<(), SystemError> { |
618 | let _info = drm_ffi::gem::close(self.as_fd().as_raw_fd(), handle.into())?; |
619 | Ok(()) |
620 | } |
621 | |
622 | /// Create a new dumb buffer with a given size and pixel format |
623 | fn create_dumb_buffer( |
624 | &self, |
625 | size: (u32, u32), |
626 | format: buffer::DrmFourcc, |
627 | bpp: u32, |
628 | ) -> Result<DumbBuffer, SystemError> { |
629 | let info = |
630 | drm_ffi::mode::dumbbuffer::create(self.as_fd().as_raw_fd(), size.0, size.1, bpp, 0)?; |
631 | |
632 | let dumb = DumbBuffer { |
633 | size: (info.width, info.height), |
634 | length: info.size as usize, |
635 | format, |
636 | pitch: info.pitch, |
637 | handle: from_u32(info.handle).unwrap(), |
638 | }; |
639 | |
640 | Ok(dumb) |
641 | } |
642 | /// Map the buffer for access |
643 | fn map_dumb_buffer<'a>( |
644 | &self, |
645 | buffer: &'a mut DumbBuffer, |
646 | ) -> Result<DumbMapping<'a>, SystemError> { |
647 | let info = |
648 | drm_ffi::mode::dumbbuffer::map(self.as_fd().as_raw_fd(), buffer.handle.into(), 0, 0)?; |
649 | |
650 | let map = { |
651 | use nix::sys::mman; |
652 | let prot = mman::ProtFlags::PROT_READ | mman::ProtFlags::PROT_WRITE; |
653 | let flags = mman::MapFlags::MAP_SHARED; |
654 | let length = NonZeroUsize::new(buffer.length).ok_or(SystemError::InvalidArgument)?; |
655 | let fd = self.as_fd().as_raw_fd(); |
656 | let offset = info.offset as _; |
657 | unsafe { mman::mmap(None, length, prot, flags, fd, offset)? } |
658 | }; |
659 | |
660 | let mapping = DumbMapping { |
661 | _phantom: ::std::marker::PhantomData, |
662 | map: unsafe { ::std::slice::from_raw_parts_mut(map as *mut _, buffer.length) }, |
663 | }; |
664 | |
665 | Ok(mapping) |
666 | } |
667 | |
668 | /// Free the memory resources of a dumb buffer |
669 | fn destroy_dumb_buffer(&self, buffer: DumbBuffer) -> Result<(), SystemError> { |
670 | let _info = |
671 | drm_ffi::mode::dumbbuffer::destroy(self.as_fd().as_raw_fd(), buffer.handle.into())?; |
672 | |
673 | Ok(()) |
674 | } |
675 | |
676 | /// Sets a hardware-cursor on the given crtc with the image of a given buffer |
677 | /// |
678 | /// A buffer argument of [`None`] will clear the cursor. |
679 | #[deprecated (note = "Usage of deprecated ioctl set_cursor: use a cursor plane instead" )] |
680 | #[allow (deprecated)] |
681 | fn set_cursor<B>(&self, crtc: crtc::Handle, buffer: Option<&B>) -> Result<(), SystemError> |
682 | where |
683 | B: buffer::Buffer + ?Sized, |
684 | { |
685 | let (id, w, h) = buffer |
686 | .map(|buf| { |
687 | let (w, h) = buf.size(); |
688 | (buf.handle().into(), w, h) |
689 | }) |
690 | .unwrap_or((0, 0, 0)); |
691 | drm_ffi::mode::set_cursor(self.as_fd().as_raw_fd(), crtc.into(), id, w, h)?; |
692 | |
693 | Ok(()) |
694 | } |
695 | |
696 | /// Sets a hardware-cursor on the given crtc with the image of a given buffer |
697 | /// and a hotspot marking the click point of the cursor. |
698 | /// |
699 | /// A buffer argument of [`None`] will clear the cursor. |
700 | #[deprecated (note = "Usage of deprecated ioctl set_cursor2: use a cursor plane instead" )] |
701 | #[allow (deprecated)] |
702 | fn set_cursor2<B>( |
703 | &self, |
704 | crtc: crtc::Handle, |
705 | buffer: Option<&B>, |
706 | hotspot: (i32, i32), |
707 | ) -> Result<(), SystemError> |
708 | where |
709 | B: buffer::Buffer + ?Sized, |
710 | { |
711 | let (id, w, h) = buffer |
712 | .map(|buf| { |
713 | let (w, h) = buf.size(); |
714 | (buf.handle().into(), w, h) |
715 | }) |
716 | .unwrap_or((0, 0, 0)); |
717 | drm_ffi::mode::set_cursor2( |
718 | self.as_fd().as_raw_fd(), |
719 | crtc.into(), |
720 | id, |
721 | w, |
722 | h, |
723 | hotspot.0, |
724 | hotspot.1, |
725 | )?; |
726 | |
727 | Ok(()) |
728 | } |
729 | |
730 | /// Moves a set cursor on a given crtc |
731 | #[deprecated (note = "Usage of deprecated ioctl move_cursor: use a cursor plane instead" )] |
732 | #[allow (deprecated)] |
733 | fn move_cursor(&self, crtc: crtc::Handle, pos: (i32, i32)) -> Result<(), SystemError> { |
734 | drm_ffi::mode::move_cursor(self.as_fd().as_raw_fd(), crtc.into(), pos.0, pos.1)?; |
735 | |
736 | Ok(()) |
737 | } |
738 | |
739 | /// Request an atomic commit with given flags and property-value pair for a list of objects. |
740 | fn atomic_commit( |
741 | &self, |
742 | flags: AtomicCommitFlags, |
743 | mut req: atomic::AtomicModeReq, |
744 | ) -> Result<(), SystemError> { |
745 | drm_ffi::mode::atomic_commit( |
746 | self.as_fd().as_raw_fd(), |
747 | flags.bits(), |
748 | unsafe { &mut *(&mut *req.objects as *mut _ as *mut [u32]) }, |
749 | &mut req.count_props_per_object, |
750 | unsafe { &mut *(&mut *req.props as *mut _ as *mut [u32]) }, |
751 | &mut req.values, |
752 | ) |
753 | } |
754 | |
755 | /// Convert a prime file descriptor to a GEM buffer handle |
756 | fn prime_fd_to_buffer(&self, fd: RawFd) -> Result<buffer::Handle, SystemError> { |
757 | let info = ffi::gem::fd_to_handle(self.as_fd().as_raw_fd(), fd)?; |
758 | Ok(from_u32(info.handle).unwrap()) |
759 | } |
760 | |
761 | /// Convert a prime file descriptor to a GEM buffer handle |
762 | fn buffer_to_prime_fd(&self, handle: buffer::Handle, flags: u32) -> Result<RawFd, SystemError> { |
763 | let info = ffi::gem::handle_to_fd(self.as_fd().as_raw_fd(), handle.into(), flags)?; |
764 | Ok(info.fd) |
765 | } |
766 | |
767 | /// Queue a page flip on the given crtc |
768 | fn page_flip( |
769 | &self, |
770 | handle: crtc::Handle, |
771 | framebuffer: framebuffer::Handle, |
772 | flags: PageFlipFlags, |
773 | target_sequence: Option<PageFlipTarget>, |
774 | ) -> Result<(), SystemError> { |
775 | let mut flags = flags.bits(); |
776 | |
777 | let sequence = match target_sequence { |
778 | Some(PageFlipTarget::Absolute(n)) => { |
779 | flags |= ffi::drm_sys::DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE; |
780 | n |
781 | } |
782 | Some(PageFlipTarget::Relative(n)) => { |
783 | flags |= ffi::drm_sys::DRM_MODE_PAGE_FLIP_TARGET_RELATIVE; |
784 | n |
785 | } |
786 | None => 0, |
787 | }; |
788 | |
789 | ffi::mode::page_flip( |
790 | self.as_fd().as_raw_fd(), |
791 | handle.into(), |
792 | framebuffer.into(), |
793 | flags, |
794 | sequence, |
795 | )?; |
796 | |
797 | Ok(()) |
798 | } |
799 | |
800 | /// Receive pending events |
801 | fn receive_events(&self) -> Result<Events, SystemError> |
802 | where |
803 | Self: Sized, |
804 | { |
805 | let mut event_buf: [u8; 1024] = [0; 1024]; |
806 | let amount = ::nix::unistd::read(self.as_fd().as_raw_fd(), &mut event_buf)?; |
807 | |
808 | Ok(Events { |
809 | event_buf, |
810 | amount, |
811 | i: 0, |
812 | }) |
813 | } |
814 | } |
815 | |
816 | bitflags::bitflags! { |
817 | /// Flags to alter the behaviour of a page flip |
818 | /// |
819 | /// Limited to the values in [`ffi::drm_sys::DRM_MODE_PAGE_FLIP_FLAGS`], |
820 | /// minus [`ffi::drm_sys::DRM_MODE_PAGE_FLIP_TARGET`] bits which are |
821 | /// passed through [`PageFlipTarget`]. |
822 | pub struct PageFlipFlags : u32 { |
823 | /// Request a vblank event on page flip |
824 | const EVENT = ffi::drm_sys::DRM_MODE_PAGE_FLIP_EVENT; |
825 | /// Request page flip as soon as possible, not waiting for vblank |
826 | const ASYNC = ffi::drm_sys::DRM_MODE_PAGE_FLIP_ASYNC; |
827 | } |
828 | } |
829 | |
830 | /// Target to alter the sequence of page flips |
831 | /// |
832 | /// These represent the [`ffi::drm_sys::DRM_MODE_PAGE_FLIP_TARGET`] bits |
833 | /// of [`PageFlipFlags`] wrapped in a regular `enum` due to their |
834 | /// mutual-exclusiveness. |
835 | #[derive (Debug, Clone, Copy, PartialEq, Eq, Hash)] |
836 | pub enum PageFlipTarget { |
837 | /// Absolute Vblank Sequence |
838 | Absolute(u32), |
839 | /// Relative Vblank Sequence (to the current, when calling) |
840 | Relative(u32), |
841 | } |
842 | |
843 | /// Iterator over [`Event`]s of a device. Create via [`Device::receive_events()`]. |
844 | pub struct Events { |
845 | event_buf: [u8; 1024], |
846 | amount: usize, |
847 | i: usize, |
848 | } |
849 | |
850 | /// An event from a device. |
851 | pub enum Event { |
852 | /// A vblank happened |
853 | Vblank(VblankEvent), |
854 | /// A page flip happened |
855 | PageFlip(PageFlipEvent), |
856 | /// Unknown event, raw data provided |
857 | Unknown(Vec<u8>), |
858 | } |
859 | |
860 | /// Vblank event |
861 | pub struct VblankEvent { |
862 | /// sequence of the frame |
863 | pub frame: u32, |
864 | /// time at which the vblank occurred |
865 | pub time: Duration, |
866 | /// crtc that did throw the event |
867 | pub crtc: crtc::Handle, |
868 | /// user data that was passed to wait_vblank |
869 | pub user_data: usize, |
870 | } |
871 | |
872 | /// Page Flip event |
873 | pub struct PageFlipEvent { |
874 | /// sequence of the frame |
875 | pub frame: u32, |
876 | /// duration between events |
877 | pub duration: Duration, |
878 | /// crtc that did throw the event |
879 | pub crtc: crtc::Handle, |
880 | } |
881 | |
882 | impl Iterator for Events { |
883 | type Item = Event; |
884 | |
885 | fn next(&mut self) -> Option<Event> { |
886 | if self.amount > 0 && self.i < self.amount { |
887 | let event = unsafe { &*(self.event_buf.as_ptr().add(self.i) as *const ffi::drm_event) }; |
888 | self.i += event.length as usize; |
889 | match event.type_ { |
890 | ffi::DRM_EVENT_VBLANK => { |
891 | let vblank_event = |
892 | unsafe { &*(event as *const _ as *const ffi::drm_event_vblank) }; |
893 | Some(Event::Vblank(VblankEvent { |
894 | frame: vblank_event.sequence, |
895 | time: Duration::new( |
896 | vblank_event.tv_sec as u64, |
897 | vblank_event.tv_usec * 1000, |
898 | ), |
899 | #[allow (clippy::unnecessary_cast)] |
900 | crtc: from_u32(vblank_event.crtc_id as u32).unwrap(), |
901 | user_data: vblank_event.user_data as usize, |
902 | })) |
903 | } |
904 | ffi::DRM_EVENT_FLIP_COMPLETE => { |
905 | let vblank_event = |
906 | unsafe { &*(event as *const _ as *const ffi::drm_event_vblank) }; |
907 | Some(Event::PageFlip(PageFlipEvent { |
908 | frame: vblank_event.sequence, |
909 | duration: Duration::new( |
910 | vblank_event.tv_sec as u64, |
911 | vblank_event.tv_usec * 1000, |
912 | ), |
913 | crtc: from_u32(if vblank_event.crtc_id != 0 { |
914 | vblank_event.crtc_id |
915 | } else { |
916 | vblank_event.user_data as u32 |
917 | }) |
918 | .unwrap(), |
919 | })) |
920 | } |
921 | _ => Some(Event::Unknown( |
922 | self.event_buf[self.i - (event.length as usize)..self.i].to_vec(), |
923 | )), |
924 | } |
925 | } else { |
926 | None |
927 | } |
928 | } |
929 | } |
930 | |
931 | /// The set of [`ResourceHandles`] that a |
932 | /// [`Device`] exposes. Excluding Plane resources. |
933 | #[derive (Debug, Clone, Hash, PartialEq, Eq)] |
934 | pub struct ResourceHandles { |
935 | /// Set of [`framebuffer::Handle`] |
936 | pub fbs: Vec<framebuffer::Handle>, |
937 | /// Set of [`crtc::Handle`] |
938 | pub crtcs: Vec<crtc::Handle>, |
939 | /// Set of [`connector::Handle`] |
940 | pub connectors: Vec<connector::Handle>, |
941 | /// Set of [`encoder::Handle`] |
942 | pub encoders: Vec<encoder::Handle>, |
943 | width: (u32, u32), |
944 | height: (u32, u32), |
945 | } |
946 | |
947 | impl ResourceHandles { |
948 | /// Returns the set of [`connector::Handle`] |
949 | pub fn connectors(&self) -> &[connector::Handle] { |
950 | &self.connectors |
951 | } |
952 | |
953 | /// Returns the set of [`encoder::Handle`] |
954 | pub fn encoders(&self) -> &[encoder::Handle] { |
955 | &self.encoders |
956 | } |
957 | |
958 | /// Returns the set of [`crtc::Handle`] |
959 | pub fn crtcs(&self) -> &[crtc::Handle] { |
960 | &self.crtcs |
961 | } |
962 | |
963 | /// Returns the set of [`framebuffer::Handle`] |
964 | pub fn framebuffers(&self) -> &[framebuffer::Handle] { |
965 | &self.fbs |
966 | } |
967 | |
968 | /// Returns the supported minimum and maximum width for framebuffers |
969 | pub fn supported_fb_width(&self) -> impl RangeBounds<u32> { |
970 | self.width.0..=self.width.1 |
971 | } |
972 | |
973 | /// Returns the supported minimum and maximum height for framebuffers |
974 | pub fn supported_fb_height(&self) -> impl RangeBounds<u32> { |
975 | self.height.0..=self.height.1 |
976 | } |
977 | |
978 | /// Apply a filter the all crtcs of these resources, resulting in a list of crtcs allowed. |
979 | pub fn filter_crtcs(&self, filter: CrtcListFilter) -> Vec<crtc::Handle> { |
980 | self.crtcs |
981 | .iter() |
982 | .enumerate() |
983 | .filter(|&(n, _)| (1 << n) & filter.0 != 0) |
984 | .map(|(_, &e)| e) |
985 | .collect() |
986 | } |
987 | } |
988 | |
989 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
990 | /// A filter that can be used with a [`ResourceHandles`] to determine the set of |
991 | /// Crtcs that can attach to a specific encoder. |
992 | pub struct CrtcListFilter(u32); |
993 | |
994 | /// Resolution and timing information for a display mode. |
995 | #[repr (transparent)] |
996 | #[derive (Copy, Clone, Hash, PartialEq, Eq, bytemuck::TransparentWrapper)] |
997 | pub struct Mode { |
998 | // We're using the FFI struct because the DRM API expects it when giving it |
999 | // to a CRTC or creating a blob from it. Rather than rearranging the fields |
1000 | // to convert to/from an abstracted type, just use the raw object. |
1001 | mode: ffi::drm_mode_modeinfo, |
1002 | } |
1003 | |
1004 | impl Mode { |
1005 | /// Returns the name of this mode. |
1006 | pub fn name(&self) -> &std::ffi::CStr { |
1007 | unsafe { std::ffi::CStr::from_ptr(&self.mode.name[0] as _) } |
1008 | } |
1009 | |
1010 | /// Returns the clock speed of this mode. |
1011 | pub fn clock(&self) -> u32 { |
1012 | self.mode.clock |
1013 | } |
1014 | |
1015 | /// Returns the size (resolution) of the mode. |
1016 | pub fn size(&self) -> (u16, u16) { |
1017 | (self.mode.hdisplay, self.mode.vdisplay) |
1018 | } |
1019 | |
1020 | /// Returns the horizontal sync start, end, and total. |
1021 | pub fn hsync(&self) -> (u16, u16, u16) { |
1022 | (self.mode.hsync_start, self.mode.hsync_end, self.mode.htotal) |
1023 | } |
1024 | |
1025 | /// Returns the vertical sync start, end, and total. |
1026 | pub fn vsync(&self) -> (u16, u16, u16) { |
1027 | (self.mode.vsync_start, self.mode.vsync_end, self.mode.vtotal) |
1028 | } |
1029 | |
1030 | /// Returns the horizontal skew of this mode. |
1031 | pub fn hskew(&self) -> u16 { |
1032 | self.mode.hskew |
1033 | } |
1034 | |
1035 | /// Returns the vertical scan of this mode. |
1036 | pub fn vscan(&self) -> u16 { |
1037 | self.mode.vscan |
1038 | } |
1039 | |
1040 | /// Returns the vertical refresh rate of this mode |
1041 | pub fn vrefresh(&self) -> u32 { |
1042 | self.mode.vrefresh |
1043 | } |
1044 | |
1045 | /// Returns the bitmask of this mode |
1046 | pub fn mode_type(&self) -> ModeTypeFlags { |
1047 | ModeTypeFlags::from_bits_truncate(self.mode.type_) |
1048 | } |
1049 | |
1050 | /// Returns the flags of this mode |
1051 | pub fn flags(&self) -> ModeFlags { |
1052 | ModeFlags::from_bits_truncate(self.mode.flags) |
1053 | } |
1054 | } |
1055 | |
1056 | impl From<ffi::drm_mode_modeinfo> for Mode { |
1057 | fn from(raw: ffi::drm_mode_modeinfo) -> Mode { |
1058 | Mode { mode: raw } |
1059 | } |
1060 | } |
1061 | |
1062 | impl From<Mode> for ffi::drm_mode_modeinfo { |
1063 | fn from(mode: Mode) -> Self { |
1064 | mode.mode |
1065 | } |
1066 | } |
1067 | |
1068 | impl std::fmt::Debug for Mode { |
1069 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
1070 | f&mut DebugStruct<'_, '_>.debug_struct("Mode" ) |
1071 | .field("name" , &self.name()) |
1072 | .field("clock" , &self.clock()) |
1073 | .field("size" , &self.size()) |
1074 | .field("hsync" , &self.hsync()) |
1075 | .field("vsync" , &self.vsync()) |
1076 | .field("hskew" , &self.hskew()) |
1077 | .field("vscan" , &self.vscan()) |
1078 | .field("vrefresh" , &self.vrefresh()) |
1079 | .field(name:"mode_type" , &self.mode_type()) |
1080 | .finish() |
1081 | } |
1082 | } |
1083 | |
1084 | bitflags::bitflags! { |
1085 | /// Display mode type flags |
1086 | pub struct ModeTypeFlags : u32 { |
1087 | /// Builtin mode type |
1088 | #[deprecated] |
1089 | const BUILTIN = ffi::DRM_MODE_TYPE_BUILTIN; |
1090 | /// CLOCK_C mode type |
1091 | #[deprecated] |
1092 | const CLOCK_C = ffi::DRM_MODE_TYPE_CLOCK_C; |
1093 | /// CRTC_C mode type |
1094 | #[deprecated] |
1095 | const CRTC_C = ffi::DRM_MODE_TYPE_CRTC_C; |
1096 | /// Preferred mode |
1097 | const PREFERRED = ffi::DRM_MODE_TYPE_PREFERRED; |
1098 | /// Default mode |
1099 | #[deprecated] |
1100 | const DEFAULT = ffi::DRM_MODE_TYPE_DEFAULT; |
1101 | /// User defined mode type |
1102 | const USERDEF = ffi::DRM_MODE_TYPE_USERDEF; |
1103 | /// Mode created by driver |
1104 | const DRIVER = ffi::DRM_MODE_TYPE_DRIVER; |
1105 | /// Bitmask of all valid (non-deprecated) mode type flags |
1106 | const ALL = ffi::DRM_MODE_TYPE_ALL; |
1107 | } |
1108 | } |
1109 | |
1110 | bitflags::bitflags! { |
1111 | /// Display mode flags |
1112 | pub struct ModeFlags: u32 { |
1113 | /// PHSYNC flag |
1114 | const PHSYNC = ffi::DRM_MODE_FLAG_PHSYNC; |
1115 | /// NHSYNC flag |
1116 | const NHSYNC = ffi::DRM_MODE_FLAG_NHSYNC; |
1117 | /// PVSYNC flag |
1118 | const PVSYNC = ffi::DRM_MODE_FLAG_PVSYNC; |
1119 | /// NVSYNC flag |
1120 | const NVSYNC = ffi::DRM_MODE_FLAG_NVSYNC; |
1121 | /// Interlace flag |
1122 | const INTERLACE = ffi::DRM_MODE_FLAG_INTERLACE; |
1123 | /// DBLSCAN flag |
1124 | const DBLSCAN = ffi::DRM_MODE_FLAG_DBLSCAN; |
1125 | /// CSYNC flag |
1126 | const CSYNC = ffi::DRM_MODE_FLAG_CSYNC; |
1127 | /// PCSYNC flag |
1128 | const PCSYNC = ffi::DRM_MODE_FLAG_PCSYNC; |
1129 | /// NCSYNC flag |
1130 | const NCSYNC = ffi::DRM_MODE_FLAG_NCSYNC; |
1131 | /// HSKEW flag |
1132 | const HSKEW = ffi::DRM_MODE_FLAG_HSKEW; |
1133 | #[deprecated] |
1134 | /// BCAST flag |
1135 | const BCAST = ffi::DRM_MODE_FLAG_BCAST; |
1136 | #[deprecated] |
1137 | /// PIXMUX flag |
1138 | const PIXMUX = ffi::DRM_MODE_FLAG_PIXMUX; |
1139 | /// DBLCLK flag |
1140 | const DBLCLK = ffi::DRM_MODE_FLAG_DBLCLK; |
1141 | /// CLKDIV2 flag |
1142 | const CLKDIV2 = ffi::DRM_MODE_FLAG_CLKDIV2; |
1143 | /// Stereo 3D mode utilizing frame packing |
1144 | const _3D_FRAME_PACKING = ffi::DRM_MODE_FLAG_3D_FRAME_PACKING; |
1145 | /// Stereo 3D mode utilizing alternating fields |
1146 | const _3D_FIELD_ALTERNATIVE = ffi::DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE; |
1147 | /// Stereo 3D mode utilizing alternating lines |
1148 | const _3D_LINE_ALTERNATIVE = ffi::DRM_MODE_FLAG_3D_LINE_ALTERNATIVE; |
1149 | /// Stereo 3D mode utilizing side by side full size image |
1150 | const _3D_SIDE_BY_SIDE_FULL = ffi::DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL; |
1151 | /// Stereo 3D mode utilizing depth images |
1152 | const _3D_L_DEPTH = ffi::DRM_MODE_FLAG_3D_L_DEPTH; |
1153 | /// Stereo 3D mode utilizing depth images |
1154 | const _3D_L_DEPTH_GFX_GFX_DEPTH = ffi::DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH; |
1155 | /// Stereo 3D mode utilizing top and bottom images |
1156 | const _3D_TOP_AND_BOTTOM = ffi::DRM_MODE_FLAG_3D_TOP_AND_BOTTOM; |
1157 | /// Stereo 3D mode utilizing side by side half size image |
1158 | const _3D_SIDE_BY_SIDE_HALF = ffi::DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF; |
1159 | } |
1160 | } |
1161 | |
1162 | /// Type of a plane |
1163 | #[repr (u32)] |
1164 | #[derive (Debug, Clone, Copy, PartialEq, Eq, Hash)] |
1165 | pub enum PlaneType { |
1166 | /// Overlay plane |
1167 | Overlay = ffi::DRM_PLANE_TYPE_OVERLAY, |
1168 | /// Primary plane |
1169 | Primary = ffi::DRM_PLANE_TYPE_PRIMARY, |
1170 | /// Cursor plane |
1171 | Cursor = ffi::DRM_PLANE_TYPE_CURSOR, |
1172 | } |
1173 | |
1174 | /// Wrapper around a set of property IDs and their raw values. |
1175 | #[derive (Debug, Clone)] |
1176 | pub struct PropertyValueSet { |
1177 | prop_ids: Vec<property::Handle>, |
1178 | prop_vals: Vec<property::RawValue>, |
1179 | } |
1180 | |
1181 | impl PropertyValueSet { |
1182 | /// Returns a pair representing a set of [`property::Handle`] and their raw values |
1183 | pub fn as_props_and_values(&self) -> (&[property::Handle], &[property::RawValue]) { |
1184 | (&self.prop_ids, &self.prop_vals) |
1185 | } |
1186 | |
1187 | /// Returns iterator over pairs representing a set of [`property::Handle`] and their raw values |
1188 | pub fn iter(&self) -> impl Iterator<Item = (&property::Handle, &property::RawValue)> { |
1189 | self.into_iter() |
1190 | } |
1191 | } |
1192 | |
1193 | impl<'a> IntoIterator for &'a PropertyValueSet { |
1194 | type Item = (&'a property::Handle, &'a property::RawValue); |
1195 | type IntoIter = |
1196 | Zip<std::slice::Iter<'a, property::Handle>, std::slice::Iter<'a, property::RawValue>>; |
1197 | |
1198 | fn into_iter(self) -> Self::IntoIter { |
1199 | self.prop_ids.iter().zip(self.prop_vals.iter()) |
1200 | } |
1201 | } |
1202 | |
1203 | impl IntoIterator for PropertyValueSet { |
1204 | type Item = (property::Handle, property::RawValue); |
1205 | type IntoIter = |
1206 | Zip<std::vec::IntoIter<property::Handle>, std::vec::IntoIter<property::RawValue>>; |
1207 | |
1208 | fn into_iter(self) -> Self::IntoIter { |
1209 | self.prop_ids.into_iter().zip(self.prop_vals.into_iter()) |
1210 | } |
1211 | } |
1212 | |
1213 | /// Describes a rectangular region of a buffer |
1214 | pub type ClipRect = ffi::drm_sys::drm_clip_rect; |
1215 | |
1216 | bitflags::bitflags! { |
1217 | /// Commit flags for atomic mode setting |
1218 | /// |
1219 | /// Limited to the values in [`ffi::drm_sys::DRM_MODE_ATOMIC_FLAGS`]. |
1220 | pub struct AtomicCommitFlags : u32 { |
1221 | /// Generate a page flip event, when the changes are applied |
1222 | const PAGE_FLIP_EVENT = ffi::drm_sys::DRM_MODE_PAGE_FLIP_EVENT; |
1223 | /// Request page flip when the changes are applied, not waiting for vblank |
1224 | const PAGE_FLIP_ASYNC = ffi::drm_sys::DRM_MODE_PAGE_FLIP_ASYNC; |
1225 | /// Test only validity of the request, do not actually apply the requested changes |
1226 | const TEST_ONLY = ffi::drm_sys::DRM_MODE_ATOMIC_TEST_ONLY; |
1227 | /// Do not block on the request and return early |
1228 | const NONBLOCK = ffi::drm_sys::DRM_MODE_ATOMIC_NONBLOCK; |
1229 | /// Allow the changes to trigger a modeset, if necessary |
1230 | /// |
1231 | /// Changes requiring a modeset are rejected otherwise. |
1232 | const ALLOW_MODESET = ffi::drm_sys::DRM_MODE_ATOMIC_ALLOW_MODESET; |
1233 | } |
1234 | } |
1235 | |
1236 | bitflags::bitflags! { |
1237 | /// Mode property flags |
1238 | pub struct ModePropFlags : u32 { |
1239 | /// Do not use |
1240 | #[deprecated] |
1241 | const PENDING = ffi::DRM_MODE_PROP_PENDING; |
1242 | |
1243 | /// Non-extended types: legacy bitmask, one bit per type: |
1244 | const LEGACY_TYPE = ffi::DRM_MODE_PROP_LEGACY_TYPE; |
1245 | /// An unsigned integer that has a min and max value |
1246 | const RANGE = ffi::DRM_MODE_PROP_RANGE; |
1247 | /// Set when this property is informational only and cannot be modified |
1248 | const IMMUTABLE = ffi::DRM_MODE_PROP_IMMUTABLE; |
1249 | /// Enumerated type with text strings |
1250 | const ENUM = ffi::DRM_MODE_PROP_ENUM; |
1251 | /// A chunk of binary data that must be acquired |
1252 | const BLOB = ffi::DRM_MODE_PROP_BLOB; |
1253 | /// Bitmask of enumerated types |
1254 | const BITMASK = ffi::DRM_MODE_PROP_BITMASK; |
1255 | |
1256 | /// Extended-types: rather than continue to consume a bit per type, |
1257 | /// grab a chunk of the bits to use as integer type id. |
1258 | const EXTENDED_TYPE = ffi::DRM_MODE_PROP_EXTENDED_TYPE; |
1259 | /// A DRM object that can have a specific type |
1260 | /// |
1261 | /// See `ffi::DRM_MODE_OBJECT_*` for specific types. |
1262 | const OBJECT = ffi::DRM_MODE_PROP_OBJECT; |
1263 | /// A signed integer that has a min and max value |
1264 | const SIGNED_RANGE = ffi::DRM_MODE_PROP_SIGNED_RANGE; |
1265 | /// the [`Self::ATOMIC`] flag is used to hide properties from userspace that |
1266 | /// is not aware of atomic properties. This is mostly to work around |
1267 | /// older userspace (DDX drivers) that read/write each prop they find, |
1268 | /// witout being aware that this could be triggering a lengthy modeset. |
1269 | const ATOMIC = ffi::DRM_MODE_PROP_ATOMIC; |
1270 | } |
1271 | } |
1272 | |