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