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