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<T> 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.
This commit is contained in:
LGUG2Z
2024-02-25 10:36:58 -08:00
parent 608ec03047
commit e7d928a065
7 changed files with 245 additions and 124 deletions

18
Cargo.lock generated
View File

@@ -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",

View File

@@ -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 = []

103
komorebi/src/colour.rs Normal file
View File

@@ -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<Rgb> for Colour {
fn from(value: Rgb) -> Self {
Self::Rgb(value)
}
}
impl From<u32> 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<Colour> 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<Hex> for Rgb {
fn from(value: Hex) -> Self {
value.0.into()
}
}
impl From<HexColor> 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<Rgb> for u32 {
fn from(value: Rgb) -> Self {
value.r | (value.g << 8) | (value.b << 16)
}
}
impl From<u32> for Rgb {
fn from(value: u32) -> Self {
Self {
r: value & 0xff,
g: value >> 8 & 0xff,
b: value >> 16 & 0xff,
}
}
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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<u32> 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();

View File

@@ -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"
}
}
]
}
}
},