cadmus_core/frontlight/
natural.rs1use super::{Frontlight, LightLevel, LightLevels};
2use crate::device::{CURRENT_DEVICE, Model};
3use anyhow::Error;
4use fxhash::FxHashMap;
5use lazy_static::lazy_static;
6use std::fs::File;
7use std::fs::OpenOptions;
8use std::io::Read;
9use std::io::Write;
10use std::path::PathBuf;
11
12const FRONTLIGHT_INTERFACE: &str = "/sys/class/backlight";
13
14const FRONTLIGHT_WHITE_A: &str = "lm3630a_led1b";
16const FRONTLIGHT_RED_A: &str = "lm3630a_led1a";
17const FRONTLIGHT_GREEN_A: &str = "lm3630a_ledb";
18
19const FRONTLIGHT_WHITE_B: &str = "lm3630a_ledb";
21const FRONTLIGHT_ORANGE_B: &str = "lm3630a_leda";
22
23const FRONTLIGHT_VALUE: &str = "brightness";
24const FRONTLIGHT_MAX_VALUE: &str = "max_brightness";
25const FRONTLIGHT_POWER: &str = "bl_power";
26
27const FRONTLIGHT_POWER_ON: i16 = 31;
28const FRONTLIGHT_POWER_OFF: i16 = 0;
29
30#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
31pub enum LightColor {
32 White,
33 Red,
34 Green,
35 Orange,
36}
37
38lazy_static! {
39 pub static ref FRONTLIGHT_DIRS: FxHashMap<LightColor, &'static str> = match CURRENT_DEVICE.model
40 {
41 Model::AuraONE | Model::AuraONELimEd => {
42 [
43 (LightColor::White, FRONTLIGHT_WHITE_A),
44 (LightColor::Red, FRONTLIGHT_RED_A),
45 (LightColor::Green, FRONTLIGHT_GREEN_A),
46 ]
47 .iter()
48 .cloned()
49 .collect()
50 }
51 _ => {
52 [
53 (LightColor::White, FRONTLIGHT_WHITE_B),
54 (LightColor::Orange, FRONTLIGHT_ORANGE_B),
55 ]
56 .iter()
57 .cloned()
58 .collect()
59 }
60 };
61}
62
63pub struct NaturalFrontlight {
64 intensity: LightLevel,
65 warmth: LightLevel,
66 values: FxHashMap<LightColor, File>,
67 powers: FxHashMap<LightColor, File>,
68 maxima: FxHashMap<LightColor, i16>,
69}
70
71impl NaturalFrontlight {
72 pub fn new(intensity: LightLevel, warmth: LightLevel) -> Result<NaturalFrontlight, Error> {
73 let mut maxima = FxHashMap::default();
74 let mut values = FxHashMap::default();
75 let mut powers = FxHashMap::default();
76 let base = PathBuf::from(FRONTLIGHT_INTERFACE);
77 for (light, name) in FRONTLIGHT_DIRS.iter() {
78 let dir = base.join(name);
79 let mut buf = String::new();
80 let mut file = File::open(dir.join(FRONTLIGHT_MAX_VALUE))?;
81 file.read_to_string(&mut buf)?;
82 maxima.insert(*light, buf.trim_end().parse()?);
83 let file = OpenOptions::new()
84 .write(true)
85 .open(dir.join(FRONTLIGHT_VALUE))?;
86 values.insert(*light, file);
87 let file = OpenOptions::new()
88 .write(true)
89 .open(dir.join(FRONTLIGHT_POWER))?;
90 powers.insert(*light, file);
91 }
92 Ok(NaturalFrontlight {
93 intensity,
94 warmth,
95 maxima,
96 values,
97 powers,
98 })
99 }
100
101 fn set(&mut self, c: LightColor, percent: f32) -> Result<(), Error> {
102 let max_value = self.maxima[&c] as f32;
103 let value = (percent.clamp(0.0, 100.0) / 100.0 * max_value) as i16;
104 let mut file = &self.values[&c];
105 write!(file, "{}", value)?;
106 let mut file = &self.powers[&c];
107 let power = if value > 0 {
108 FRONTLIGHT_POWER_ON
109 } else {
110 FRONTLIGHT_POWER_OFF
111 };
112 write!(file, "{}", power)?;
113 Ok(())
114 }
115
116 fn update(&mut self, intensity: LightLevel, warmth: LightLevel) -> Result<(), Error> {
117 let i = intensity.as_fraction();
118 let w = warmth.as_fraction();
119 let white = 80.0 * i * (1.0 - w).sqrt();
120 self.set(LightColor::White, white)?;
121
122 if self.values.len() == 3 {
123 let green = 64.0 * (w * i).sqrt();
124 let red = if green == 0.0 {
125 0.0
126 } else {
127 green + 20.0 + 7.0 * (1.0 - green / 64.0) + w * 4.0
128 };
129 self.set(LightColor::Red, red)?;
130 self.set(LightColor::Green, green)?;
131 } else {
132 let orange = 95.0 * (w * i).sqrt();
133 self.set(LightColor::Orange, orange)?;
134 }
135
136 self.intensity = intensity;
137 self.warmth = warmth;
138 Ok(())
139 }
140}
141
142impl Frontlight for NaturalFrontlight {
143 fn set_intensity(&mut self, value: LightLevel) -> Result<(), Error> {
144 let warmth = self.warmth;
145 self.update(value, warmth)
146 }
147
148 fn set_warmth(&mut self, value: LightLevel) -> Result<(), Error> {
149 let intensity = self.intensity;
150 self.update(intensity, value)
151 }
152
153 fn levels(&self) -> LightLevels {
154 LightLevels {
155 intensity: self.intensity,
156 warmth: self.warmth,
157 }
158 }
159}