linux_video_core/impls/
control.rs

1use crate::{calls, types::*, utils, Internal, Result};
2use core::mem::MaybeUninit;
3use getset::CopyGetters;
4use std::os::unix::io::RawFd;
5
6impl Internal<QueryCtrl> {
7    pub fn query(fd: RawFd, id: u32) -> Result<Self> {
8        let ctrl = MaybeUninit::<QueryCtrl>::zeroed();
9
10        let ctrl = unsafe_call!({
11            let mut ctrl = ctrl.assume_init();
12            ctrl.id = id;
13            calls::query_ctrl(fd, &mut ctrl).map(|_| ctrl)
14        })?;
15
16        utils::check_str(&ctrl.name)?;
17
18        Ok(ctrl.into())
19    }
20
21    pub fn query_next(fd: RawFd, prev_id: u32) -> Result<Option<Self>> {
22        Self::query(
23            fd,
24            prev_id | (CtrlEnumFlag::NextCtrl | CtrlEnumFlag::NextCompound).bits(),
25        )
26        .map(Some)
27        .or_else(|error| {
28            if error.kind() == std::io::ErrorKind::InvalidInput {
29                Ok(None)
30            } else {
31                Err(error)
32            }
33        })
34    }
35}
36
37trivial_impls! {
38    QueryCtrl {
39        /// Control name
40        getstr name: &str,
41    }
42}
43
44impl QueryCtrl {
45    pub fn is_menu(&self) -> bool {
46        self.type_.is_menu()
47    }
48
49    pub fn has_payload(&self) -> bool {
50        self.flags.contains(CtrlFlag::HasPayload)
51    }
52}
53
54impl core::fmt::Display for QueryCtrl {
55    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
56        match CtrlId::try_from(self.id()) {
57            Ok(id) => id.fmt(f),
58            Err(id) => id.fmt(f),
59        }?;
60        f.write_str(": ")?;
61        self.type_().fmt(f)?;
62        f.write_str(" '")?;
63        self.name().fmt(f)?;
64        f.write_str("' ")?;
65        if !self.flags().is_none() {
66            self.flags().fmt(f)?;
67            f.write_str(" ")?;
68        }
69        f.write_str("(")?;
70        self.min().fmt(f)?;
71        f.write_str("..=")?;
72        self.max().fmt(f)?;
73        f.write_str(" step:")?;
74        self.step().fmt(f)?;
75        f.write_str(" def:")?;
76        self.default().fmt(f)?;
77        f.write_str(")")?;
78        Ok(())
79    }
80}
81
82impl From<QueryCtrl> for QueryExtCtrl {
83    fn from(ctrl: QueryCtrl) -> Self {
84        Self {
85            id: ctrl.id,
86            type_: ctrl.type_,
87            name: ctrl.name,
88            min: ctrl.min as _,
89            max: if matches!(ctrl.type_, CtrlType::BitMask) {
90                ctrl.max as u32 as _
91            } else {
92                ctrl.max as _
93            },
94            default: if matches!(ctrl.type_, CtrlType::BitMask) {
95                ctrl.default as u32 as _
96            } else {
97                ctrl.default as _
98            },
99            step: ctrl.step as _,
100            flags: ctrl.flags
101                | if matches!(ctrl.type_, CtrlType::String) {
102                    CtrlFlag::HasPayload
103                } else {
104                    CtrlFlag::none()
105                },
106            elems: 1,
107            nr_of_dims: 0,
108            elem_size: match ctrl.type_ {
109                CtrlType::Integer64 => core::mem::size_of::<i64>() as _,
110                CtrlType::String => (ctrl.max + 1) as _,
111                _ => core::mem::size_of::<i32>() as _,
112            },
113            dims: [0; CTRL_MAX_DIMS],
114            reserved: [0; 32],
115        }
116    }
117}
118
119impl Internal<QueryExtCtrl> {
120    pub fn query(fd: RawFd, id: u32) -> Result<Self> {
121        let ctrl = MaybeUninit::<QueryExtCtrl>::zeroed();
122
123        let ctrl = unsafe_call!({
124            let mut ctrl = ctrl.assume_init();
125            ctrl.id = id;
126            calls::query_ext_ctrl(fd, &mut ctrl).map(|_| ctrl)
127        })?;
128
129        utils::check_str(&ctrl.name)?;
130
131        Ok(ctrl.into())
132    }
133
134    pub fn query_next(fd: RawFd, prev_id: u32) -> Result<Option<Self>> {
135        Self::query(
136            fd,
137            prev_id | (CtrlEnumFlag::NextCtrl | CtrlEnumFlag::NextCompound).bits(),
138        )
139        .map(Some)
140        .or_else(|error| {
141            if error.kind() == std::io::ErrorKind::InvalidInput {
142                Ok(None)
143            } else {
144                Err(error)
145            }
146        })
147    }
148
149    pub fn query_fallback(fd: RawFd, id: u32) -> Result<Self> {
150        Self::query(fd, id).or_else(|error| {
151            if error.kind() == std::io::ErrorKind::BrokenPipe {
152                Internal::<QueryCtrl>::query(fd, id).map(|fb_ctrl| fb_ctrl.map(From::from))
153            } else {
154                Err(error)
155            }
156        })
157    }
158
159    pub fn query_next_fallback(fd: RawFd, prev_id: u32) -> Result<Option<Self>> {
160        Self::query_fallback(
161            fd,
162            prev_id | (CtrlEnumFlag::NextCtrl | CtrlEnumFlag::NextCompound).bits(),
163        )
164        .map(Some)
165        .or_else(|error| {
166            if error.kind() == std::io::ErrorKind::InvalidInput {
167                Ok(None)
168            } else {
169                Err(error)
170            }
171        })
172    }
173}
174
175trivial_impls! {
176    QueryExtCtrl {
177        /// Control name
178        getstr name: &str,
179    }
180}
181
182impl QueryExtCtrl {
183    pub fn is_menu(&self) -> bool {
184        self.type_.is_menu()
185    }
186
187    pub fn has_payload(&self) -> bool {
188        self.flags.contains(CtrlFlag::HasPayload)
189    }
190
191    /// The size of each dimension
192    pub fn dims(&self) -> &[u32] {
193        &self.dims[..=self.nr_of_dims as usize]
194    }
195
196    /// Size of value in bytes
197    pub fn size(&self) -> u32 {
198        self.elem_size * self.elems
199    }
200}
201
202impl core::fmt::Display for QueryExtCtrl {
203    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
204        match CtrlId::try_from(self.id()) {
205            Ok(id) => id.fmt(f),
206            Err(id) => id.fmt(f),
207        }?;
208        f.write_str(": ")?;
209        self.type_().fmt(f)?;
210        f.write_str(" '")?;
211        self.name().fmt(f)?;
212        f.write_str("' ")?;
213        if !self.flags().is_none() {
214            self.flags().fmt(f)?;
215            f.write_str(" ")?;
216        }
217        f.write_str("(")?;
218        self.min().fmt(f)?;
219        f.write_str("..=")?;
220        self.max().fmt(f)?;
221        f.write_str(" step:")?;
222        self.step().fmt(f)?;
223        f.write_str(" def:")?;
224        self.default().fmt(f)?;
225        f.write_str(")")?;
226        Ok(())
227    }
228}
229
230impl QueryMenu {
231    pub fn id(&self) -> u32 {
232        self.id
233    }
234
235    pub fn index(&self) -> u32 {
236        self.index
237    }
238}
239
240impl Internal<QueryMenu> {
241    pub fn query(fd: RawFd, id: u32, index: u32) -> Result<Option<Self>> {
242        let mut menu = unsafe {
243            QueryMenu {
244                id,
245                index,
246                ..core::mem::zeroed()
247            }
248        };
249
250        unsafe_call!(calls::query_menu(fd, &mut menu as *mut _))
251            .map(|_| Some(Internal(menu)))
252            .or_else(|error| {
253                if error.kind() == std::io::ErrorKind::InvalidInput {
254                    Ok(None)
255                } else {
256                    Err(error)
257                }
258            })
259    }
260
261    pub fn into_item(self, type_: CtrlType, index: u32) -> MenuItem {
262        MenuItem {
263            item: self,
264            type_,
265            index,
266        }
267    }
268}
269
270/// Helper type to represent menu item
271#[derive(Clone, Copy, CopyGetters)]
272pub struct MenuItem {
273    /// Control type
274    #[getset(get_copy = "pub")]
275    type_: CtrlType,
276
277    /// Item index
278    #[getset(get_copy = "pub")]
279    index: u32,
280
281    item: Internal<QueryMenu>,
282}
283
284impl Internal<MenuItem> {
285    pub fn query(
286        fd: RawFd,
287        ctrl_type: CtrlType,
288        ctrl_id: u32,
289        index: u32,
290    ) -> Result<Option<Internal<MenuItem>>> {
291        Internal::<QueryMenu>::query(fd, ctrl_id, index)
292            .map(|ok| ok.map(|item| item.into_item(ctrl_type, index).into()))
293    }
294}
295
296impl MenuItem {
297    pub fn name(&self) -> Option<&str> {
298        if matches!(self.type_, CtrlType::Menu) {
299            utils::get_str(unsafe { &self.item.union_.name }).ok()
300        } else {
301            None
302        }
303    }
304
305    pub fn value(&self) -> Option<i64> {
306        if matches!(self.type_, CtrlType::IntegerMenu) {
307            Some(unsafe { self.item.union_.value })
308        } else {
309            None
310        }
311    }
312}
313
314impl core::fmt::Display for MenuItem {
315    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
316        self.index().fmt(f)?;
317        f.write_str(": ")?;
318        if let Some(name) = self.name() {
319            f.write_str("'")?;
320            name.fmt(f)?;
321            f.write_str("'")?;
322        } else if let Some(value) = self.value() {
323            value.fmt(f)?;
324        }
325        Ok(())
326    }
327}