linux_video_core/impls/
control.rs1use 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 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 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 pub fn dims(&self) -> &[u32] {
193 &self.dims[..=self.nr_of_dims as usize]
194 }
195
196 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#[derive(Clone, Copy, CopyGetters)]
272pub struct MenuItem {
273 #[getset(get_copy = "pub")]
275 type_: CtrlType,
276
277 #[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}