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 + "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 + } + } }, - "g": { - "description": "Green", - "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 + "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 + } + } }, - "g": { - "description": "Green", - "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 + "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 + } + } }, - "g": { - "description": "Green", - "type": "integer", - "format": "uint32", - "minimum": 0.0 - }, - "r": { - "description": "Red", - "type": "integer", - "format": "uint32", - "minimum": 0.0 + { + "description": "Colour represented as Hex", + "type": "string" } - } + ] } } },