1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use core::iter;
use core::ops::BitOr;

use super::ObjectId;

/// An atomic modesetting commit request.
#[derive(Debug)]
pub struct AtomicRequest {
    objs: BTreeMap<u32, AtomicRequestObj>,
    total_props: u32,
}

#[derive(Debug)]
struct AtomicRequestObj {
    prop_ids: Vec<u32>,
    prop_values: Vec<u64>,
}

impl AtomicRequest {
    pub fn new() -> Self {
        Self {
            objs: BTreeMap::new(),
            total_props: 0,
        }
    }

    /// Clear any previously-set properties, returning the object to empty.
    ///
    /// This function does, however, retain any allocated capacity for
    /// property-set requests for existing objects, so callers can avoid
    /// making lots of dynamic memory allocations on every change by
    /// retaining and reusing a single request object. This optimization
    /// will only be productive if the object is reused to set a similar
    /// set of properties on a similar set of objects.
    pub fn reset(&mut self) {
        // The idea here is to retain all of our existing BTreeMap elements,
        // but to reduce the length of the nested vectors to zero. This
        // therefore means that subsequent uses of the request with similar
        // object ids and a similar number of properties per object can
        // avoid the need for lots of reallocation.
        for (_, v) in self.objs.iter_mut() {
            v.prop_ids.truncate(0);
            v.prop_values.truncate(0);
        }
        self.total_props = 0;
    }

    pub fn set_property(&mut self, obj_id: ObjectId, prop_id: u32, value: u64) {
        let (_, obj_id) = obj_id.as_raw_type_and_id();
        let obj = self.objs.entry(obj_id).or_insert_with(|| AtomicRequestObj {
            prop_ids: Vec::new(),
            prop_values: Vec::new(),
        });

        // We'll reserve first to make sure that running out of memory can't
        // cause these two vecs to end up with different lengths when we're done.
        obj.prop_ids.reserve(1);
        obj.prop_values.reserve(1);

        obj.prop_ids.push(prop_id);
        obj.prop_values.push(value);
        self.total_props += 1; // panics if request contains more than u32::MAX total properties
        if self.objs.len() > (u32::MAX as usize) {
            panic!("too many distinct objects in request");
        }
    }

    pub(crate) fn for_ioctl_req(&self) -> AtomicRequestRawParts {
        let obj_count = self.objs.len();
        let mut obj_ids = Vec::<u32>::with_capacity(obj_count);
        let mut obj_prop_counts = Vec::<u32>::with_capacity(obj_count);
        let total_prop_count = self.total_props as usize;
        let mut prop_ids = Vec::<u32>::with_capacity(total_prop_count);
        let mut prop_values = Vec::<u64>::with_capacity(total_prop_count);

        for (obj_id, obj) in self.objs.iter() {
            obj_ids.push(*obj_id);
            obj_prop_counts.push(obj.prop_ids.len() as u32);

            for (prop_id, value) in iter::zip(
                obj.prop_ids.iter().copied(),
                obj.prop_values.iter().copied(),
            ) {
                prop_ids.push(prop_id);
                prop_values.push(value);
            }
        }

        AtomicRequestRawParts {
            obj_ids,
            obj_prop_counts,
            prop_ids,
            prop_values,
        }
    }
}

pub(crate) struct AtomicRequestRawParts {
    pub(crate) obj_ids: Vec<u32>,
    pub(crate) obj_prop_counts: Vec<u32>,
    pub(crate) prop_ids: Vec<u32>,
    pub(crate) prop_values: Vec<u64>,
}

pub struct AtomicCommitFlags(pub(crate) u32);

impl AtomicCommitFlags {
    pub const NONE: Self = Self(0);
    pub const TEST_ONLY: Self = Self(crate::ioctl::DRM_MODE_ATOMIC_TEST_ONLY);
    pub const NONBLOCK: Self = Self(crate::ioctl::DRM_MODE_ATOMIC_NONBLOCK);
    pub const ALLOW_MODESET: Self = Self(crate::ioctl::DRM_MODE_ATOMIC_ALLOW_MODESET);
    pub const PAGE_FLIP_EVENT: Self = Self(crate::ioctl::DRM_MODE_PAGE_FLIP_EVENT);
    pub const ASYNC: Self = Self(crate::ioctl::DRM_MODE_PAGE_FLIP_ASYNC);
}

impl BitOr for AtomicCommitFlags {
    type Output = Self;

    fn bitor(self, rhs: Self) -> Self {
        Self(self.0 | rhs.0)
    }
}