use std::os::unix::io::AsRawFd;
use bitflags::bitflags;
use nix::errno::Errno;
use thiserror::Error;
use crate::bindings;
use crate::bindings::v4l2_query_ext_ctrl;
use crate::bindings::v4l2_queryctrl;
#[derive(Debug, PartialEq, Eq)]
pub struct CtrlId(u32);
#[derive(Debug, Error, PartialEq, Eq)]
pub enum CtrlIdError {
#[error("invalid control number: 0x{0:08x}")]
InvalidControl(u32),
}
impl CtrlId {
pub fn new(ctrl: u32) -> Result<Self, CtrlIdError> {
if (ctrl & bindings::V4L2_CTRL_ID_MASK) != ctrl {
Err(CtrlIdError::InvalidControl(ctrl))
} else {
Ok(CtrlId(ctrl))
}
}
}
bitflags! {
#[derive(Clone, Copy, Debug)]
pub struct QueryCtrlFlags: u32 {
const NEXT = bindings::V4L2_CTRL_FLAG_NEXT_CTRL;
const COMPOUND = bindings::V4L2_CTRL_FLAG_NEXT_COMPOUND;
}
}
pub fn parse_ctrl_id_and_flags(ctrl: u32) -> (CtrlId, QueryCtrlFlags) {
(
CtrlId(ctrl & bindings::V4L2_CTRL_ID_MASK),
QueryCtrlFlags::from_bits_truncate(ctrl),
)
}
#[doc(hidden)]
mod ioctl {
use crate::bindings::v4l2_queryctrl;
nix::ioctl_readwrite!(vidioc_queryctrl, b'V', 36, v4l2_queryctrl);
use crate::bindings::v4l2_query_ext_ctrl;
nix::ioctl_readwrite!(vidioc_query_ext_ctrl, b'V', 103, v4l2_query_ext_ctrl);
}
#[derive(Debug, Error)]
pub enum QueryCtrlError {
#[error("ioctl error: {0}")]
IoctlError(Errno),
}
impl From<QueryCtrlError> for Errno {
fn from(err: QueryCtrlError) -> Self {
match err {
QueryCtrlError::IoctlError(e) => e,
}
}
}
pub fn queryctrl<T: From<v4l2_queryctrl>>(
fd: &impl AsRawFd,
id: CtrlId,
flags: QueryCtrlFlags,
) -> Result<T, QueryCtrlError> {
let mut qctrl: v4l2_queryctrl = v4l2_queryctrl {
id: id.0 | flags.bits(),
..Default::default()
};
match unsafe { ioctl::vidioc_queryctrl(fd.as_raw_fd(), &mut qctrl) } {
Ok(_) => Ok(T::from(qctrl)),
Err(e) => Err(QueryCtrlError::IoctlError(e)),
}
}
pub fn query_ext_ctrl<T: From<v4l2_query_ext_ctrl>>(
fd: &impl AsRawFd,
id: CtrlId,
flags: QueryCtrlFlags,
) -> Result<T, QueryCtrlError> {
let mut qctrl: v4l2_query_ext_ctrl = v4l2_query_ext_ctrl {
id: id.0 | flags.bits(),
..Default::default()
};
match unsafe { ioctl::vidioc_query_ext_ctrl(fd.as_raw_fd(), &mut qctrl) } {
Ok(_) => Ok(T::from(qctrl)),
Err(e) => Err(QueryCtrlError::IoctlError(e)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ctrlid() {
assert_eq!(
CtrlId::new(bindings::V4L2_CID_AUDIO_VOLUME),
Ok(CtrlId(bindings::V4L2_CID_AUDIO_VOLUME))
);
assert_eq!(
CtrlId::new(bindings::V4L2_CTRL_FLAG_NEXT_CTRL),
Err(CtrlIdError::InvalidControl(
bindings::V4L2_CTRL_FLAG_NEXT_CTRL
))
);
}
}