Skip to main content

openlogi_camera/
controls.rs

1//! Platform-independent control vocabulary shared by every UVC backend
2//! (IOKit on macOS, DirectShow on Windows, stubs elsewhere).
3
4/// One adjustable camera control, mapped to a UVC selector by each backend.
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum CameraControl {
7    Zoom,
8    Focus,
9    Exposure,
10    Brightness,
11    Contrast,
12    Saturation,
13    Sharpness,
14    WhiteBalance,
15    Tint,
16}
17
18impl CameraControl {
19    /// Every control, in the order the UI lists them (lens first, then image).
20    pub const ALL: [Self; 9] = [
21        Self::Zoom,
22        Self::Focus,
23        Self::Exposure,
24        Self::Brightness,
25        Self::Contrast,
26        Self::Saturation,
27        Self::Sharpness,
28        Self::WhiteBalance,
29        Self::Tint,
30    ];
31
32    /// Stable snake_case identifier used for config persistence and the CLI.
33    #[must_use]
34    pub fn name(self) -> &'static str {
35        match self {
36            Self::Zoom => "zoom",
37            Self::Focus => "focus",
38            Self::Exposure => "exposure",
39            Self::Brightness => "brightness",
40            Self::Contrast => "contrast",
41            Self::Saturation => "saturation",
42            Self::Sharpness => "sharpness",
43            Self::WhiteBalance => "white_balance",
44            Self::Tint => "tint",
45        }
46    }
47
48    /// The auto-mode toggle that gates this control, if the device has one.
49    #[must_use]
50    pub fn auto_toggle(self) -> Option<AutoToggle> {
51        match self {
52            Self::Focus => Some(AutoToggle::Focus),
53            Self::Exposure => Some(AutoToggle::Exposure),
54            Self::WhiteBalance => Some(AutoToggle::WhiteBalance),
55            _ => None,
56        }
57    }
58}
59
60/// An auto-mode toggle paired with a manual control (focus / exposure / white
61/// balance).
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub enum AutoToggle {
64    Focus,
65    Exposure,
66    WhiteBalance,
67}
68
69impl AutoToggle {
70    /// Every toggle, matching [`CameraControl::auto_toggle`] pairs.
71    pub const ALL: [Self; 3] = [Self::Focus, Self::Exposure, Self::WhiteBalance];
72
73    /// Stable snake_case identifier used for config persistence and the CLI.
74    #[must_use]
75    pub fn name(self) -> &'static str {
76        match self {
77            Self::Focus => "focus_auto",
78            Self::Exposure => "exposure_auto",
79            Self::WhiteBalance => "white_balance_auto",
80        }
81    }
82}
83
84/// One auto toggle's live and default state, read from the device.
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
86pub struct AutoState {
87    pub current: bool,
88    pub default: bool,
89}
90
91/// Everything the controls UI needs, read in a single device-open: each
92/// supported control's range and each supported auto toggle's state.
93#[derive(Debug, Clone, Default)]
94pub struct CameraState {
95    pub controls: Vec<(CameraControl, ControlRange)>,
96    pub autos: Vec<(AutoToggle, AutoState)>,
97}
98
99/// The device's reported range and current value for a control.
100#[derive(Debug, Clone, Copy, PartialEq, Eq)]
101pub struct ControlRange {
102    pub min: i32,
103    pub max: i32,
104    pub default: i32,
105    pub current: i32,
106}
107
108/// Why a UVC control operation failed.
109#[derive(Debug, Clone)]
110pub enum ControlError {
111    /// No matching camera device (or it exposes no controllable unit).
112    NotFound,
113    /// The selected camera can't be uniquely identified: its unique id didn't
114    /// resolve to a USB location and more than one Logitech camera is attached,
115    /// so a write could hit the wrong device. Fails closed instead of guessing.
116    Ambiguous,
117    /// The camera rejected or didn't support the control — or the platform
118    /// has no UVC control backend at all.
119    Unsupported,
120    /// A platform API call failed (open, bind, or the control transfer).
121    Io(String),
122}
123
124impl std::fmt::Display for ControlError {
125    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126        match self {
127            Self::NotFound => write!(f, "no matching UVC device"),
128            Self::Ambiguous => write!(f, "camera could not be uniquely identified"),
129            Self::Unsupported => write!(f, "camera does not support that control"),
130            Self::Io(s) => write!(f, "platform error: {s}"),
131        }
132    }
133}
134
135impl std::error::Error for ControlError {}