drm/control/
mod.rs

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