1 use std::os::unix::io::AsRawFd;
2 
3 use bitflags::bitflags;
4 use enumn::N;
5 use nix::errno::Errno;
6 use thiserror::Error;
7 
8 use crate::bindings;
9 use crate::bindings::v4l2_rect;
10 use crate::bindings::v4l2_selection;
11 
12 #[derive(Debug, N, Clone, Copy, PartialEq, Eq)]
13 #[repr(u32)]
14 pub enum SelectionType {
15     Capture = bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE,
16     Output = bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_OUTPUT,
17 }
18 
19 #[derive(Debug, N, Clone, Copy, PartialEq, Eq)]
20 #[repr(u32)]
21 pub enum SelectionTarget {
22     Crop = bindings::V4L2_SEL_TGT_CROP,
23     CropDefault = bindings::V4L2_SEL_TGT_CROP_DEFAULT,
24     CropBounds = bindings::V4L2_SEL_TGT_CROP_BOUNDS,
25     NativeSize = bindings::V4L2_SEL_TGT_NATIVE_SIZE,
26     Compose = bindings::V4L2_SEL_TGT_COMPOSE,
27     ComposeDefault = bindings::V4L2_SEL_TGT_COMPOSE_DEFAULT,
28     ComposeBounds = bindings::V4L2_SEL_TGT_COMPOSE_BOUNDS,
29     ComposePadded = bindings::V4L2_SEL_TGT_COMPOSE_PADDED,
30 }
31 
32 bitflags! {
33     #[derive(Clone, Copy, Debug)]
34     pub struct SelectionFlags: u32 {
35         const GE = bindings::V4L2_SEL_FLAG_GE;
36         const LE = bindings::V4L2_SEL_FLAG_LE;
37         const KEEP_CONFIG = bindings::V4L2_SEL_FLAG_KEEP_CONFIG;
38     }
39 }
40 
41 #[doc(hidden)]
42 mod ioctl {
43     use crate::bindings::v4l2_selection;
44     nix::ioctl_readwrite!(vidioc_g_selection, b'V', 94, v4l2_selection);
45     nix::ioctl_readwrite!(vidioc_s_selection, b'V', 95, v4l2_selection);
46 }
47 
48 #[derive(Debug, Error)]
49 pub enum GSelectionError {
50     #[error("invalid type or target requested")]
51     Invalid,
52     #[error("ioctl error: {0}")]
53     IoctlError(Errno),
54 }
55 
56 impl From<GSelectionError> for Errno {
from(err: GSelectionError) -> Self57     fn from(err: GSelectionError) -> Self {
58         match err {
59             GSelectionError::Invalid => Errno::EINVAL,
60             GSelectionError::IoctlError(e) => e,
61         }
62     }
63 }
64 
65 /// Safe wrapper around the `VIDIOC_G_SELECTION` ioctl.
g_selection<R: From<v4l2_rect>>( fd: &impl AsRawFd, selection: SelectionType, target: SelectionTarget, ) -> Result<R, GSelectionError>66 pub fn g_selection<R: From<v4l2_rect>>(
67     fd: &impl AsRawFd,
68     selection: SelectionType,
69     target: SelectionTarget,
70 ) -> Result<R, GSelectionError> {
71     let mut sel = v4l2_selection {
72         type_: selection as u32,
73         target: target as u32,
74         ..Default::default()
75     };
76 
77     match unsafe { ioctl::vidioc_g_selection(fd.as_raw_fd(), &mut sel) } {
78         Ok(_) => Ok(R::from(sel.r)),
79         Err(Errno::EINVAL) => Err(GSelectionError::Invalid),
80         Err(e) => Err(GSelectionError::IoctlError(e)),
81     }
82 }
83 
84 #[derive(Debug, Error)]
85 pub enum SSelectionError {
86     #[error("invalid type or target requested")]
87     Invalid,
88     #[error("invalid range requested")]
89     InvalidRange,
90     #[error("cannot change selection rectangle currently")]
91     Busy,
92     #[error("ioctl error: {0}")]
93     IoctlError(nix::Error),
94 }
95 
96 impl From<SSelectionError> for Errno {
from(err: SSelectionError) -> Self97     fn from(err: SSelectionError) -> Self {
98         match err {
99             SSelectionError::Invalid => Errno::EINVAL,
100             SSelectionError::InvalidRange => Errno::ERANGE,
101             SSelectionError::Busy => Errno::EBUSY,
102             SSelectionError::IoctlError(e) => e,
103         }
104     }
105 }
106 
107 /// Safe wrapper around the `VIDIOC_S_SELECTION` ioctl.
s_selection<RI: Into<v4l2_rect>, RO: From<v4l2_rect>>( fd: &impl AsRawFd, selection: SelectionType, target: SelectionTarget, rect: RI, flags: SelectionFlags, ) -> Result<RO, SSelectionError>108 pub fn s_selection<RI: Into<v4l2_rect>, RO: From<v4l2_rect>>(
109     fd: &impl AsRawFd,
110     selection: SelectionType,
111     target: SelectionTarget,
112     rect: RI,
113     flags: SelectionFlags,
114 ) -> Result<RO, SSelectionError> {
115     let mut sel = v4l2_selection {
116         type_: selection as u32,
117         target: target as u32,
118         flags: flags.bits(),
119         r: rect.into(),
120         ..Default::default()
121     };
122 
123     match unsafe { ioctl::vidioc_s_selection(fd.as_raw_fd(), &mut sel) } {
124         Ok(_) => Ok(RO::from(sel.r)),
125         Err(Errno::EINVAL) => Err(SSelectionError::Invalid),
126         Err(Errno::ERANGE) => Err(SSelectionError::InvalidRange),
127         Err(Errno::EBUSY) => Err(SSelectionError::Busy),
128         Err(e) => Err(SSelectionError::IoctlError(e)),
129     }
130 }
131