From e7d928a065eb63bb4ea1fb864c69c1cae8cc763b Mon Sep 17 00:00:00 2001 From: LGUG2Z Date: Sun, 25 Feb 2024 10:36:58 -0800 Subject: [PATCH] feat(config): allow colours in both rgb and hex This commit introduces the ability for users to specify colours in the static config file in either RGB or Hex format. This is done by introducing a new enum, Colour, which provides variants wrapping the internal Rgb type, and the HexColour type from the hex_colour crate. HexColour itself is wrapped in a new struct, Hex, in order to allow the implementation of the JsonSchema trait. A number of From implementations have been done in order to make working with the Colour enum more ergonomic. Ultimately, the core representation for colours in komorebi remains the Rgb struct, any and Hex-specified colours will be converted to this Rgb type before being converted to u32 to pass them on to various Win32 API calls. --- Cargo.lock | 18 ++++ komorebi/Cargo.toml | 13 +-- komorebi/src/colour.rs | 103 +++++++++++++++++++ komorebi/src/lib.rs | 1 + komorebi/src/process_command.rs | 9 +- komorebi/src/static_config.rs | 54 +++------- schema.json | 171 ++++++++++++++++++-------------- 7 files changed, 245 insertions(+), 124 deletions(-) create mode 100644 komorebi/src/colour.rs diff --git a/Cargo.lock b/Cargo.lock index e5435d74..bd4bf85f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,6 +74,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "autocfg" version = "1.1.0" @@ -590,6 +596,17 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "379dada1584ad501b383485dd706b8afb7a70fcbc7f4da7d780638a5a6124a60" +[[package]] +name = "hex_color" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d37f101bf4c633f7ca2e4b5e136050314503dd198e78e325ea602c327c484ef0" +dependencies = [ + "arrayvec", + "rand", + "serde", +] + [[package]] name = "home" version = "0.5.9" @@ -795,6 +812,7 @@ dependencies = [ "ctrlc", "dirs", "getset", + "hex_color", "hotwatch", "komorebi-core", "lazy_static", diff --git a/komorebi/Cargo.toml b/komorebi/Cargo.toml index 5ee5b8d9..7aabcaaa 100644 --- a/komorebi/Cargo.toml +++ b/komorebi/Cargo.toml @@ -15,10 +15,13 @@ komorebi-core = { path = "../komorebi-core" } bitflags = "2" clap = { version = "4", features = ["derive"] } +color-eyre = { workspace = true } crossbeam-channel = "0.5" crossbeam-utils = "0.8" ctrlc = "3" +dirs = { workspace = true } getset = "0.1" +hex_color = { version = "3", features = ["serde"] } hotwatch = "0.4" lazy_static = "1" miow = "0.5" @@ -38,14 +41,12 @@ tracing-appender = "0.2" tracing-subscriber = { version = "0.3", features = ["env-filter"] } uds_windows = "1" which = "5" +widestring = "1" +windows = { workspace = true } +windows-implement = { workspace = true } +windows-interface = { workspace = true } winput = "0.2" winreg = "0.52" -windows-interface = { workspace = true } -windows-implement = { workspace = true } -windows = { workspace = true } -color-eyre = { workspace = true } -dirs = { workspace = true } -widestring = "1" [features] deadlock_detection = [] diff --git a/komorebi/src/colour.rs b/komorebi/src/colour.rs new file mode 100644 index 00000000..092b4309 --- /dev/null +++ b/komorebi/src/colour.rs @@ -0,0 +1,103 @@ +use hex_color::HexColor; +use schemars::gen::SchemaGenerator; +use schemars::schema::InstanceType; +use schemars::schema::Schema; +use schemars::schema::SchemaObject; +use schemars::JsonSchema; +use serde::Deserialize; +use serde::Serialize; + +#[derive(Debug, Copy, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(untagged)] +pub enum Colour { + /// Colour represented as RGB + Rgb(Rgb), + /// Colour represented as Hex + Hex(Hex), +} + +impl From for Colour { + fn from(value: Rgb) -> Self { + Self::Rgb(value) + } +} + +impl From for Colour { + fn from(value: u32) -> Self { + Self::Rgb(Rgb::from(value)) + } +} + +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +pub struct Hex(HexColor); + +impl JsonSchema for Hex { + fn schema_name() -> String { + String::from("Hex") + } + + fn json_schema(_: &mut SchemaGenerator) -> Schema { + SchemaObject { + instance_type: Some(InstanceType::String.into()), + ..Default::default() + } + .into() + } +} + +impl From for u32 { + fn from(value: Colour) -> Self { + match value { + Colour::Rgb(val) => val.into(), + Colour::Hex(val) => (Rgb::from(val)).into(), + } + } +} + +#[derive(Debug, Copy, Clone, Serialize, Deserialize, JsonSchema)] +pub struct Rgb { + /// Red + pub r: u32, + /// Green + pub g: u32, + /// Blue + pub b: u32, +} + +impl Rgb { + pub fn new(r: u32, g: u32, b: u32) -> Self { + Self { r, g, b } + } +} + +impl From for Rgb { + fn from(value: Hex) -> Self { + value.0.into() + } +} + +impl From for Rgb { + fn from(value: HexColor) -> Self { + Self { + r: value.r as u32, + g: value.g as u32, + b: value.b as u32, + } + } +} + +impl From for u32 { + fn from(value: Rgb) -> Self { + value.r | (value.g << 8) | (value.b << 16) + } +} + +impl From for Rgb { + fn from(value: u32) -> Self { + Self { + r: value & 0xff, + g: value >> 8 & 0xff, + b: value >> 16 & 0xff, + } + } +} diff --git a/komorebi/src/lib.rs b/komorebi/src/lib.rs index 29d8bdac..f32816be 100644 --- a/komorebi/src/lib.rs +++ b/komorebi/src/lib.rs @@ -2,6 +2,7 @@ pub mod border; pub mod com; #[macro_use] pub mod ring; +pub mod colour; pub mod container; pub mod hidden; pub mod monitor; diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index 69028a67..7435aac8 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -38,6 +38,7 @@ use komorebi_core::WindowContainerBehaviour; use komorebi_core::WindowKind; use crate::border::Border; +use crate::colour::Rgb; use crate::current_virtual_desktop; use crate::notify_subscribers; use crate::static_config::StaticConfig; @@ -1234,14 +1235,14 @@ impl WindowManager { SocketMessage::ActiveWindowBorderColour(kind, r, g, b) => { match kind { WindowKind::Single => { - BORDER_COLOUR_SINGLE.store(r | (g << 8) | (b << 16), Ordering::SeqCst); - BORDER_COLOUR_CURRENT.store(r | (g << 8) | (b << 16), Ordering::SeqCst); + BORDER_COLOUR_SINGLE.store(Rgb::new(r, g, b).into(), Ordering::SeqCst); + BORDER_COLOUR_CURRENT.store(Rgb::new(r, g, b).into(), Ordering::SeqCst); } WindowKind::Stack => { - BORDER_COLOUR_STACK.store(r | (g << 8) | (b << 16), Ordering::SeqCst); + BORDER_COLOUR_STACK.store(Rgb::new(r, g, b).into(), Ordering::SeqCst); } WindowKind::Monocle => { - BORDER_COLOUR_MONOCLE.store(r | (g << 8) | (b << 16), Ordering::SeqCst); + BORDER_COLOUR_MONOCLE.store(Rgb::new(r, g, b).into(), Ordering::SeqCst); } } diff --git a/komorebi/src/static_config.rs b/komorebi/src/static_config.rs index bbd0d15c..50b012ed 100644 --- a/komorebi/src/static_config.rs +++ b/komorebi/src/static_config.rs @@ -1,4 +1,5 @@ use crate::border::Border; +use crate::colour::Colour; use crate::current_virtual_desktop; use crate::monitor::Monitor; use crate::ring::Ring; @@ -28,6 +29,7 @@ use crate::OBJECT_NAME_CHANGE_ON_LAUNCH; use crate::REGEX_IDENTIFIERS; use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS; use crate::WORKSPACE_RULES; + use color_eyre::Result; use crossbeam_channel::Receiver; use hotwatch::notify::DebouncedEvent; @@ -62,34 +64,14 @@ use std::sync::Arc; use uds_windows::UnixListener; use uds_windows::UnixStream; -#[derive(Debug, Serialize, Deserialize, JsonSchema)] -pub struct Rgb { - /// Red - pub r: u32, - /// Green - pub g: u32, - /// Blue - pub b: u32, -} - -impl From for Rgb { - fn from(value: u32) -> Self { - Self { - r: value & 0xff, - g: value >> 8 & 0xff, - b: value >> 16 & 0xff, - } - } -} - #[derive(Debug, Serialize, Deserialize, JsonSchema)] pub struct ActiveWindowBorderColours { /// Border colour when the container contains a single window - pub single: Rgb, + pub single: Colour, /// Border colour when the container contains multiple windows - pub stack: Rgb, + pub stack: Colour, /// Border colour when the container is in monocle mode - pub monocle: Rgb, + pub monocle: Colour, } #[derive(Debug, Serialize, Deserialize, JsonSchema)] @@ -377,13 +359,13 @@ impl From<&WindowManager> for StaticConfig { None } else { Option::from(ActiveWindowBorderColours { - single: Rgb::from(BORDER_COLOUR_SINGLE.load(Ordering::SeqCst)), - stack: Rgb::from(if BORDER_COLOUR_STACK.load(Ordering::SeqCst) == 0 { + single: Colour::from(BORDER_COLOUR_SINGLE.load(Ordering::SeqCst)), + stack: Colour::from(if BORDER_COLOUR_STACK.load(Ordering::SeqCst) == 0 { BORDER_COLOUR_SINGLE.load(Ordering::SeqCst) } else { BORDER_COLOUR_STACK.load(Ordering::SeqCst) }), - monocle: Rgb::from(if BORDER_COLOUR_MONOCLE.load(Ordering::SeqCst) == 0 { + monocle: Colour::from(if BORDER_COLOUR_MONOCLE.load(Ordering::SeqCst) == 0 { BORDER_COLOUR_SINGLE.load(Ordering::SeqCst) } else { BORDER_COLOUR_MONOCLE.load(Ordering::SeqCst) @@ -487,22 +469,10 @@ impl StaticConfig { ); if let Some(colours) = &self.active_window_border_colours { - BORDER_COLOUR_SINGLE.store( - colours.single.r | (colours.single.g << 8) | (colours.single.b << 16), - Ordering::SeqCst, - ); - BORDER_COLOUR_CURRENT.store( - colours.single.r | (colours.single.g << 8) | (colours.single.b << 16), - Ordering::SeqCst, - ); - BORDER_COLOUR_STACK.store( - colours.stack.r | (colours.stack.g << 8) | (colours.stack.b << 16), - Ordering::SeqCst, - ); - BORDER_COLOUR_MONOCLE.store( - colours.monocle.r | (colours.monocle.g << 8) | (colours.monocle.b << 16), - Ordering::SeqCst, - ); + BORDER_COLOUR_SINGLE.store(u32::from(colours.single), Ordering::SeqCst); + BORDER_COLOUR_CURRENT.store(u32::from(colours.single), Ordering::SeqCst); + BORDER_COLOUR_STACK.store(u32::from(colours.stack), Ordering::SeqCst); + BORDER_COLOUR_MONOCLE.store(u32::from(colours.monocle), Ordering::SeqCst); } let mut float_identifiers = FLOAT_IDENTIFIERS.lock(); diff --git a/schema.json b/schema.json index 62a2aeb4..91c73e0b 100644 --- a/schema.json +++ b/schema.json @@ -19,90 +19,117 @@ "properties": { "monocle": { "description": "Border colour when the container is in monocle mode", - "type": "object", - "required": [ - "b", - "g", - "r" - ], - "properties": { - "b": { - "description": "Blue", - "type": "integer", - "format": "uint32", - "minimum": 0.0 - }, - "g": { - "description": "Green", - "type": "integer", - "format": "uint32", - "minimum": 0.0 + "anyOf": [ + { + "description": "Colour represented as RGB", + "type": "object", + "required": [ + "b", + "g", + "r" + ], + "properties": { + "b": { + "description": "Blue", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "g": { + "description": "Green", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "r": { + "description": "Red", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } }, - "r": { - "description": "Red", - "type": "integer", - "format": "uint32", - "minimum": 0.0 + { + "description": "Colour represented as Hex", + "type": "string" } - } + ] }, "single": { "description": "Border colour when the container contains a single window", - "type": "object", - "required": [ - "b", - "g", - "r" - ], - "properties": { - "b": { - "description": "Blue", - "type": "integer", - "format": "uint32", - "minimum": 0.0 - }, - "g": { - "description": "Green", - "type": "integer", - "format": "uint32", - "minimum": 0.0 + "anyOf": [ + { + "description": "Colour represented as RGB", + "type": "object", + "required": [ + "b", + "g", + "r" + ], + "properties": { + "b": { + "description": "Blue", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "g": { + "description": "Green", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "r": { + "description": "Red", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } }, - "r": { - "description": "Red", - "type": "integer", - "format": "uint32", - "minimum": 0.0 + { + "description": "Colour represented as Hex", + "type": "string" } - } + ] }, "stack": { "description": "Border colour when the container contains multiple windows", - "type": "object", - "required": [ - "b", - "g", - "r" - ], - "properties": { - "b": { - "description": "Blue", - "type": "integer", - "format": "uint32", - "minimum": 0.0 - }, - "g": { - "description": "Green", - "type": "integer", - "format": "uint32", - "minimum": 0.0 + "anyOf": [ + { + "description": "Colour represented as RGB", + "type": "object", + "required": [ + "b", + "g", + "r" + ], + "properties": { + "b": { + "description": "Blue", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "g": { + "description": "Green", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "r": { + "description": "Red", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } }, - "r": { - "description": "Red", - "type": "integer", - "format": "uint32", - "minimum": 0.0 + { + "description": "Colour represented as Hex", + "type": "string" } - } + ] } } },