switch to oklab

This commit is contained in:
Alex Janka 2024-09-05 09:36:49 +10:00
parent 77095d6653
commit f49bf7b949
7 changed files with 131 additions and 34 deletions

103
Cargo.lock generated
View file

@ -38,6 +38,15 @@ version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" 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]] [[package]]
name = "arc-swap" name = "arc-swap"
version = "1.7.1" version = "1.7.1"
@ -123,6 +132,12 @@ version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "by_address"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.5.0" version = "1.5.0"
@ -267,6 +282,12 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "fast-srgb8"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1"
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.1.1" version = "2.1.1"
@ -485,12 +506,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "lab"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf36173d4167ed999940f804952e6b08197cae5ad5d572eb4db150ce8ad5d58f"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.158" version = "0.2.158"
@ -675,6 +690,30 @@ dependencies = [
"num-traits", "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]] [[package]]
name = "parking" name = "parking"
version = "2.2.0" version = "2.2.0"
@ -704,6 +743,48 @@ dependencies = [
"windows-targets 0.52.6", "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]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.14" version = "0.2.14"
@ -927,6 +1008,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "siphasher"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.9" version = "0.4.9"
@ -954,15 +1041,15 @@ dependencies = [
[[package]] [[package]]
name = "sway-flash-indicator" name = "sway-flash-indicator"
version = "0.4.3" version = "0.5.0"
dependencies = [ dependencies = [
"directories", "directories",
"futures-util", "futures-util",
"lab",
"log", "log",
"log4rs", "log4rs",
"notify", "notify",
"notify-debouncer-mini", "notify-debouncer-mini",
"palette",
"serde", "serde",
"swayipc-async", "swayipc-async",
"thiserror", "thiserror",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "sway-flash-indicator" name = "sway-flash-indicator"
version = "0.4.3" version = "0.5.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
@ -9,10 +9,10 @@ log4rs = "1.3.0"
swayipc-async = "2.0.3" swayipc-async = "2.0.3"
tokio = { version = "1.40.0", features = ["full"] } tokio = { version = "1.40.0", features = ["full"] }
futures-util = "0.3.30" futures-util = "0.3.30"
lab = "0.11.0"
directories = "5.0.1" directories = "5.0.1"
toml = "0.8.19" toml = "0.8.19"
serde = { version = "1.0.209", features = ["derive"] } serde = { version = "1.0.209", features = ["derive"] }
thiserror = "1.0.63" thiserror = "1.0.63"
notify-debouncer-mini = "0.4.1" notify-debouncer-mini = "0.4.1"
notify = "6.1.1" notify = "6.1.1"
palette = "0.7.6"

View file

@ -1,7 +1,7 @@
# Maintainer: Alex Janka <alex@alexjanka.com> # Maintainer: Alex Janka <alex@alexjanka.com>
pkgname=sway-flash-indicator pkgname=sway-flash-indicator
pkgver=0.4.3 pkgver=0.5.0
pkgrel=1 pkgrel=1
pkgdesc="flashes sway indicator border rather than always showing it" pkgdesc="flashes sway indicator border rather than always showing it"
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')

View file

@ -1,11 +1,23 @@
use lab::Lab; use palette::FromColor;
use crate::prelude::*; use crate::prelude::*;
pub fn parse_hex(hex: &str) -> Res<Lab> { pub type Format = palette::Oklab;
pub type RgbFormat = palette::Srgb<u8>;
pub fn parse_hex(hex: &str) -> Res<Format> {
let hex = hex.strip_prefix('#').unwrap_or(hex); let hex = hex.strip_prefix('#').unwrap_or(hex);
let r = u8::from_str_radix(&hex[..2], 16).map_err(|_| Error::HexParse)?; 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 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)?; 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::<f32>::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}")
} }

View file

@ -1,4 +1,3 @@
use lab::Lab;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::prelude::*; use crate::prelude::*;
@ -10,7 +9,7 @@ pub struct Config {
pub frames_anim: u32, pub frames_anim: u32,
pub refresh_rate: u64, pub refresh_rate: u64,
#[serde(serialize_with = "lab_ser", deserialize_with = "lab_de")] #[serde(serialize_with = "lab_ser", deserialize_with = "lab_de")]
pub flash_colour: Lab, pub flash_colour: colour::Format,
pub autosplit_enabled: bool, pub autosplit_enabled: bool,
pub autosplit_ratio: f64, pub autosplit_ratio: f64,
pub output_blocklist: Vec<String>, pub output_blocklist: Vec<String>,
@ -128,11 +127,7 @@ impl Default for Config {
frames_delay: 10, frames_delay: 10,
frames_anim: 20, frames_anim: 20,
refresh_rate: 60, refresh_rate: 60,
flash_colour: Lab { flash_colour: colour::parse_hex("#ff0000").unwrap(),
l: 53.2,
a: 80.1,
b: 67.2,
},
autosplit_enabled: true, autosplit_enabled: true,
autosplit_ratio: 1.0, autosplit_ratio: 1.0,
output_blocklist: Vec::new(), output_blocklist: Vec::new(),
@ -141,14 +136,16 @@ impl Default for Config {
} }
} }
fn lab_ser<S: serde::Serializer>(colour: &Lab, serializer: S) -> Result<S::Ok, S::Error> { fn lab_ser<S: serde::Serializer>(
let [r, g, b] = colour.to_rgb(); colour: &colour::Format,
format!("#{r:02x}{g:02x}{b:02x}").serialize(serializer) serializer: S,
) -> Result<S::Ok, S::Error> {
colour::oklab_to_hex(colour).serialize(serializer)
} }
fn lab_de<'a, D: serde::Deserializer<'a>>(deserializer: D) -> Result<Lab, D::Error> { fn lab_de<'a, D: serde::Deserializer<'a>>(deserializer: D) -> Result<colour::Format, D::Error> {
use serde::de::Error; use serde::de::Error;
let hex = String::deserialize(deserializer)?; 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"))
} }

View file

@ -6,7 +6,7 @@ pub async fn interpolate_task() -> Res<()> {
let to = crate::DEFAULT_BORDER.get().ok_or(Error::NoMatchingConfig)?; let to = crate::DEFAULT_BORDER.get().ok_or(Error::NoMatchingConfig)?;
let config = CONFIG.get().await; let config = CONFIG.get().await;
let per_frame = 1.0 / config.frames_anim as f32; 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.l - config.flash_colour.l) * per_frame,
(to.a - config.flash_colour.a) * per_frame, (to.a - config.flash_colour.a) * per_frame,
(to.b - config.flash_colour.b) * 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; tokio::time::sleep(dur_per_frame * config.frames_delay).await;
for _ in 0..config.frames_anim { for _ in 0..config.frames_anim {
c.l += d_l; c.l += d_l;
c.a += d_a; c.a += d_chroma;
c.b += d_b; c.b += d_hue;
connection.run_command(set_command(&c)?).await?; connection.run_command(set_command(&c)?).await?;
@ -37,10 +37,11 @@ pub async fn interpolate_task() -> Res<()> {
Ok(()) Ok(())
} }
fn set_command(colour: &lab::Lab) -> Res<String> { fn set_command(colour: &colour::Format) -> Res<String> {
let [r, g, b] = colour.to_rgb(); let hex = colour::oklab_to_hex(colour);
Ok(format!( Ok(format!(
"{} #{r:02x}{g:02x}{b:02x}", "{} {hex}",
crate::DEFAULT_COLOURS_NO_INDICATOR crate::DEFAULT_COLOURS_NO_INDICATOR
.get() .get()
.ok_or(Error::NoMatchingConfig)? .ok_or(Error::NoMatchingConfig)?

View file

@ -1,5 +1,4 @@
use futures_util::StreamExt; use futures_util::StreamExt;
use lab::Lab;
mod colour; mod colour;
mod config; mod config;
@ -8,6 +7,7 @@ mod flash;
mod logger; mod logger;
pub mod prelude { pub mod prelude {
pub(crate) use crate::colour;
pub use crate::error::*; pub use crate::error::*;
pub use crate::CONFIG; pub use crate::CONFIG;
} }
@ -18,7 +18,7 @@ pub static CONFIG: config::ConfigHandle = config::ConfigHandle::new();
static DEFAULT_COLOURS: tokio::sync::OnceCell<String> = tokio::sync::OnceCell::const_new(); static DEFAULT_COLOURS: tokio::sync::OnceCell<String> = tokio::sync::OnceCell::const_new();
static DEFAULT_COLOURS_NO_INDICATOR: tokio::sync::OnceCell<String> = static DEFAULT_COLOURS_NO_INDICATOR: tokio::sync::OnceCell<String> =
tokio::sync::OnceCell::const_new(); tokio::sync::OnceCell::const_new();
static DEFAULT_BORDER: tokio::sync::OnceCell<Lab> = tokio::sync::OnceCell::const_new(); static DEFAULT_BORDER: tokio::sync::OnceCell<colour::Format> = tokio::sync::OnceCell::const_new();
#[tokio::main] #[tokio::main]
async fn main() -> Res<()> { async fn main() -> Res<()> {