1//! # Property
2//!
3//! A property of a modesetting resource.
4//!
5//! All modesetting resources have a set of properties that have values that
6//! can be modified. These properties are modesetting resources themselves, and
7//! may even have their own set of properties.
8//!
9//! Properties may have mutable values attached to them. These can be changed by
10//! either changing the state of a resource (thereby affecting the property),
11//! directly changing the property value itself, or by batching property changes
12//! together and executing them all atomically.
13
14use control::{RawResourceHandle, ResourceHandle};
15use drm_ffi as ffi;
16
17/// A raw property value that does not have a specific property type
18pub type RawValue = u64;
19
20/// A handle to a property
21#[repr(transparent)]
22#[derive(Copy, Clone, Hash, PartialEq, Eq)]
23pub struct Handle(RawResourceHandle);
24
25// Safety: Handle is repr(transparent) over NonZeroU32
26unsafe impl bytemuck::ZeroableInOption for Handle {}
27unsafe impl bytemuck::PodInOption for Handle {}
28
29impl From<Handle> for RawResourceHandle {
30 fn from(handle: Handle) -> Self {
31 handle.0
32 }
33}
34
35impl From<Handle> for u32 {
36 fn from(handle: Handle) -> Self {
37 handle.0.into()
38 }
39}
40
41impl From<RawResourceHandle> for Handle {
42 fn from(handle: RawResourceHandle) -> Self {
43 Handle(handle)
44 }
45}
46
47impl ResourceHandle for Handle {
48 const FFI_TYPE: u32 = ffi::DRM_MODE_OBJECT_PROPERTY;
49}
50
51impl std::fmt::Debug for Handle {
52 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
53 f.debug_tuple(name:"property::Handle").field(&self.0).finish()
54 }
55}
56
57/// Information about a property
58#[derive(Debug, Clone, Hash, PartialEq, Eq)]
59pub struct Info {
60 pub(crate) handle: Handle,
61 pub(crate) val_type: ValueType,
62 pub(crate) mutable: bool,
63 pub(crate) atomic: bool,
64 pub(crate) info: ffi::drm_mode_get_property,
65}
66
67impl Info {
68 /// Returns the handle to this property.
69 pub fn handle(&self) -> Handle {
70 self.handle
71 }
72
73 /// Returns the name of this property.
74 pub fn name(&self) -> &std::ffi::CStr {
75 unsafe { std::ffi::CStr::from_ptr(&self.info.name[0] as _) }
76 }
77
78 /// Returns the ValueType of this property.
79 pub fn value_type(&self) -> ValueType {
80 self.val_type.clone()
81 }
82
83 /// Returns whether this property is mutable.
84 pub fn mutable(&self) -> bool {
85 self.mutable
86 }
87
88 /// Returns whether this property can be atomically updated.
89 pub fn atomic(&self) -> bool {
90 self.atomic
91 }
92}
93
94/// Describes the types of value that a property uses.
95#[allow(clippy::upper_case_acronyms)]
96#[allow(clippy::large_enum_variant)]
97#[derive(Debug, Clone, Hash, PartialEq, Eq)]
98pub enum ValueType {
99 /// A catch-all for any unknown types
100 Unknown,
101 /// A True or False type
102 Boolean,
103 /// An unsigned integer that has a min and max value
104 UnsignedRange(u64, u64),
105 /// A signed integer that has a min and max value
106 SignedRange(i64, i64),
107 /// A set of values that are mutually exclusive
108 Enum(EnumValues),
109 /// A set of values that can be combined
110 Bitmask,
111 /// A chunk of binary data that must be acquired
112 Blob,
113 /// A non-specific DRM object
114 Object,
115 /// A CRTC object
116 CRTC,
117 /// A Connector object
118 Connector,
119 /// An Encoder object
120 Encoder,
121 /// A Framebuffer object
122 Framebuffer,
123 /// A Plane object
124 Plane,
125 /// A Property object
126 Property,
127}
128
129impl ValueType {
130 /// Given a [`RawValue`], convert it into a specific [`Value`]
131 pub fn convert_value(&self, value: RawValue) -> Value {
132 match self {
133 ValueType::Unknown => Value::Unknown(value),
134 ValueType::Boolean => Value::Boolean(value != 0),
135 ValueType::UnsignedRange(_, _) => Value::UnsignedRange(value),
136 ValueType::SignedRange(_, _) => Value::SignedRange(value as i64),
137 ValueType::Enum(values: &EnumValues) => Value::Enum(values.get_value_from_raw_value(value)),
138 ValueType::Bitmask => Value::Bitmask(value),
139 ValueType::Blob => Value::Blob(value),
140 ValueType::Object => Value::Object(bytemuck::cast(value as u32)),
141 ValueType::CRTC => Value::CRTC(bytemuck::cast(value as u32)),
142 ValueType::Connector => Value::Connector(bytemuck::cast(value as u32)),
143 ValueType::Encoder => Value::Encoder(bytemuck::cast(value as u32)),
144 ValueType::Framebuffer => Value::Framebuffer(bytemuck::cast(value as u32)),
145 ValueType::Plane => Value::Plane(bytemuck::cast(value as u32)),
146 ValueType::Property => Value::Property(bytemuck::cast(value as u32)),
147 }
148 }
149}
150
151/// The value of a property, in a typed format
152#[allow(missing_docs)]
153#[allow(clippy::upper_case_acronyms)]
154#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
155pub enum Value<'a> {
156 /// Unknown value
157 Unknown(RawValue),
158 /// Boolean value
159 Boolean(bool),
160 /// Unsigned range value
161 UnsignedRange(u64),
162 /// Signed range value
163 SignedRange(i64),
164 /// Enum Value
165 Enum(Option<&'a EnumValue>),
166 /// Bitmask value
167 Bitmask(u64),
168 /// Opaque (blob) value
169 Blob(u64),
170 /// Unknown object value
171 Object(Option<RawResourceHandle>),
172 /// Crtc object value
173 CRTC(Option<super::crtc::Handle>),
174 /// Connector object value
175 Connector(Option<super::connector::Handle>),
176 /// Encoder object value
177 Encoder(Option<super::encoder::Handle>),
178 /// Framebuffer object value
179 Framebuffer(Option<super::framebuffer::Handle>),
180 /// Plane object value
181 Plane(Option<super::plane::Handle>),
182 /// Property object value
183 Property(Option<Handle>),
184}
185
186impl<'a> From<Value<'a>> for RawValue {
187 fn from(value: Value<'a>) -> Self {
188 match value {
189 Value::Unknown(x: u64) => x,
190 Value::Boolean(true) => 1,
191 Value::Boolean(false) => 0,
192 Value::UnsignedRange(x: u64) => x,
193 Value::SignedRange(x: i64) => x as u64,
194 Value::Enum(val: Option<&EnumValue>) => val.map_or(default:0, f:EnumValue::value),
195 Value::Bitmask(x: u64) => x,
196 Value::Blob(x: u64) => x,
197 Value::Object(x: Option>) => bytemuck::cast::<_, u32>(x) as u64,
198 Value::CRTC(x: Option) => bytemuck::cast::<_, u32>(x) as u64,
199 Value::Connector(x: Option) => bytemuck::cast::<_, u32>(x) as u64,
200 Value::Encoder(x: Option) => bytemuck::cast::<_, u32>(x) as u64,
201 Value::Framebuffer(x: Option) => bytemuck::cast::<_, u32>(x) as u64,
202 Value::Plane(x: Option) => bytemuck::cast::<_, u32>(x) as u64,
203 Value::Property(x: Option) => bytemuck::cast::<_, u32>(x) as u64,
204 }
205 }
206}
207
208macro_rules! match_variant {
209 ($this:ident, $variant:ident) => {
210 if let Self::$variant(v) = *$this {
211 Some(v)
212 } else {
213 None
214 }
215 };
216}
217
218impl<'a> Value<'a> {
219 /// Boolean value
220 pub fn as_boolean(&self) -> Option<bool> {
221 match_variant!(self, Boolean)
222 }
223
224 /// Unsigned range value
225 pub fn as_unsigned_range(&self) -> Option<u64> {
226 match_variant!(self, UnsignedRange)
227 }
228
229 /// Signed range value
230 pub fn as_signed_range(&self) -> Option<i64> {
231 match_variant!(self, SignedRange)
232 }
233
234 /// Enum Value
235 pub fn as_enum(&self) -> Option<&'a EnumValue> {
236 match_variant!(self, Enum).flatten()
237 }
238
239 /// Bitmask value
240 pub fn as_bitmask(&self) -> Option<u64> {
241 match_variant!(self, Bitmask)
242 }
243
244 /// Opaque (blob) value
245 pub fn as_blob(&self) -> Option<u64> {
246 match_variant!(self, Blob)
247 }
248
249 /// Unknown object value
250 pub fn as_object(&self) -> Option<RawResourceHandle> {
251 match_variant!(self, Object).flatten()
252 }
253
254 /// Crtc object value
255 pub fn as_crtc(&self) -> Option<super::crtc::Handle> {
256 match_variant!(self, CRTC).flatten()
257 }
258
259 /// Connector object value
260 pub fn as_connector(&self) -> Option<super::connector::Handle> {
261 match_variant!(self, Connector).flatten()
262 }
263
264 /// Encoder object value
265 pub fn as_encoder(&self) -> Option<super::encoder::Handle> {
266 match_variant!(self, Encoder).flatten()
267 }
268
269 /// Framebuffer object value
270 pub fn as_framebuffer(&self) -> Option<super::framebuffer::Handle> {
271 match_variant!(self, Framebuffer).flatten()
272 }
273
274 /// Plane object value
275 pub fn as_plane(&self) -> Option<super::plane::Handle> {
276 match_variant!(self, Plane).flatten()
277 }
278
279 /// Property object value
280 pub fn as_property(&self) -> Option<Handle> {
281 match_variant!(self, Property).flatten()
282 }
283}
284
285/// A single value of [`ValueType::Enum`] type
286#[repr(transparent)]
287#[derive(Copy, Clone, Hash, PartialEq, Eq, bytemuck::TransparentWrapper)]
288pub struct EnumValue(ffi::drm_mode_property_enum);
289
290impl EnumValue {
291 /// Returns the [`RawValue`] of this value
292 pub fn value(&self) -> RawValue {
293 self.0.value
294 }
295
296 /// Returns the name of this value
297 pub fn name(&self) -> &std::ffi::CStr {
298 unsafe { std::ffi::CStr::from_ptr(&self.0.name[0] as _) }
299 }
300}
301
302impl From<ffi::drm_mode_property_enum> for EnumValue {
303 fn from(inner: ffi::drm_mode_property_enum) -> Self {
304 EnumValue(inner)
305 }
306}
307
308impl std::fmt::Debug for EnumValue {
309 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
310 f&mut DebugStruct<'_, '_>.debug_struct("EnumValue")
311 .field("value", &self.value())
312 .field(name:"name", &self.name())
313 .finish()
314 }
315}
316
317/// A set of [`EnumValue`]s for a single property
318#[derive(Debug, Clone, Hash, PartialEq, Eq)]
319pub struct EnumValues {
320 pub(crate) values: Vec<u64>,
321 pub(crate) enums: Vec<EnumValue>,
322}
323
324impl EnumValues {
325 /// Returns a tuple containing slices to the [`RawValue`]s and the [`EnumValue`]s
326 pub fn values(&self) -> (&[RawValue], &[EnumValue]) {
327 (&self.values, &self.enums)
328 }
329
330 /// Returns an [`EnumValue`] for a [`RawValue`], or [`None`] if `value` is
331 /// not part of this [`EnumValues`].
332 pub fn get_value_from_raw_value(&self, value: RawValue) -> Option<&EnumValue> {
333 let (values: &[u64], enums: &[EnumValue]) = self.values();
334 let index: usize = if values.get(index:value as usize) == Some(&value) {
335 // Early-out: indices match values
336 value as usize
337 } else {
338 values.iter().position(|&v: u64| v == value)?
339 };
340 Some(&enums[index])
341 }
342}
343