1pub mod sway;
18pub mod xresources;
19
20use crate::errors::*;
21use serde::de::{self, Deserializer, Visitor};
22use serde::{Deserialize, Serialize, Serializer};
23use smart_default::SmartDefault;
24use std::fmt;
25use std::ops::Add;
26use std::str::FromStr;
27
28#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
30pub struct Rgba {
31 pub r: u8,
32 pub g: u8,
33 pub b: u8,
34 pub a: u8,
35}
36
37impl Rgba {
38 pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
48 Self { r, g, b, a }
49 }
50
51 pub fn from_hex(hex: u32) -> Self {
55 let [r, g, b, a] = hex.to_be_bytes();
56 Self { r, g, b, a }
57 }
58}
59
60impl Add for Rgba {
61 type Output = Self;
62 fn add(self, rhs: Self) -> Self::Output {
63 Rgba::new(
64 self.r.saturating_add(rhs.r),
65 self.g.saturating_add(rhs.g),
66 self.b.saturating_add(rhs.b),
67 self.a.saturating_add(rhs.a),
68 )
69 }
70}
71
72#[derive(Copy, Clone, Debug, Default)]
74pub struct Hsva {
75 pub h: f64,
76 pub s: f64,
77 pub v: f64,
78 pub a: u8,
79}
80
81impl Hsva {
82 pub fn new(h: f64, s: f64, v: f64, a: u8) -> Self {
92 Self { h, s, v, a }
93 }
94}
95
96impl PartialEq for Hsva {
97 fn eq(&self, other: &Self) -> bool {
98 approx(self.h, other.h)
99 && approx(self.s, other.s)
100 && approx(self.v, other.v)
101 && self.a == other.a
102 }
103}
104
105impl From<Rgba> for Hsva {
106 fn from(rgba: Rgba) -> Self {
107 let r = rgba.r as f64 / 255.0;
108 let g = rgba.g as f64 / 255.0;
109 let b = rgba.b as f64 / 255.0;
110
111 let min = r.min(g.min(b));
112 let max = r.max(g.max(b));
113 let delta = max - min;
114
115 let v = max;
116 let s = match max > 1e-3 {
117 true => delta / max,
118 false => 0.0,
119 };
120 let h = match delta == 0.0 {
121 true => 0.0,
122 false => {
123 if r == max {
124 (g - b) / delta
125 } else if g == max {
126 2.0 + (b - r) / delta
127 } else {
128 4.0 + (r - g) / delta
129 }
130 }
131 };
132 let h2 = ((h * 60.0) + 360.0) % 360.0;
133
134 Self::new(h2, s, v, rgba.a)
135 }
136}
137
138impl From<Hsva> for Rgba {
139 fn from(hsva: Hsva) -> Self {
140 let range = (hsva.h / 60.0) as u8;
141 let c = hsva.v * hsva.s;
142 let x = c * (1.0 - (((hsva.h / 60.0) % 2.0) - 1.0).abs());
143 let m = hsva.v - c;
144
145 let cm_scaled = ((c + m) * 255.0) as u8;
146 let xm_scaled = ((x + m) * 255.0) as u8;
147 let m_scaled = (m * 255.0) as u8;
148
149 match range {
150 0 => Self::new(cm_scaled, xm_scaled, m_scaled, hsva.a),
151 1 => Self::new(xm_scaled, cm_scaled, m_scaled, hsva.a),
152 2 => Self::new(m_scaled, cm_scaled, xm_scaled, hsva.a),
153 3 => Self::new(m_scaled, xm_scaled, cm_scaled, hsva.a),
154 4 => Self::new(xm_scaled, m_scaled, cm_scaled, hsva.a),
155 _ => Self::new(cm_scaled, m_scaled, xm_scaled, hsva.a),
156 }
157 }
158}
159
160impl Add for Hsva {
161 type Output = Self;
162 fn add(self, rhs: Self) -> Self::Output {
163 Hsva::new(
164 (self.h + rhs.h) % 360.,
165 (self.s + rhs.s).clamp(0., 1.),
166 (self.v + rhs.v).clamp(0., 1.),
167 self.a.saturating_add(rhs.a),
168 )
169 }
170}
171
172pub fn approx(a: f64, b: f64) -> bool {
173 if a == b {
174 return true;
175 }
176 let eps = 1e-2;
177 let abs_a = a.abs();
178 let abs_b = b.abs();
179 let diff = (abs_a - abs_b).abs();
180 if a == 0.0 || b == 0.0 || abs_a + abs_b < f64::EPSILON {
181 diff < eps * f64::EPSILON
182 } else {
183 diff / (abs_a + abs_b).min(f64::MAX) < eps
184 }
185}
186
187#[derive(Debug, Clone, Copy, PartialEq, SmartDefault)]
188pub enum Color {
189 #[default]
190 None,
191 Auto,
192 Rgba(Rgba),
193 Hsva(Hsva),
194}
195
196impl Color {
197 pub fn skip_ser(&self) -> bool {
198 matches!(self, Self::None | Self::Auto)
199 }
200}
201
202impl Add for Color {
203 type Output = Color;
204 fn add(self, rhs: Self) -> Self::Output {
205 match (self, rhs) {
206 (x, Self::None | Self::Auto) | (Self::None | Self::Auto, x) => x,
208 (Color::Hsva(hsva1), Color::Hsva(hsva2)) => Color::Hsva(hsva1 + hsva2),
210 (Color::Rgba(rgba1), Color::Rgba(rgba2)) => Color::Rgba(rgba1 + rgba2),
212 (Color::Hsva(hsva), Color::Rgba(rgba)) | (Color::Rgba(rgba), Color::Hsva(hsva)) => {
215 Color::Hsva(hsva + rgba.into())
216 }
217 }
218 }
219}
220
221impl FromStr for Color {
222 type Err = Error;
223
224 fn from_str(color: &str) -> Result<Self, Self::Err> {
225 Ok(if color == "none" || color.is_empty() {
226 Color::None
227 } else if color == "auto" {
228 Color::Auto
229 } else if color.starts_with("hsv:") {
230 let err_msg = || format!("'{color}' is not a valid HSVA color");
231 let color = color.split_at(4).1;
232 let mut components = color.split(':').map(|x| x.parse::<f64>().or_error(err_msg));
233 let h = components.next().or_error(err_msg)??;
234 let s = components.next().or_error(err_msg)??;
235 let v = components.next().or_error(err_msg)??;
236 let a = components.next().unwrap_or(Ok(100.))?;
237 Color::Hsva(Hsva::new(h, s / 100., v / 100., (a / 100. * 255.) as u8))
238 } else if color.starts_with("x:") {
239 let name = color.split_at(2).1;
240 xresources::get_color(name)?
241 .or_error(|| format!("color '{name}' not defined in ~/.Xresources"))?
242 .parse()
243 .or_error(|| format!("invalid color definition '{name}'"))?
244 } else if color.starts_with("sway:") {
245 let name = color.split_at(5).1;
246 sway::get_color(name)?
247 .parse()
248 .or_error(|| format!("invalid color definition '{name}'"))?
249 } else {
250 let err_msg = || format!("'{color}' is not a valid RGBA color");
251 let rgb = color.get(1..7).or_error(err_msg)?;
252 let a = color.get(7..9).unwrap_or("FF");
253 Color::Rgba(Rgba::from_hex(
254 (u32::from_str_radix(rgb, 16).or_error(err_msg)? << 8)
255 + u32::from_str_radix(a, 16).or_error(err_msg)?,
256 ))
257 })
258 }
259}
260
261impl Serialize for Color {
262 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
263 where
264 S: Serializer,
265 {
266 let format_rgba =
267 |rgba: Rgba| format!("#{:02X}{:02X}{:02X}{:02X}", rgba.r, rgba.g, rgba.b, rgba.a);
268 match *self {
269 Self::None | Self::Auto => serializer.serialize_none(),
270 Self::Rgba(rgba) => serializer.serialize_str(&format_rgba(rgba)),
271 Self::Hsva(hsva) => serializer.serialize_str(&format_rgba(hsva.into())),
272 }
273 }
274}
275
276impl<'de> Deserialize<'de> for Color {
277 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
278 where
279 D: Deserializer<'de>,
280 {
281 struct ColorVisitor;
282
283 impl Visitor<'_> for ColorVisitor {
284 type Value = Color;
285
286 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
287 formatter.write_str("color")
288 }
289
290 fn visit_str<E>(self, s: &str) -> Result<Color, E>
291 where
292 E: de::Error,
293 {
294 s.parse().serde_error()
295 }
296 }
297
298 deserializer.deserialize_any(ColorVisitor)
299 }
300}