Skip to main content

cadmus_core/frontlight/
mod.rs

1/// Automatic frontlight calculations based on sunrise and sunset.
2pub mod auto;
3mod natural;
4mod premixed;
5mod standard;
6
7pub use self::natural::NaturalFrontlight;
8pub use self::premixed::PremixedFrontlight;
9pub use self::standard::StandardFrontlight;
10use crate::geom::lerp;
11use libc::c_int;
12use serde::{Deserialize, Serialize};
13use std::cmp::Ordering;
14use std::fmt::{Display, Formatter};
15
16/// The level of light intensity from 0-100.
17#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
18pub struct LightLevel(f32);
19
20/// The default implementation reutrns 1%, so that the screen can be seen
21/// if the device happens to be turned on in a dark place.
22impl Default for LightLevel {
23    fn default() -> Self {
24        Self(1f32)
25    }
26}
27
28impl From<LightLevel> for c_int {
29    fn from(value: LightLevel) -> Self {
30        value.0.round() as c_int
31    }
32}
33
34impl From<LightLevel> for i16 {
35    fn from(value: LightLevel) -> Self {
36        value.0.round() as i16
37    }
38}
39
40impl From<LightLevel> for String {
41    fn from(value: LightLevel) -> Self {
42        format!("{:.0}", value.0)
43    }
44}
45
46impl Display for LightLevel {
47    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
48        write!(f, "{:.0}%", self.value())
49    }
50}
51
52impl std::cmp::PartialEq<f32> for LightLevel {
53    fn eq(&self, other: &f32) -> bool {
54        self.value().eq(other)
55    }
56}
57
58impl std::cmp::PartialOrd<f32> for LightLevel {
59    fn partial_cmp(&self, other: &f32) -> Option<Ordering> {
60        self.value().partial_cmp(other)
61    }
62}
63
64impl std::ops::Sub<f32> for LightLevel {
65    type Output = LightLevel;
66
67    fn sub(self, rhs: f32) -> Self::Output {
68        Self(self.value() - rhs)
69    }
70}
71impl From<f32> for LightLevel {
72    fn from(value: f32) -> Self {
73        Self(value.clamp(0.0, 100.0))
74    }
75}
76
77impl From<LightLevel> for f32 {
78    fn from(value: LightLevel) -> Self {
79        value.0
80    }
81}
82
83impl LightLevel {
84    const ZERO: f32 = 0.0;
85
86    /// Returns the absolute value of the light level.
87    pub fn abs(self) -> Self {
88        self.value().abs().into()
89    }
90
91    /// Returns a light level representing `0%` output.
92    pub fn off() -> Self {
93        Self(Self::ZERO)
94    }
95
96    fn value(&self) -> f32 {
97        self.0
98    }
99
100    /// Given a value between 0.0 and 1, this normalizes LightLevel accordingly to 0-100.
101    pub fn from_fraction(fraction: f32) -> Self {
102        Self::from(fraction * 100f32)
103    }
104
105    /// Returns the level as a value between 0.0-1
106    pub fn as_fraction(&self) -> f32 {
107        self.value() / 100.0
108    }
109
110    /// Instead of 0-100, this returns a value between 0-10.
111    /// Usefull for e.g. `/sys/class/backlight/lm3630a_led/color` that accepts
112    /// a value between 0-10.
113    pub fn as_10_base(&self) -> i16 {
114        (self.value() / 10.0).round() as i16
115    }
116
117    /// Similar to [`Self::as_10_base`] but instead of 10 being max, 0 is max.
118    pub fn as_10_base_inverted(&self) -> i16 {
119        10 - self.as_10_base()
120    }
121}
122
123/// A complete frontlight state containing brightness and warmth.
124#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
125pub struct LightLevels {
126    /// The overall frontlight brightness.
127    pub intensity: LightLevel,
128    /// The warmth of the emitted light.
129    pub warmth: LightLevel,
130}
131
132impl Default for LightLevels {
133    fn default() -> Self {
134        LightLevels {
135            intensity: LightLevel::default(),
136            warmth: LightLevel::default(),
137        }
138    }
139}
140
141impl LightLevels {
142    /// Linearly interpolates between two frontlight states.
143    ///
144    /// `t = 0.0` returns `self` and `t = 1.0` returns `other`.
145    pub fn interpolate(self, other: Self, t: f32) -> Self {
146        LightLevels {
147            intensity: LightLevel(lerp(self.intensity.value(), other.intensity.value(), t)),
148            warmth: LightLevel(lerp(self.warmth.value(), other.warmth.value(), t)),
149        }
150    }
151}
152
153pub trait Frontlight {
154    // value is a percentage.
155    fn set_intensity(&mut self, value: LightLevel) -> anyhow::Result<()>;
156    fn set_warmth(&mut self, value: LightLevel) -> anyhow::Result<()>;
157    fn levels(&self) -> LightLevels;
158    /// Turns the FrontLight off by setting everything to [`LightLevel::off()`]
159    fn turn_off(&mut self) -> anyhow::Result<()> {
160        self.set_intensity(LightLevel::off())?;
161        self.set_warmth(LightLevel::off())?;
162        Ok(())
163    }
164}
165
166impl Frontlight for LightLevels {
167    fn set_intensity(&mut self, value: LightLevel) -> anyhow::Result<()> {
168        self.intensity = value;
169        Ok(())
170    }
171
172    fn set_warmth(&mut self, value: LightLevel) -> anyhow::Result<()> {
173        self.warmth = value;
174        Ok(())
175    }
176
177    fn levels(&self) -> LightLevels {
178        *self
179    }
180}