1use thiserror::Error;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum CameraControl {
9 Zoom,
10 Pan,
11 Tilt,
12 Focus,
13 Exposure,
14 PowerLineFrequency,
15 LowLightCompensation,
16 Brightness,
17 Contrast,
18 Saturation,
19 Sharpness,
20 Gain,
21 BacklightCompensation,
22 WhiteBalance,
23 Tint,
24}
25
26impl CameraControl {
27 pub const ALL: [Self; 15] = [
29 Self::Zoom,
30 Self::Pan,
31 Self::Tilt,
32 Self::Focus,
33 Self::Exposure,
34 Self::PowerLineFrequency,
35 Self::LowLightCompensation,
36 Self::Brightness,
37 Self::Contrast,
38 Self::Saturation,
39 Self::Sharpness,
40 Self::Gain,
41 Self::BacklightCompensation,
42 Self::WhiteBalance,
43 Self::Tint,
44 ];
45
46 #[must_use]
48 pub fn name(self) -> &'static str {
49 match self {
50 Self::Zoom => "zoom",
51 Self::Pan => "pan",
52 Self::Tilt => "tilt",
53 Self::Focus => "focus",
54 Self::Exposure => "exposure",
55 Self::PowerLineFrequency => "power_line_frequency",
56 Self::LowLightCompensation => "low_light_compensation",
57 Self::Brightness => "brightness",
58 Self::Contrast => "contrast",
59 Self::Saturation => "saturation",
60 Self::Sharpness => "sharpness",
61 Self::Gain => "gain",
62 Self::BacklightCompensation => "backlight_compensation",
63 Self::WhiteBalance => "white_balance",
64 Self::Tint => "tint",
65 }
66 }
67
68 #[must_use]
70 pub fn auto_toggle(self) -> Option<AutoToggle> {
71 match self {
72 Self::Focus => Some(AutoToggle::Focus),
73 Self::Exposure => Some(AutoToggle::Exposure),
74 Self::WhiteBalance => Some(AutoToggle::WhiteBalance),
75 _ => None,
76 }
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub enum AutoToggle {
84 Focus,
85 Exposure,
86 WhiteBalance,
87}
88
89impl AutoToggle {
90 pub const ALL: [Self; 3] = [Self::Focus, Self::Exposure, Self::WhiteBalance];
92
93 #[must_use]
95 pub fn name(self) -> &'static str {
96 match self {
97 Self::Focus => "focus_auto",
98 Self::Exposure => "exposure_auto",
99 Self::WhiteBalance => "white_balance_auto",
100 }
101 }
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106pub struct AutoState {
107 pub current: bool,
108 pub default: bool,
109}
110
111#[derive(Debug, Clone, Default)]
114pub struct CameraState {
115 pub controls: Vec<(CameraControl, ControlRange)>,
116 pub autos: Vec<(AutoToggle, AutoState)>,
117}
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121pub struct ControlRange {
122 pub min: i32,
123 pub max: i32,
124 pub default: i32,
125 pub current: i32,
126 pub value_mask: Option<u32>,
129}
130
131impl ControlRange {
132 #[must_use]
134 pub fn supports(self, value: i32) -> bool {
135 if !(self.min..=self.max).contains(&value) {
136 return false;
137 }
138 self.value_mask.is_none_or(|mask| {
139 u32::try_from(value)
140 .ok()
141 .filter(|value| *value < u32::BITS)
142 .is_some_and(|value| mask & (1 << value) != 0)
143 })
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::ControlRange;
150
151 #[test]
152 fn discrete_range_rejects_missing_values() {
153 let range = ControlRange {
154 min: 0,
155 max: 3,
156 default: 1,
157 current: 1,
158 value_mask: Some((1 << 1) | (1 << 3)),
159 };
160
161 assert!(range.supports(1));
162 assert!(!range.supports(2));
163 assert!(range.supports(3));
164 }
165}
166
167#[derive(Debug, Clone, Error)]
169pub enum ControlError {
170 #[error("no matching UVC device")]
172 NotFound,
173 #[error("camera could not be uniquely identified")]
177 Ambiguous,
178 #[error("camera does not support that control")]
181 Unsupported,
182 #[error("platform error: {0}")]
184 Io(String),
185}