diff --git a/Cargo.lock b/Cargo.lock index 8393a42..2055b18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,6 +38,15 @@ version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "arc-swap" version = "1.7.1" @@ -123,6 +132,12 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "by_address" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" + [[package]] name = "byteorder" version = "1.5.0" @@ -267,6 +282,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "fast-srgb8" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" + [[package]] name = "fastrand" version = "2.1.1" @@ -485,12 +506,6 @@ dependencies = [ "libc", ] -[[package]] -name = "lab" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf36173d4167ed999940f804952e6b08197cae5ad5d572eb4db150ce8ad5d58f" - [[package]] name = "libc" version = "0.2.158" @@ -675,6 +690,30 @@ dependencies = [ "num-traits", ] +[[package]] +name = "palette" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" +dependencies = [ + "approx", + "fast-srgb8", + "palette_derive", + "phf", +] + +[[package]] +name = "palette_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30" +dependencies = [ + "by_address", + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "parking" version = "2.2.0" @@ -704,6 +743,48 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -927,6 +1008,12 @@ dependencies = [ "libc", ] +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "slab" version = "0.4.9" @@ -954,15 +1041,15 @@ dependencies = [ [[package]] name = "sway-flash-indicator" -version = "0.4.3" +version = "0.5.0" dependencies = [ "directories", "futures-util", - "lab", "log", "log4rs", "notify", "notify-debouncer-mini", + "palette", "serde", "swayipc-async", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index 4fb755e..51f45b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sway-flash-indicator" -version = "0.4.3" +version = "0.5.0" edition = "2021" [dependencies] @@ -9,10 +9,10 @@ log4rs = "1.3.0" swayipc-async = "2.0.3" tokio = { version = "1.40.0", features = ["full"] } futures-util = "0.3.30" -lab = "0.11.0" directories = "5.0.1" toml = "0.8.19" serde = { version = "1.0.209", features = ["derive"] } thiserror = "1.0.63" notify-debouncer-mini = "0.4.1" notify = "6.1.1" +palette = "0.7.6" diff --git a/packaging/PKGBUILD b/packaging/PKGBUILD index b282fa4..b75176b 100644 --- a/packaging/PKGBUILD +++ b/packaging/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Alex Janka pkgname=sway-flash-indicator -pkgver=0.4.3 +pkgver=0.5.0 pkgrel=1 pkgdesc="flashes sway indicator border rather than always showing it" arch=('x86_64' 'aarch64') diff --git a/src/colour.rs b/src/colour.rs index 0304946..94cad92 100644 --- a/src/colour.rs +++ b/src/colour.rs @@ -1,11 +1,23 @@ -use lab::Lab; +use palette::FromColor; use crate::prelude::*; -pub fn parse_hex(hex: &str) -> Res { +pub type Format = palette::Oklab; +pub type RgbFormat = palette::Srgb; + +pub fn parse_hex(hex: &str) -> Res { let hex = hex.strip_prefix('#').unwrap_or(hex); let r = u8::from_str_radix(&hex[..2], 16).map_err(|_| Error::HexParse)?; let g = u8::from_str_radix(&hex[2..4], 16).map_err(|_| Error::HexParse)?; let b = u8::from_str_radix(&hex[4..], 16).map_err(|_| Error::HexParse)?; - Ok(Lab::from_rgb(&[r, g, b])) + let rgb_u8 = palette::rgb::Srgb::new(r, g, b); + let rgb_f32 = palette::rgb::Srgb::::from(rgb_u8); + let oklab = palette::Oklab::from_color(rgb_f32); + + Ok(oklab) +} + +pub fn oklab_to_hex(oklab: &Format) -> String { + let (r, g, b) = RgbFormat::from(palette::Srgb::from_color(*oklab)).into_components(); + format!("#{r:02x}{g:02x}{b:02x}") } diff --git a/src/config.rs b/src/config.rs index 0c2cbde..996fee3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,3 @@ -use lab::Lab; use serde::{Deserialize, Serialize}; use crate::prelude::*; @@ -10,7 +9,7 @@ pub struct Config { pub frames_anim: u32, pub refresh_rate: u64, #[serde(serialize_with = "lab_ser", deserialize_with = "lab_de")] - pub flash_colour: Lab, + pub flash_colour: colour::Format, pub autosplit_enabled: bool, pub autosplit_ratio: f64, pub output_blocklist: Vec, @@ -128,11 +127,7 @@ impl Default for Config { frames_delay: 10, frames_anim: 20, refresh_rate: 60, - flash_colour: Lab { - l: 53.2, - a: 80.1, - b: 67.2, - }, + flash_colour: colour::parse_hex("#ff0000").unwrap(), autosplit_enabled: true, autosplit_ratio: 1.0, output_blocklist: Vec::new(), @@ -141,14 +136,16 @@ impl Default for Config { } } -fn lab_ser(colour: &Lab, serializer: S) -> Result { - let [r, g, b] = colour.to_rgb(); - format!("#{r:02x}{g:02x}{b:02x}").serialize(serializer) +fn lab_ser( + colour: &colour::Format, + serializer: S, +) -> Result { + colour::oklab_to_hex(colour).serialize(serializer) } -fn lab_de<'a, D: serde::Deserializer<'a>>(deserializer: D) -> Result { +fn lab_de<'a, D: serde::Deserializer<'a>>(deserializer: D) -> Result { use serde::de::Error; let hex = String::deserialize(deserializer)?; - crate::colour::parse_hex(&hex).map_err(|_| D::Error::custom("couldn't parse hex")) + colour::parse_hex(&hex).map_err(|_| D::Error::custom("couldn't parse hex")) } diff --git a/src/flash.rs b/src/flash.rs index 4259970..1308dec 100644 --- a/src/flash.rs +++ b/src/flash.rs @@ -6,7 +6,7 @@ pub async fn interpolate_task() -> Res<()> { let to = crate::DEFAULT_BORDER.get().ok_or(Error::NoMatchingConfig)?; let config = CONFIG.get().await; let per_frame = 1.0 / config.frames_anim as f32; - let (d_l, d_a, d_b) = ( + let (d_l, d_chroma, d_hue) = ( (to.l - config.flash_colour.l) * per_frame, (to.a - config.flash_colour.a) * per_frame, (to.b - config.flash_colour.b) * per_frame, @@ -20,8 +20,8 @@ pub async fn interpolate_task() -> Res<()> { tokio::time::sleep(dur_per_frame * config.frames_delay).await; for _ in 0..config.frames_anim { c.l += d_l; - c.a += d_a; - c.b += d_b; + c.a += d_chroma; + c.b += d_hue; connection.run_command(set_command(&c)?).await?; @@ -37,10 +37,11 @@ pub async fn interpolate_task() -> Res<()> { Ok(()) } -fn set_command(colour: &lab::Lab) -> Res { - let [r, g, b] = colour.to_rgb(); +fn set_command(colour: &colour::Format) -> Res { + let hex = colour::oklab_to_hex(colour); + Ok(format!( - "{} #{r:02x}{g:02x}{b:02x}", + "{} {hex}", crate::DEFAULT_COLOURS_NO_INDICATOR .get() .ok_or(Error::NoMatchingConfig)? diff --git a/src/main.rs b/src/main.rs index 81032f4..cac4173 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,4 @@ use futures_util::StreamExt; -use lab::Lab; mod colour; mod config; @@ -8,6 +7,7 @@ mod flash; mod logger; pub mod prelude { + pub(crate) use crate::colour; pub use crate::error::*; pub use crate::CONFIG; } @@ -18,7 +18,7 @@ pub static CONFIG: config::ConfigHandle = config::ConfigHandle::new(); static DEFAULT_COLOURS: tokio::sync::OnceCell = tokio::sync::OnceCell::const_new(); static DEFAULT_COLOURS_NO_INDICATOR: tokio::sync::OnceCell = tokio::sync::OnceCell::const_new(); -static DEFAULT_BORDER: tokio::sync::OnceCell = tokio::sync::OnceCell::const_new(); +static DEFAULT_BORDER: tokio::sync::OnceCell = tokio::sync::OnceCell::const_new(); #[tokio::main] async fn main() -> Res<()> {