add panel using cnx

This commit is contained in:
emilis 2024-03-01 18:06:52 +00:00
parent 0174eeb4cc
commit 4036ed42f0
13 changed files with 2072 additions and 552 deletions

886
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,12 @@ toml = "0.8.8"
which = "6.0.0" which = "6.0.0"
log = "0.4" log = "0.4"
pretty_env_logger = "0.5" pretty_env_logger = "0.5"
cnx = { git = "https://github.com/mjkillough/cnx.git", rev = "7845d99baa296901171c083db61588a62f9a8b34" }
[dev-dependencies] [dev-dependencies]
pretty_assertions = "1.4.0" pretty_assertions = "1.4.0"
[profile.release]
opt-level = 3
strip = "debuginfo"
lto = "fat"

42
src/cmd.rs Normal file
View File

@ -0,0 +1,42 @@
use std::{os::unix::process::ExitStatusExt, process::Output};
use log::error;
use crate::hlwm::command::CommandError;
pub fn check_status(output: &Output) -> Result<(), CommandError> {
let exit_status = output.status;
if let Some(code) = exit_status.code() {
if code == 0 {
return Ok(());
} else {
let output = String::from_utf8(output.stdout.clone())
.unwrap_or_default()
.trim()
.to_string();
error!("command failed with error code [{code}]");
if !output.is_empty() {
error!("command output: {output}");
}
return Err(CommandError::StatusCode(
code,
if output.is_empty() {
None
} else {
Some(output)
},
));
}
}
if let Some(signal) = exit_status.signal() {
return Err(CommandError::KilledBySignal {
signal,
core_dumped: exit_status.core_dumped(),
});
}
if let Some(signal) = exit_status.stopped_signal() {
return Err(CommandError::StoppedBySignal(signal));
}
Err(CommandError::OtherExitStatus(exit_status))
}

View File

@ -60,8 +60,13 @@ pub enum ConfigError {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Config { pub struct Config {
pub font: Option<String>, pub font: String,
pub font_bold: Option<String>, pub font_bold: String,
/// Pango font for use where pango fonts are
/// used (i.e. panel)
pub font_pango: String,
/// Bold version of `font_pango`
pub font_pango_bold: String,
pub mod_key: Key, pub mod_key: Key,
pub keybinds: Vec<Keybind>, pub keybinds: Vec<Keybind>,
pub mousebinds: Vec<Mousebind>, pub mousebinds: Vec<Mousebind>,
@ -210,9 +215,9 @@ impl Config {
fn attrs_set(&self) -> Result<Vec<HlwmCommand>, ConfigError> { fn attrs_set(&self) -> Result<Vec<HlwmCommand>, ConfigError> {
info!("loading attr settings command set"); info!("loading attr settings command set");
Ok([ Ok([
(Self::FONT, self.font.clone()), (Self::FONT, Some(self.font.clone())),
(Self::MOD_KEY, Some(self.mod_key.to_string())), (Self::MOD_KEY, Some(self.mod_key.to_string())),
(Self::FONT_BOLD, self.font_bold.clone()), (Self::FONT_BOLD, Some(self.font_bold.clone())),
( (
Self::SERVICES, Self::SERVICES,
if self.services.len() == 0 { if self.services.len() == 0 {
@ -393,16 +398,14 @@ impl Config {
Config { Config {
font: setting( font: setting(
Self::FONT, Self::FONT,
|f| -> Result<_, Infallible> { Ok(Some(f.to_string())) }, |f| -> Result<_, Infallible> { Ok(f.to_string()) },
default.font, default.font,
), ),
mod_key: setting(Self::MOD_KEY, Key::from_str, default.mod_key), mod_key: setting(Self::MOD_KEY, Key::from_str, default.mod_key),
font_bold: Some( font_bold: client
client .get_attr(ThemeAttr::TitleFont(String::new()).attr_path())
.get_attr(ThemeAttr::TitleFont(String::new()).attr_path()) .map(|a| a.to_string())
.map(|a| a.to_string()) .unwrap_or(default.font_bold),
.unwrap_or(default.font_bold.unwrap()),
),
services: setting( services: setting(
Self::SERVICES, Self::SERVICES,
|v| serde_json::de::from_str(v), |v| serde_json::de::from_str(v),
@ -508,6 +511,45 @@ pub struct Theme {
pub attributes: Vec<ThemeAttr>, pub attributes: Vec<ThemeAttr>,
} }
#[allow(unused)]
impl Theme {
pub fn active_color(&self) -> Option<Color> {
for attr in &self.attributes {
if let ThemeAttr::ActiveColor(col) = attr {
return Some(col.clone());
}
}
None
}
pub fn normal_color(&self) -> Option<Color> {
for attr in &self.attributes {
if let ThemeAttr::NormalColor(col) = attr {
return Some(col.clone());
}
}
None
}
pub fn urgent_color(&self) -> Option<Color> {
for attr in &self.attributes {
if let ThemeAttr::UrgentColor(col) = attr {
return Some(col.clone());
}
}
None
}
pub fn text_color(&self) -> Option<Color> {
for attr in &self.attributes {
if let ThemeAttr::TitleColor(col) = attr {
return Some(col.clone());
}
}
None
}
}
#[derive(Debug, Clone, Copy, PartialEq, Error)] #[derive(Debug, Clone, Copy, PartialEq, Error)]
pub enum InclusiveError { pub enum InclusiveError {
#[error("out of range")] #[error("out of range")]
@ -712,8 +754,8 @@ impl Default for Config {
Self { Self {
mod_key, mod_key,
font: Some(String::from("-*-fixed-medium-*-*-*-12-*-*-*-*-*-*-*")), font: String::from("-*-fixed-medium-*-*-*-12-*-*-*-*-*-*-*"),
font_bold: Some(font_bold.clone()), font_bold: font_bold.clone(),
keybinds: vec![ keybinds: vec![
Keybind::new( Keybind::new(
[mod_key, Key::Shift, Key::Char('r')].into_iter(), [mod_key, Key::Shift, Key::Char('r')].into_iter(),
@ -944,6 +986,8 @@ impl Default for Config {
Setting::FrameBorderActiveColor(active_color.clone()), Setting::FrameBorderActiveColor(active_color.clone()),
Setting::FrameBgActiveColor(active_color.clone()), Setting::FrameBgActiveColor(active_color.clone()),
], ],
font_pango: String::from("Monospace 9"),
font_pango_bold: String::from("Monospace Bold 9"),
} }
} }
} }

View File

@ -1,426 +0,0 @@
use std::str::FromStr;
use serde::{de::Unexpected, Deserialize, Serialize};
use strum::IntoEnumIterator;
use thiserror::Error;
use crate::hlwm::StringParseError;
use super::hex::{HexError, ParseHex};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Color {
RGB { r: u8, g: u8, b: u8 },
X11(X11Color),
}
impl Serialize for Color {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for Color {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct ExpectedColor;
impl serde::de::Expected for ExpectedColor {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("an X11 color string or a hex color string")
}
}
let str_val: String = Deserialize::deserialize(deserializer)?;
Ok(Color::from_str(&str_val).map_err(|_| {
serde::de::Error::invalid_value(Unexpected::Str(&str_val), &ExpectedColor)
})?)
}
}
impl Default for Color {
fn default() -> Self {
Self::RGB {
r: Default::default(),
g: Default::default(),
b: Default::default(),
}
}
}
#[derive(Debug, Clone, Error)]
pub enum ParseError {
#[error("length must be either 6 characters or 7 with '#' prefix")]
InvalidLength,
#[error("invalid hex value: [{0}]")]
HexError(#[from] HexError),
}
impl FromStr for Color {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(x11) = X11Color::from_str(s) {
return Ok(Self::X11(x11));
}
Self::from_hex(s)
}
}
impl Color {
pub const BLACK: Color = Color::rgb(0, 0, 0);
pub fn from_hex(hex: &str) -> Result<Self, ParseError> {
let expected_len = if hex.starts_with('#') { 7 } else { 6 };
if hex.len() != expected_len {
return Err(ParseError::InvalidLength);
}
let hex = hex.strip_prefix('#').unwrap_or(hex);
Ok(Self::RGB {
r: (&hex[0..2]).parse_hex()?,
g: (&hex[2..4]).parse_hex()?,
b: (&hex[4..6]).parse_hex()?,
})
}
pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
Self::RGB { r, g, b }
}
}
impl std::fmt::Display for Color {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Color::RGB { r, g, b } => write!(f, "#{r:02X}{g:02X}{b:02X}"),
Color::X11(color) => f.write_str(&color.to_string()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, strum::Display, strum::EnumIter, PartialEq, Eq)]
pub enum X11Color {
GhostWhite,
WhiteSmoke,
FloralWhite,
OldLace,
AntiqueWhite,
PapayaWhip,
BlanchedAlmond,
PeachPuff,
NavajoWhite,
LemonChiffon,
MintCream,
AliceBlue,
LavenderBlush,
MistyRose,
DarkSlateGray,
DarkSlateGrey,
DimGray,
DimGrey,
SlateGray,
SlateGrey,
LightSlateGray,
LightSlateGrey,
X11Gray,
X11Grey,
WebGray,
WebGrey,
LightGrey,
LightGray,
MidnightBlue,
NavyBlue,
CornflowerBlue,
DarkSlateBlue,
SlateBlue,
MediumSlateBlue,
LightSlateBlue,
MediumBlue,
RoyalBlue,
DodgerBlue,
DeepSkyBlue,
SkyBlue,
LightSkyBlue,
SteelBlue,
LightSteelBlue,
LightBlue,
PowderBlue,
PaleTurquoise,
DarkTurquoise,
MediumTurquoise,
LightCyan,
CadetBlue,
MediumAquamarine,
DarkGreen,
DarkOliveGreen,
DarkSeaGreen,
SeaGreen,
MediumSeaGreen,
LightSeaGreen,
PaleGreen,
SpringGreen,
LawnGreen,
X11Green,
WebGreen,
MediumSpringGreen,
GreenYellow,
LimeGreen,
YellowGreen,
ForestGreen,
OliveDrab,
DarkKhaki,
PaleGoldenrod,
LightGoldenrodYellow,
LightYellow,
LightGoldenrod,
DarkGoldenrod,
RosyBrown,
IndianRed,
SaddleBrown,
SandyBrown,
DarkSalmon,
LightSalmon,
DarkOrange,
LightCoral,
OrangeRed,
HotPink,
DeepPink,
LightPink,
PaleVioletRed,
X11Maroon,
WebMaroon,
MediumVioletRed,
VioletRed,
MediumOrchid,
DarkOrchid,
DarkViolet,
BlueViolet,
X11Purple,
WebPurple,
MediumPurple,
AntiqueWhite1,
AntiqueWhite2,
AntiqueWhite3,
AntiqueWhite4,
PeachPuff1,
PeachPuff2,
PeachPuff3,
PeachPuff4,
NavajoWhite1,
NavajoWhite2,
NavajoWhite3,
NavajoWhite4,
LemonChiffon1,
LemonChiffon2,
LemonChiffon3,
LemonChiffon4,
LavenderBlush1,
LavenderBlush2,
LavenderBlush3,
LavenderBlush4,
MistyRose1,
MistyRose2,
MistyRose3,
MistyRose4,
SlateBlue1,
SlateBlue2,
SlateBlue3,
SlateBlue4,
RoyalBlue1,
RoyalBlue2,
RoyalBlue3,
RoyalBlue4,
DodgerBlue1,
DodgerBlue2,
DodgerBlue3,
DodgerBlue4,
SteelBlue1,
SteelBlue2,
SteelBlue3,
SteelBlue4,
DeepSkyBlue1,
DeepSkyBlue2,
DeepSkyBlue3,
DeepSkyBlue4,
SkyBlue1,
SkyBlue2,
SkyBlue3,
SkyBlue4,
LightSkyBlue1,
LightSkyBlue2,
LightSkyBlue3,
LightSkyBlue4,
SlateGray1,
SlateGray2,
SlateGray3,
SlateGray4,
LightSteelBlue1,
LightSteelBlue2,
LightSteelBlue3,
LightSteelBlue4,
LightBlue1,
LightBlue2,
LightBlue3,
LightBlue4,
LightCyan1,
LightCyan2,
LightCyan3,
LightCyan4,
PaleTurquoise1,
PaleTurquoise2,
PaleTurquoise3,
PaleTurquoise4,
CadetBlue1,
CadetBlue2,
CadetBlue3,
CadetBlue4,
DarkSlateGray1,
DarkSlateGray2,
DarkSlateGray3,
DarkSlateGray4,
DarkSeaGreen1,
DarkSeaGreen2,
DarkSeaGreen3,
DarkSeaGreen4,
SeaGreen1,
SeaGreen2,
SeaGreen3,
SeaGreen4,
PaleGreen1,
PaleGreen2,
PaleGreen3,
PaleGreen4,
SpringGreen1,
SpringGreen2,
SpringGreen3,
SpringGreen4,
OliveDrab1,
OliveDrab2,
OliveDrab3,
OliveDrab4,
DarkOliveGreen1,
DarkOliveGreen2,
DarkOliveGreen3,
DarkOliveGreen4,
LightGoldenrod1,
LightGoldenrod2,
LightGoldenrod3,
LightGoldenrod4,
LightYellow1,
LightYellow2,
LightYellow3,
LightYellow4,
DarkGoldenrod1,
DarkGoldenrod2,
DarkGoldenrod3,
DarkGoldenrod4,
RosyBrown1,
RosyBrown2,
RosyBrown3,
RosyBrown4,
IndianRed1,
IndianRed2,
IndianRed3,
IndianRed4,
LightSalmon1,
LightSalmon2,
LightSalmon3,
LightSalmon4,
DarkOrange1,
DarkOrange2,
DarkOrange3,
DarkOrange4,
OrangeRed1,
OrangeRed2,
OrangeRed3,
OrangeRed4,
DeepPink1,
DeepPink2,
DeepPink3,
DeepPink4,
HotPink1,
HotPink2,
HotPink3,
HotPink4,
LightPink1,
LightPink2,
LightPink3,
LightPink4,
PaleVioletRed1,
PaleVioletRed2,
PaleVioletRed3,
PaleVioletRed4,
VioletRed1,
VioletRed2,
VioletRed3,
VioletRed4,
MediumOrchid1,
MediumOrchid2,
MediumOrchid3,
MediumOrchid4,
DarkOrchid1,
DarkOrchid2,
DarkOrchid3,
DarkOrchid4,
MediumPurple1,
MediumPurple2,
MediumPurple3,
MediumPurple4,
DarkGrey,
DarkGray,
DarkBlue,
DarkCyan,
DarkMagenta,
DarkRed,
LightGreen,
RebeccaPurple,
}
impl FromStr for X11Color {
type Err = StringParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::iter()
.into_iter()
.find(|i| i.to_string() == s)
.ok_or(StringParseError::UnknownValue)
}
}
#[cfg(test)]
mod test {
use serde::{Deserialize, Serialize};
use super::{Color, X11Color};
#[derive(Debug, Serialize, Deserialize)]
pub struct ColorContainer {
color: Color,
}
fn color_serialize_deserialize(color: Color) {
let original_color = color.clone();
let color_str =
toml::to_string_pretty(&ColorContainer { color }).expect("serialization failure");
let color_parsed: ColorContainer =
toml::from_str(color_str.as_str()).expect("deserialization failure");
assert_eq!(original_color, color_parsed.color);
}
#[test]
fn color_serialize_deserialize_rgb() {
color_serialize_deserialize(Color::RGB {
r: 128,
g: 128,
b: 128,
});
}
#[test]
fn color_serialize_deserialize_x11() {
color_serialize_deserialize(Color::X11(X11Color::HotPink2));
}
}

151
src/hlwm/color/mod.rs Normal file
View File

@ -0,0 +1,151 @@
use std::str::FromStr;
use serde::{de::Unexpected, Deserialize, Serialize};
use thiserror::Error;
use super::hex::{HexError, ParseHex};
mod x11;
pub use x11::X11Color;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Color {
RGB { r: u8, g: u8, b: u8 },
X11(X11Color),
}
impl Serialize for Color {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for Color {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct ExpectedColor;
impl serde::de::Expected for ExpectedColor {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("an X11 color string or a hex color string")
}
}
let str_val: String = Deserialize::deserialize(deserializer)?;
Ok(Color::from_str(&str_val).map_err(|_| {
serde::de::Error::invalid_value(Unexpected::Str(&str_val), &ExpectedColor)
})?)
}
}
impl Default for Color {
fn default() -> Self {
Self::RGB {
r: Default::default(),
g: Default::default(),
b: Default::default(),
}
}
}
#[derive(Debug, Clone, Error)]
pub enum ParseError {
#[error("length must be either 6 characters or 7 with '#' prefix")]
InvalidLength,
#[error("invalid hex value: [{0}]")]
HexError(#[from] HexError),
}
impl FromStr for Color {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(x11) = X11Color::from_str(s) {
return Ok(Self::X11(x11));
}
Self::from_hex(s)
}
}
impl Color {
pub const BLACK: Color = Color::from_rgb(0, 0, 0);
pub fn from_hex(hex: &str) -> Result<Self, ParseError> {
let expected_len = if hex.starts_with('#') { 7 } else { 6 };
if hex.len() != expected_len {
return Err(ParseError::InvalidLength);
}
let hex = hex.strip_prefix('#').unwrap_or(hex);
Ok(Self::RGB {
r: (&hex[0..2]).parse_hex()?,
g: (&hex[2..4]).parse_hex()?,
b: (&hex[4..6]).parse_hex()?,
})
}
pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self {
Self::RGB { r, g, b }
}
pub const fn to_rgb(&self) -> (u8, u8, u8) {
match self {
Color::RGB { r, g, b } => (*r, *g, *b),
Color::X11(xc) => xc.to_rgb(),
}
}
}
impl std::fmt::Display for Color {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Color::RGB { r, g, b } => write!(f, "#{r:02X}{g:02X}{b:02X}"),
Color::X11(color) => f.write_str(&color.to_string()),
}
}
}
impl From<Color> for cnx::text::Color {
fn from(value: Color) -> Self {
let (r, g, b) = value.to_rgb();
cnx::text::Color::from_rgb(r, g, b)
}
}
#[cfg(test)]
mod test {
use serde::{Deserialize, Serialize};
use super::{Color, X11Color};
#[derive(Debug, Serialize, Deserialize)]
pub struct ColorContainer {
color: Color,
}
fn color_serialize_deserialize(color: Color) {
let original_color = color.clone();
let color_str =
toml::to_string_pretty(&ColorContainer { color }).expect("serialization failure");
let color_parsed: ColorContainer =
toml::from_str(color_str.as_str()).expect("deserialization failure");
assert_eq!(original_color, color_parsed.color);
}
#[test]
fn color_serialize_deserialize_rgb() {
color_serialize_deserialize(Color::RGB {
r: 128,
g: 128,
b: 128,
});
}
#[test]
fn color_serialize_deserialize_x11() {
color_serialize_deserialize(Color::X11(X11Color::HotPink2));
}
}

728
src/hlwm/color/x11.rs Normal file
View File

@ -0,0 +1,728 @@
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use crate::hlwm::StringParseError;
macro_rules! color_impl {
() => {};
($($name:tt($($str:literal),+) => $r:literal, $g:literal, $b:literal,)+) => {
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum X11Color {
$($name,)+
}
impl X11Color {
const ALL: [X11Color; 658] = [$(X11Color::$name,)+];
pub const fn to_rgb(&self) -> (u8, u8, u8) {
match self {
$(X11Color::$name => ($r, $g, $b),)+
}
}
pub fn iter() -> std::array::IntoIter<X11Color, 658> {
X11Color::ALL.into_iter()
}
fn color_names(&self) -> Vec<&'static str> {
match self {
$(
X11Color::$name => vec![$($str),+],
)+
}
}
}
impl std::fmt::Display for X11Color {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
$(
X11Color::$name => f.write_str(color_impl!(First $($str,)+)),
)+
}
}
}
};
(First $lit:literal, $($lit2:literal,)*) => {
$lit
};
}
impl FromStr for X11Color {
type Err = StringParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
X11Color::ALL
.into_iter()
.find(|c| c.color_names().into_iter().any(|n| n == s))
.ok_or(StringParseError::UnknownValue)
}
}
impl Default for X11Color {
fn default() -> Self {
X11Color::Black
}
}
color_impl! {
White("white") => 255,255,255,
Red3("red3") => 205,0,0,
VioletRed1("VioletRed1") => 255,62,150,
Gray54("gray54") => 138,138,138,
Gold1("gold1") => 255,215,0,
Pink1("pink1") => 255,181,197,
Grey76("grey76") => 194,194,194,
Goldenrod("goldenrod") => 218,165,32,
Wheat4("wheat4") => 139,126,102,
Gray71("gray71") => 181,181,181,
DarkTurquoise("dark turquoise", "DarkTurquoise") => 0,206,209,
Plum4("plum4") => 139,102,139,
Grey44("grey44") => 112,112,112,
Grey15("grey15") => 38,38,38,
Plum("plum") => 221,160,221,
Gray55("gray55") => 140,140,140,
Green2("green2") => 0,238,0,
Grey65("grey65") => 166,166,166,
MediumPurple1("MediumPurple1") => 171,130,255,
DarkOliveGreen2("DarkOliveGreen2") => 188,238,104,
Grey74("grey74") => 189,189,189,
LightGoldenrod1("LightGoldenrod1") => 255,236,139,
MistyRose2("MistyRose2") => 238,213,210,
LightSalmon4("LightSalmon4") => 139,87,66,
Ivory3("ivory3") => 205,205,193,
DarkGray("dark gray", "DarkGray") => 169,169,169,
SlateGray1("SlateGray1") => 198,226,255,
BlanchedAlmond("blanched almond", "BlanchedAlmond") => 255,235,205,
Gray8("gray8") => 20,20,20,
Orange("orange") => 255,165,0,
Cornsilk2("cornsilk2") => 238,232,205,
Thistle2("thistle2") => 238,210,238,
Gray91("gray91") => 232,232,232,
MediumPurple4("MediumPurple4") => 93,71,139,
Orchid4("orchid4") => 139,71,137,
PaleTurquoise4("PaleTurquoise4") => 102,139,139,
AntiqueWhite("antique white", "AntiqueWhite") => 250,235,215,
Grey92("grey92") => 235,235,235,
Lavender("lavender") => 230,230,250,
Sienna("sienna") => 160,82,45,
Tan("tan") => 210,180,140,
LightYellow("light yellow", "LightYellow") => 255,255,224,
Brown3("brown3") => 205,51,51,
AliceBlue("alice blue", "AliceBlue") => 240,248,255,
Grey96("grey96") => 245,245,245,
GhostWhite("ghost white", "GhostWhite") => 248,248,255,
PaleGoldenrod("pale goldenrod", "PaleGoldenrod") => 238,232,170,
Gray96("gray96") => 245,245,245,
Gray90("gray90") => 229,229,229,
Gray85("gray85") => 217,217,217,
Grey34("grey34") => 87,87,87,
Khaki2("khaki2") => 238,230,133,
Grey54("grey54") => 138,138,138,
DarkGrey("dark grey", "DarkGrey") => 169,169,169,
Gray42("gray42") => 107,107,107,
Grey18("grey18") => 46,46,46,
Gray17("gray17") => 43,43,43,
OrangeRed3("OrangeRed3") => 205,55,0,
Grey29("grey29") => 74,74,74,
LightPink("light pink", "LightPink") => 255,182,193,
Salmon4("salmon4") => 139,76,57,
CadetBlue3("CadetBlue3") => 122,197,205,
Grey36("grey36") => 92,92,92,
Tomato2("tomato2") => 238,92,66,
DeepPink("deep pink", "DeepPink") => 255,20,147,
Cyan4("cyan4") => 0,139,139,
LightCyan2("LightCyan2") => 209,238,238,
Chartreuse1("chartreuse1") => 127,255,0,
Gray62("gray62") => 158,158,158,
Bisque4("bisque4") => 139,125,107,
PaleTurquoise3("PaleTurquoise3") => 150,205,205,
SpringGreen1("SpringGreen1") => 0,255,127,
MediumTurquoise("medium turquoise", "MediumTurquoise") => 72,209,204,
Grey39("grey39") => 99,99,99,
SeaGreen2("SeaGreen2") => 78,238,148,
Grey17("grey17") => 43,43,43,
Grey6("grey6") => 15,15,15,
Pink("pink") => 255,192,203,
Cyan2("cyan2") => 0,238,238,
LightCyan3("LightCyan3") => 180,205,205,
Brown4("brown4") => 139,35,35,
Chartreuse3("chartreuse3") => 102,205,0,
GreenYellow("green yellow", "GreenYellow") => 173,255,47,
DarkSeaGreen3("DarkSeaGreen3") => 155,205,155,
Gray49("gray49") => 125,125,125,
VioletRed("violet red", "VioletRed") => 208,32,144,
PaleVioletRed1("PaleVioletRed1") => 255,130,171,
RosyBrown4("RosyBrown4") => 139,105,105,
Grey79("grey79") => 201,201,201,
Honeydew("honeydew") => 240,255,240,
Gray26("gray26") => 66,66,66,
Grey40("grey40") => 102,102,102,
MediumAquamarine("medium aquamarine", "MediumAquamarine") => 102,205,170,
Gray89("gray89") => 227,227,227,
Pink4("pink4") => 139,99,108,
Grey95("grey95") => 242,242,242,
Turquoise("turquoise") => 64,224,208,
Azure3("azure3") => 193,205,205,
NavajoWhite("navajo white", "NavajoWhite") => 255,222,173,
AntiqueWhite4("AntiqueWhite4") => 139,131,120,
PeachPuff("peach puff", "PeachPuff") => 255,218,185,
Turquoise4("turquoise4") => 0,134,139,
Gray68("gray68") => 173,173,173,
Burlywood4("burlywood4") => 139,115,85,
SlateBlue4("SlateBlue4") => 71,60,139,
DodgerBlue2("DodgerBlue2") => 28,134,238,
LightSlateGray("light slate gray", "LightSlateGray") => 119,136,153,
Red1("red1") => 255,0,0,
Yellow2("yellow2") => 238,238,0,
Gray32("gray32") => 82,82,82,
MistyRose("misty rose", "MistyRose") => 255,228,225,
Cornsilk3("cornsilk3") => 205,200,177,
RosyBrown3("RosyBrown3") => 205,155,155,
Pink2("pink2") => 238,169,184,
Gray6("gray6") => 15,15,15,
Khaki3("khaki3") => 205,198,115,
Turquoise2("turquoise2") => 0,229,238,
PowderBlue("powder blue", "PowderBlue") => 176,224,230,
Gray84("gray84") => 214,214,214,
Grey59("grey59") => 150,150,150,
Yellow3("yellow3") => 205,205,0,
SpringGreen2("SpringGreen2") => 0,238,118,
NavajoWhite1("NavajoWhite1") => 255,222,173,
Gray38("gray38") => 97,97,97,
AntiqueWhite2("AntiqueWhite2") => 238,223,204,
Azure4("azure4") => 131,139,139,
Green4("green4") => 0,139,0,
Gray72("gray72") => 184,184,184,
Gray31("gray31") => 79,79,79,
SpringGreen4("SpringGreen4") => 0,139,69,
DarkOrange1("DarkOrange1") => 255,127,0,
LightGoldenrod4("LightGoldenrod4") => 139,129,76,
DarkSeaGreen("dark sea green", "DarkSeaGreen") => 143,188,143,
LightBlue("light blue", "LightBlue") => 173,216,230,
DarkOliveGreen1("DarkOliveGreen1") => 202,255,112,
Gray24("gray24") => 61,61,61,
SeaGreen3("SeaGreen3") => 67,205,128,
Grey69("grey69") => 176,176,176,
Grey98("grey98") => 250,250,250,
DarkCyan("dark cyan", "DarkCyan") => 0,139,139,
Tomato4("tomato4") => 139,54,38,
OliveDrab4("OliveDrab4") => 105,139,34,
SlateBlue("slate blue", "SlateBlue") => 106,90,205,
DarkSeaGreen4("DarkSeaGreen4") => 105,139,105,
Gray0("gray0") => 0,0,0,
Gray9("gray9") => 23,23,23,
Gray60("gray60") => 153,153,153,
Gray45("gray45") => 115,115,115,
DarkBlue("dark blue", "DarkBlue") => 0,0,139,
Gray41("gray41") => 105,105,105,
LightBlue3("LightBlue3") => 154,192,205,
Blue2("blue2") => 0,0,238,
Gray48("gray48") => 122,122,122,
LightGoldenrodYellow("light goldenrod yellow", "LightGoldenrodYellow") => 250,250,210,
Seashell4("seashell4") => 139,134,130,
Chocolate3("chocolate3") => 205,102,29,
DarkOrange("dark orange", "DarkOrange") => 255,140,0,
DarkGoldenrod1("DarkGoldenrod1") => 255,185,15,
Aquamarine1("aquamarine1") => 127,255,212,
Gray78("gray78") => 199,199,199,
PaleVioletRed3("PaleVioletRed3") => 205,104,137,
Thistle("thistle") => 216,191,216,
LightSalmon1("LightSalmon1") => 255,160,122,
Blue3("blue3") => 0,0,205,
Gray1("gray1") => 3,3,3,
Grey5("grey5") => 13,13,13,
DeepSkyBlue1("DeepSkyBlue1") => 0,191,255,
RoyalBlue4("RoyalBlue4") => 39,64,139,
DeepPink4("DeepPink4") => 139,10,80,
MediumPurple3("MediumPurple3") => 137,104,205,
Yellow1("yellow1") => 255,255,0,
Grey8("grey8") => 20,20,20,
Blue1("blue1") => 0,0,255,
IndianRed2("IndianRed2") => 238,99,99,
SeaGreen4("SeaGreen4") => 46,139,87,
MediumSeaGreen("medium sea green", "MediumSeaGreen") => 60,179,113,
Orchid("orchid") => 218,112,214,
RosyBrown2("RosyBrown2") => 238,180,180,
Seashell("seashell") => 255,245,238,
MediumPurple2("MediumPurple2") => 159,121,238,
Tan3("tan3") => 205,133,63,
Pink3("pink3") => 205,145,158,
Maroon1("maroon1") => 255,52,179,
Plum1("plum1") => 255,187,255,
DebianRed("DebianRed") => 215,7,81,
SaddleBrown("saddle brown", "SaddleBrown") => 139,69,19,
Goldenrod4("goldenrod4") => 139,105,20,
Khaki4("khaki4") => 139,134,78,
Grey53("grey53") => 135,135,135,
Gray58("gray58") => 148,148,148,
Azure2("azure2") => 224,238,238,
SlateGray4("SlateGray4") => 108,123,139,
DodgerBlue4("DodgerBlue4") => 16,78,139,
IndianRed3("IndianRed3") => 205,85,85,
DarkKhaki("dark khaki", "DarkKhaki") => 189,183,107,
Grey73("grey73") => 186,186,186,
DodgerBlue3("DodgerBlue3") => 24,116,205,
Tan1("tan1") => 255,165,79,
OliveDrab2("OliveDrab2") => 179,238,58,
Seashell3("seashell3") => 205,197,191,
Gray30("gray30") => 77,77,77,
Gray65("gray65") => 166,166,166,
Orange4("orange4") => 139,90,0,
Gray43("gray43") => 110,110,110,
Grey12("grey12") => 31,31,31,
Snow3("snow3") => 205,201,201,
ForestGreen("forest green", "ForestGreen") => 34,139,34,
DarkSlateBlue("dark slate blue", "DarkSlateBlue") => 72,61,139,
Thistle1("thistle1") => 255,225,255,
Grey45("grey45") => 115,115,115,
DarkOrchid("dark orchid", "DarkOrchid") => 153,50,204,
MediumOrchid1("MediumOrchid1") => 224,102,255,
SkyBlue2("SkyBlue2") => 126,192,238,
DarkViolet("dark violet", "DarkViolet") => 148,0,211,
LightSteelBlue1("LightSteelBlue1") => 202,225,255,
Blue4("blue4") => 0,0,139,
LawnGreen("lawn green", "LawnGreen") => 124,252,0,
LemonChiffon4("LemonChiffon4") => 139,137,112,
SkyBlue1("SkyBlue1") => 135,206,255,
Turquoise3("turquoise3") => 0,197,205,
Gray64("gray64") => 163,163,163,
Grey33("grey33") => 84,84,84,
Orchid3("orchid3") => 205,105,201,
Grey77("grey77") => 196,196,196,
Grey81("grey81") => 207,207,207,
DarkOrchid4("DarkOrchid4") => 104,34,139,
Gray36("gray36") => 92,92,92,
Gray5("gray5") => 13,13,13,
Grey46("grey46") => 117,117,117,
Khaki("khaki") => 240,230,140,
Wheat2("wheat2") => 238,216,174,
Cornsilk1("cornsilk1") => 255,248,220,
Red2("red2") => 238,0,0,
Gray20("gray20") => 51,51,51,
OrangeRed("orange red", "OrangeRed") => 255,69,0,
CadetBlue("cadet blue", "CadetBlue") => 95,158,160,
Grey9("grey9") => 23,23,23,
Gray37("gray37") => 94,94,94,
SteelBlue3("SteelBlue3") => 79,148,205,
Firebrick2("firebrick2") => 238,44,44,
DarkSlateGrey("dark slate grey", "DarkSlateGrey") => 47,79,79,
LemonChiffon3("LemonChiffon3") => 205,201,165,
Violet("violet") => 238,130,238,
Orchid1("orchid1") => 255,131,250,
Yellow("yellow") => 255,255,0,
LightBlue2("LightBlue2") => 178,223,238,
Gray46("gray46") => 117,117,117,
MediumSpringGreen("medium spring green", "MediumSpringGreen") => 0,250,154,
LavenderBlush3("LavenderBlush3") => 205,193,197,
VioletRed2("VioletRed2") => 238,58,140,
Snow1("snow1") => 255,250,250,
Azure("azure") => 240,255,255,
MistyRose4("MistyRose4") => 139,125,123,
Grey22("grey22") => 56,56,56,
Sienna3("sienna3") => 205,104,57,
Salmon3("salmon3") => 205,112,84,
Burlywood1("burlywood1") => 255,211,155,
LightCyan("light cyan", "LightCyan") => 224,255,255,
Grey55("grey55") => 140,140,140,
Grey31("grey31") => 79,79,79,
AntiqueWhite3("AntiqueWhite3") => 205,192,176,
MediumOrchid3("MediumOrchid3") => 180,82,205,
Grey75("grey75") => 191,191,191,
DarkSalmon("dark salmon", "DarkSalmon") => 233,150,122,
HotPink2("HotPink2") => 238,106,167,
Gray69("gray69") => 176,176,176,
Gray52("gray52") => 133,133,133,
Gray21("gray21") => 54,54,54,
SlateGray2("SlateGray2") => 185,211,238,
Grey100("grey100") => 255,255,255,
VioletRed3("VioletRed3") => 205,50,120,
DimGray("dim gray", "DimGray") => 105,105,105,
RoyalBlue("royal blue", "RoyalBlue") => 65,105,225,
Yellow4("yellow4") => 139,139,0,
Gray39("gray39") => 99,99,99,
YellowGreen("yellow green", "YellowGreen") => 154,205,50,
Sienna1("sienna1") => 255,130,71,
Grey83("grey83") => 212,212,212,
Goldenrod1("goldenrod1") => 255,193,37,
MidnightBlue("midnight blue", "MidnightBlue") => 25,25,112,
Firebrick4("firebrick4") => 139,26,26,
MediumOrchid4("MediumOrchid4") => 122,55,139,
Gray82("gray82") => 209,209,209,
Brown("brown") => 165,42,42,
Grey24("grey24") => 61,61,61,
DarkOrchid3("DarkOrchid3") => 154,50,205,
Gainsboro("gainsboro") => 220,220,220,
MediumSlateBlue("medium slate blue", "MediumSlateBlue") => 123,104,238,
LightSkyBlue1("LightSkyBlue1") => 176,226,255,
Seashell2("seashell2") => 238,229,222,
PaleVioletRed("pale violet red", "PaleVioletRed") => 219,112,147,
RoyalBlue2("RoyalBlue2") => 67,110,238,
Gray4("gray4") => 10,10,10,
LimeGreen("lime green", "LimeGreen") => 50,205,50,
Maroon2("maroon2") => 238,48,167,
Grey2("grey2") => 5,5,5,
Grey11("grey11") => 28,28,28,
SteelBlue("steel blue", "SteelBlue") => 70,130,180,
Sienna2("sienna2") => 238,121,66,
NavajoWhite4("NavajoWhite4") => 139,121,94,
Grey25("grey25") => 64,64,64,
DeepSkyBlue("deep sky blue", "DeepSkyBlue") => 0,191,255,
Gray10("gray10") => 26,26,26,
Salmon2("salmon2") => 238,130,98,
LightGoldenrod2("LightGoldenrod2") => 238,220,130,
HotPink1("HotPink1") => 255,110,180,
Grey49("grey49") => 125,125,125,
Bisque2("bisque2") => 238,213,183,
Gray87("gray87") => 222,222,222,
Linen("linen") => 250,240,230,
Ivory1("ivory1") => 255,255,240,
Gray61("gray61") => 156,156,156,
LightSalmon2("LightSalmon2") => 238,149,114,
Grey61("grey61") => 156,156,156,
DarkGoldenrod2("DarkGoldenrod2") => 238,173,14,
Cyan3("cyan3") => 0,205,205,
Firebrick3("firebrick3") => 205,38,38,
Grey48("grey48") => 122,122,122,
LightSalmon("light salmon", "LightSalmon") => 255,160,122,
Cornsilk4("cornsilk4") => 139,136,120,
HotPink4("HotPink4") => 139,58,98,
DarkOrange2("DarkOrange2") => 238,118,0,
Goldenrod2("goldenrod2") => 238,180,34,
Gray53("gray53") => 135,135,135,
Grey85("grey85") => 217,217,217,
LightCyan1("LightCyan1") => 224,255,255,
PeachPuff3("PeachPuff3") => 205,175,149,
Gray95("gray95") => 242,242,242,
Grey80("grey80") => 204,204,204,
RoyalBlue3("RoyalBlue3") => 58,95,205,
Orange1("orange1") => 255,165,0,
MediumOrchid2("MediumOrchid2") => 209,95,238,
Grey86("grey86") => 219,219,219,
Gray80("gray80") => 204,204,204,
Grey3("grey3") => 8,8,8,
Gray34("gray34") => 87,87,87,
SpringGreen("spring green", "SpringGreen") => 0,255,127,
Aquamarine2("aquamarine2") => 118,238,198,
DarkOrange3("DarkOrange3") => 205,102,0,
Brown2("brown2") => 238,59,59,
Magenta1("magenta1") => 255,0,255,
Grey37("grey37") => 94,94,94,
Goldenrod3("goldenrod3") => 205,155,29,
LavenderBlush("lavender blush", "LavenderBlush") => 255,240,245,
Snow("snow") => 255,250,250,
PaleGreen3("PaleGreen3") => 124,205,124,
Gray99("gray99") => 252,252,252,
Gray76("gray76") => 194,194,194,
Tan2("tan2") => 238,154,73,
LightCoral("light coral", "LightCoral") => 240,128,128,
Grey10("grey10") => 26,26,26,
Ivory4("ivory4") => 139,139,131,
MediumVioletRed("medium violet red", "MediumVioletRed") => 199,21,133,
Gray66("gray66") => 168,168,168,
Magenta2("magenta2") => 238,0,238,
Purple("purple") => 160,32,240,
Gray81("gray81") => 207,207,207,
Plum2("plum2") => 238,174,238,
Peru("peru") => 205,133,63,
PaleGreen2("PaleGreen2") => 144,238,144,
Grey93("grey93") => 237,237,237,
IndianRed4("IndianRed4") => 139,58,58,
Grey58("grey58") => 148,148,148,
Tomato1("tomato1") => 255,99,71,
Aquamarine("aquamarine") => 127,255,212,
LightYellow1("LightYellow1") => 255,255,224,
Aquamarine4("aquamarine4") => 69,139,116,
IndianRed("indian red", "IndianRed") => 205,92,92,
Grey60("grey60") => 153,153,153,
Black("black") => 0,0,0,
LightSteelBlue2("LightSteelBlue2") => 188,210,238,
Gray92("gray92") => 235,235,235,
Grey0("grey0") => 0,0,0,
LightSlateBlue("light slate blue", "LightSlateBlue") => 132,112,255,
AntiqueWhite1("AntiqueWhite1") => 255,239,219,
DarkOliveGreen("dark olive green", "DarkOliveGreen") => 85,107,47,
Gray70("gray70") => 179,179,179,
Gray44("gray44") => 112,112,112,
LightGray("light gray", "LightGray") => 211,211,211,
Gray23("gray23") => 59,59,59,
Orange2("orange2") => 238,154,0,
Gold("gold") => 255,215,0,
Grey7("grey7") => 18,18,18,
DeepSkyBlue3("DeepSkyBlue3") => 0,154,205,
Wheat3("wheat3") => 205,186,150,
Green3("green3") => 0,205,0,
Chocolate("chocolate") => 210,105,30,
OrangeRed1("OrangeRed1") => 255,69,0,
DarkOrchid2("DarkOrchid2") => 178,58,238,
Gray("gray") => 190,190,190,
LightPink3("LightPink3") => 205,140,149,
BlueViolet("blue violet", "BlueViolet") => 138,43,226,
PaleTurquoise("pale turquoise", "PaleTurquoise") => 175,238,238,
LavenderBlush2("LavenderBlush2") => 238,224,229,
HotPink3("HotPink3") => 205,96,144,
Gray27("gray27") => 69,69,69,
DarkOliveGreen4("DarkOliveGreen4") => 110,139,61,
Chocolate2("chocolate2") => 238,118,33,
WhiteSmoke("white smoke", "WhiteSmoke") => 245,245,245,
PaleTurquoise1("PaleTurquoise1") => 187,255,255,
LightSkyBlue("light sky blue", "LightSkyBlue") => 135,206,250,
SteelBlue1("SteelBlue1") => 99,184,255,
Grey97("grey97") => 247,247,247,
Bisque("bisque") => 255,228,196,
Bisque3("bisque3") => 205,183,158,
Blue("blue") => 0,0,255,
LightSteelBlue("light steel blue", "LightSteelBlue") => 176,196,222,
Grey51("grey51") => 130,130,130,
MintCream("mint cream", "MintCream") => 245,255,250,
MediumOrchid("medium orchid", "MediumOrchid") => 186,85,211,
PaleVioletRed4("PaleVioletRed4") => 139,71,93,
Gold2("gold2") => 238,201,0,
Gray73("gray73") => 186,186,186,
Grey99("grey99") => 252,252,252,
Grey14("grey14") => 36,36,36,
Cyan("cyan") => 0,255,255,
Coral2("coral2") => 238,106,80,
Gray93("gray93") => 237,237,237,
LemonChiffon1("LemonChiffon1") => 255,250,205,
NavajoWhite3("NavajoWhite3") => 205,179,139,
Burlywood3("burlywood3") => 205,170,125,
Bisque1("bisque1") => 255,228,196,
Gray35("gray35") => 89,89,89,
LightYellow4("LightYellow4") => 139,139,122,
Snow4("snow4") => 139,137,137,
Gray56("gray56") => 143,143,143,
Gray59("gray59") => 150,150,150,
Gray15("gray15") => 38,38,38,
LightSteelBlue4("LightSteelBlue4") => 110,123,139,
LightSteelBlue3("LightSteelBlue3") => 162,181,205,
OliveDrab("olive drab", "OliveDrab") => 107,142,35,
RosyBrown1("RosyBrown1") => 255,193,193,
Grey84("grey84") => 214,214,214,
Green1("green1") => 0,255,0,
Gray63("gray63") => 161,161,161,
LightPink4("LightPink4") => 139,95,101,
Wheat("wheat") => 245,222,179,
Gray14("gray14") => 36,36,36,
Gray16("gray16") => 41,41,41,
MediumBlue("medium blue", "MediumBlue") => 0,0,205,
OrangeRed4("OrangeRed4") => 139,37,0,
LightPink1("LightPink1") => 255,174,185,
SlateBlue2("SlateBlue2") => 122,103,238,
SandyBrown("sandy brown", "SandyBrown") => 244,164,96,
Gray83("gray83") => 212,212,212,
SkyBlue3("SkyBlue3") => 108,166,205,
Moccasin("moccasin") => 255,228,181,
Grey27("grey27") => 69,69,69,
PaleTurquoise2("PaleTurquoise2") => 174,238,238,
Firebrick("firebrick") => 178,34,34,
Purple3("purple3") => 125,38,205,
SkyBlue4("SkyBlue4") => 74,112,139,
Gray67("gray67") => 171,171,171,
Aquamarine3("aquamarine3") => 102,205,170,
LightSkyBlue3("LightSkyBlue3") => 141,182,205,
Magenta("magenta") => 255,0,255,
Grey38("grey38") => 97,97,97,
SeaGreen("sea green", "SeaGreen") => 46,139,87,
Grey90("grey90") => 229,229,229,
Chartreuse("chartreuse") => 127,255,0,
DeepSkyBlue4("DeepSkyBlue4") => 0,104,139,
VioletRed4("VioletRed4") => 139,34,82,
DarkRed("dark red", "DarkRed") => 139,0,0,
Gray25("gray25") => 64,64,64,
Tan4("tan4") => 139,90,43,
Grey47("grey47") => 120,120,120,
SlateGrey("slate grey", "SlateGrey") => 112,128,144,
IndianRed1("IndianRed1") => 255,106,106,
LightGreen("light green", "LightGreen") => 144,238,144,
Honeydew3("honeydew3") => 193,205,193,
Orchid2("orchid2") => 238,122,233,
Honeydew2("honeydew2") => 224,238,224,
Snow2("snow2") => 238,233,233,
OliveDrab3("OliveDrab3") => 154,205,50,
Purple2("purple2") => 145,44,238,
Gray47("gray47") => 120,120,120,
Grey89("grey89") => 227,227,227,
Coral1("coral1") => 255,114,86,
Gray22("gray22") => 56,56,56,
Gray74("gray74") => 189,189,189,
Orange3("orange3") => 205,133,0,
Maroon3("maroon3") => 205,41,144,
Tomato3("tomato3") => 205,79,57,
RosyBrown("rosy brown", "RosyBrown") => 188,143,143,
Grey20("grey20") => 51,51,51,
Gray51("gray51") => 130,130,130,
DeepPink1("DeepPink1") => 255,20,147,
DarkGoldenrod4("DarkGoldenrod4") => 139,101,8,
Grey50("grey50") => 127,127,127,
Tomato("tomato") => 255,99,71,
Red("red") => 255,0,0,
SteelBlue4("SteelBlue4") => 54,100,139,
HotPink("hot pink", "HotPink") => 255,105,180,
SkyBlue("sky blue", "SkyBlue") => 135,206,235,
LightYellow3("LightYellow3") => 205,205,180,
Gray33("gray33") => 84,84,84,
Grey35("grey35") => 89,89,89,
Grey88("grey88") => 224,224,224,
CornflowerBlue("cornflower blue", "CornflowerBlue") => 100,149,237,
Grey13("grey13") => 33,33,33,
Grey19("grey19") => 48,48,48,
Chartreuse2("chartreuse2") => 118,238,0,
Grey72("grey72") => 184,184,184,
SlateGray("slate gray", "SlateGray") => 112,128,144,
CadetBlue2("CadetBlue2") => 142,229,238,
NavajoWhite2("NavajoWhite2") => 238,207,161,
Gray19("gray19") => 48,48,48,
DarkOrange4("DarkOrange4") => 139,69,0,
Turquoise1("turquoise1") => 0,245,255,
LightPink2("LightPink2") => 238,162,173,
LightBlue1("LightBlue1") => 191,239,255,
Grey71("grey71") => 181,181,181,
DimGrey("dim grey", "DimGrey") => 105,105,105,
LightGoldenrod3("LightGoldenrod3") => 205,190,112,
SlateGray3("SlateGray3") => 159,182,205,
Grey78("grey78") => 199,199,199,
Grey94("grey94") => 240,240,240,
Azure1("azure1") => 240,255,255,
Gray94("gray94") => 240,240,240,
Grey62("grey62") => 158,158,158,
Magenta4("magenta4") => 139,0,139,
Coral3("coral3") => 205,91,69,
Salmon("salmon") => 250,128,114,
Khaki1("khaki1") => 255,246,143,
Gray3("gray3") => 8,8,8,
Brown1("brown1") => 255,64,64,
Grey52("grey52") => 133,133,133,
Grey21("grey21") => 54,54,54,
LightSkyBlue4("LightSkyBlue4") => 96,123,139,
Gold3("gold3") => 205,173,0,
LavenderBlush4("LavenderBlush4") => 139,131,134,
Honeydew1("honeydew1") => 240,255,240,
DarkSlateGray("dark slate gray", "DarkSlateGray") => 47,79,79,
Maroon("maroon") => 176,48,96,
Wheat1("wheat1") => 255,231,186,
Gray28("gray28") => 71,71,71,
Gray40("gray40") => 102,102,102,
Grey91("grey91") => 232,232,232,
Gray57("gray57") => 145,145,145,
Grey68("grey68") => 173,173,173,
SpringGreen3("SpringGreen3") => 0,205,102,
PeachPuff1("PeachPuff1") => 255,218,185,
FloralWhite("floral white", "FloralWhite") => 255,250,240,
Coral4("coral4") => 139,62,47,
Gray50("gray50") => 127,127,127,
Grey82("grey82") => 209,209,209,
Gray86("gray86") => 219,219,219,
Sienna4("sienna4") => 139,71,38,
Grey57("grey57") => 145,145,145,
Grey70("grey70") => 179,179,179,
Grey63("grey63") => 161,161,161,
DarkOliveGreen3("DarkOliveGreen3") => 162,205,90,
Beige("beige") => 245,245,220,
OldLace("old lace", "OldLace") => 253,245,230,
Gray13("gray13") => 33,33,33,
MediumPurple("medium purple", "MediumPurple") => 147,112,219,
PaleGreen1("PaleGreen1") => 154,255,154,
Grey43("grey43") => 110,110,110,
Chocolate4("chocolate4") => 139,69,19,
Chocolate1("chocolate1") => 255,127,36,
DarkSeaGreen2("DarkSeaGreen2") => 180,238,180,
Gray77("gray77") => 196,196,196,
Maroon4("maroon4") => 139,28,98,
Gray2("gray2") => 5,5,5,
OrangeRed2("OrangeRed2") => 238,64,0,
PaleGreen4("PaleGreen4") => 84,139,84,
DarkGoldenrod3("DarkGoldenrod3") => 205,149,12,
Grey67("grey67") => 171,171,171,
PaleGreen("pale green", "PaleGreen") => 152,251,152,
Grey1("grey1") => 3,3,3,
PapayaWhip("papaya whip", "PapayaWhip") => 255,239,213,
Gray11("gray11") => 28,28,28,
Grey41("grey41") => 105,105,105,
Gray98("gray98") => 250,250,250,
Grey56("grey56") => 143,143,143,
Plum3("plum3") => 205,150,205,
DeepPink3("DeepPink3") => 205,16,118,
Grey64("grey64") => 163,163,163,
Grey26("grey26") => 66,66,66,
DodgerBlue("dodger blue", "DodgerBlue") => 30,144,255,
Grey42("grey42") => 107,107,107,
SlateBlue3("SlateBlue3") => 105,89,205,
Gold4("gold4") => 139,117,0,
DarkOrchid1("DarkOrchid1") => 191,62,255,
Grey("grey") => 190,190,190,
Ivory("ivory") => 255,255,240,
DarkSlateGray2("DarkSlateGray2") => 141,238,238,
Gray12("gray12") => 31,31,31,
Grey16("grey16") => 41,41,41,
LightCyan4("LightCyan4") => 122,139,139,
Burlywood2("burlywood2") => 238,197,145,
LemonChiffon("lemon chiffon", "LemonChiffon") => 255,250,205,
DeepPink2("DeepPink2") => 238,18,137,
Gray7("gray7") => 18,18,18,
Magenta3("magenta3") => 205,0,205,
LightSalmon3("LightSalmon3") => 205,129,98,
LemonChiffon2("LemonChiffon2") => 238,233,191,
Gray79("gray79") => 201,201,201,
DeepSkyBlue2("DeepSkyBlue2") => 0,178,238,
Burlywood("burlywood") => 222,184,135,
Grey30("grey30") => 77,77,77,
Green("green") => 0,255,0,
Navy("navy") => 0,0,128,
Grey66("grey66") => 168,168,168,
Grey87("grey87") => 222,222,222,
Honeydew4("honeydew4") => 131,139,131,
MistyRose1("MistyRose1") => 255,228,225,
Gray18("gray18") => 46,46,46,
Ivory2("ivory2") => 238,238,224,
LavenderBlush1("LavenderBlush1") => 255,240,245,
DodgerBlue1("DodgerBlue1") => 30,144,255,
Chartreuse4("chartreuse4") => 69,139,0,
MistyRose3("MistyRose3") => 205,183,181,
Grey28("grey28") => 71,71,71,
DarkSlateGray1("DarkSlateGray1") => 151,255,255,
Coral("coral") => 255,127,80,
Gray97("gray97") => 247,247,247,
Firebrick1("firebrick1") => 255,48,48,
DarkSeaGreen1("DarkSeaGreen1") => 193,255,193,
LightGrey("light grey", "LightGrey") => 211,211,211,
DarkSlateGray4("DarkSlateGray4") => 82,139,139,
Cornsilk("cornsilk") => 255,248,220,
Thistle3("thistle3") => 205,181,205,
Grey4("grey4") => 10,10,10,
DarkMagenta("dark magenta", "DarkMagenta") => 139,0,139,
Cyan1("cyan1") => 0,255,255,
Red4("red4") => 139,0,0,
Salmon1("salmon1") => 255,140,105,
PeachPuff4("PeachPuff4") => 139,119,101,
LightBlue4("LightBlue4") => 104,131,139,
SteelBlue2("SteelBlue2") => 92,172,238,
LightSeaGreen("light sea green", "LightSeaGreen") => 32,178,170,
CadetBlue4("CadetBlue4") => 83,134,139,
NavyBlue("navy blue", "NavyBlue") => 0,0,128,
SeaGreen1("SeaGreen1") => 84,255,159,
DarkSlateGray3("DarkSlateGray3") => 121,205,205,
Gray29("gray29") => 74,74,74,
Grey23("grey23") => 59,59,59,
LightYellow2("LightYellow2") => 238,238,209,
LightSlateGrey("light slate grey", "LightSlateGrey") => 119,136,153,
Gray88("gray88") => 224,224,224,
Seashell1("seashell1") => 255,245,238,
DarkGreen("dark green", "DarkGreen") => 0,100,0,
DarkGoldenrod("dark goldenrod", "DarkGoldenrod") => 184,134,11,
LightSkyBlue2("LightSkyBlue2") => 164,211,238,
Thistle4("thistle4") => 139,123,139,
Gray75("gray75") => 191,191,191,
RoyalBlue1("RoyalBlue1") => 72,118,255,
CadetBlue1("CadetBlue1") => 152,245,255,
Gray100("gray100") => 255,255,255,
OliveDrab1("OliveDrab1") => 192,255,62,
Purple1("purple1") => 155,48,255,
PeachPuff2("PeachPuff2") => 238,203,173,
Purple4("purple4") => 85,26,139,
SlateBlue1("SlateBlue1") => 131,111,255,
PaleVioletRed2("PaleVioletRed2") => 238,121,159,
Grey32("grey32") => 82,82,82,
LightGoldenrod("light goldenrod", "LightGoldenrod") => 238,221,130,
}

View File

@ -11,6 +11,7 @@ use super::{
hlwmbool::ToggleBool, hlwmbool::ToggleBool,
hook::Hook, hook::Hook,
key::{KeyUnbind, Keybind, Mousebind}, key::{KeyUnbind, Keybind, Mousebind},
pad::Pad,
rule::Rule, rule::Rule,
setting::{FrameLayout, Setting, SettingName}, setting::{FrameLayout, Setting, SettingName},
split, split,
@ -164,6 +165,19 @@ pub enum HlwmCommand {
Try(Box<HlwmCommand>), Try(Box<HlwmCommand>),
/// executes the provided command, but discards its output and only returns its exit code. /// executes the provided command, but discards its output and only returns its exit code.
Silent(Box<HlwmCommand>), Silent(Box<HlwmCommand>),
/// Prints the rectangle of the specified monitor in the format: X Y W H
/// If no monitor is given, then the current monitor is used.
///
/// If `without_pad` is supplied, then the remaining rect without the pad around this
/// monitor is printed.
MonitorRect {
monitor: Option<Monitor>,
without_pad: bool,
},
Pad {
monitor: Monitor,
pad: Pad,
},
} }
impl FromStr for Box<HlwmCommand> { impl FromStr for Box<HlwmCommand> {
@ -469,64 +483,27 @@ impl HlwmCommand {
index: _, index: _,
skip_visible: _, skip_visible: _,
} => { } => {
if args.contains(&"--skip-visible".to_string()) { parse!(skip_visible: [Flag("--skip-visible")], index: FromStr => MoveIndex)
Ok(HlwmCommand::MoveIndex {
index: args
.into_iter()
.filter(|a| a != "--skip-visible")
.next()
.ok_or(CommandParseError::BadArgument {
command: command.to_string(),
})?
.parse()?,
skip_visible: true,
})
} else {
Ok(HlwmCommand::MoveIndex {
index: args
.into_iter()
.next()
.ok_or(CommandParseError::BadArgument {
command: command.to_string(),
})?
.parse()?,
skip_visible: false,
})
}
} }
HlwmCommand::UseIndex { HlwmCommand::UseIndex {
index: _, index: _,
skip_visible: _, skip_visible: _,
} => { } => {
if args.contains(&"--skip-visible".to_string()) { parse!(skip_visible: [Flag("--skip-visible")], index: FromStr => UseIndex)
Ok(HlwmCommand::UseIndex {
index: args
.into_iter()
.filter(|a| a != "--skip-visible")
.next()
.ok_or(CommandParseError::BadArgument {
command: command.to_string(),
})?
.parse()?,
skip_visible: true,
})
} else {
Ok(HlwmCommand::UseIndex {
index: args
.into_iter()
.next()
.ok_or(CommandParseError::BadArgument {
command: command.to_string(),
})?
.parse()?,
skip_visible: false,
})
}
} }
HlwmCommand::UseTag(_) => parse!(String => UseTag), HlwmCommand::UseTag(_) => parse!(String => UseTag),
HlwmCommand::MoveTag(_) => parse!(String => MoveTag), HlwmCommand::MoveTag(_) => parse!(String => MoveTag),
HlwmCommand::Try(_) => parse!(FromStrAll => Try), HlwmCommand::Try(_) => parse!(FromStrAll => Try),
HlwmCommand::Silent(_) => parse!(FromStrAll => Silent), HlwmCommand::Silent(_) => parse!(FromStrAll => Silent),
HlwmCommand::MonitorRect {
monitor: _,
without_pad: _,
} => {
parse!(without_pad: [Flag("-p")], monitor: [Option<FromStr>] => MonitorRect)
}
HlwmCommand::Pad { monitor: _, pad: _ } => {
parse!(monitor: FromStr, pad: FromStrAll => Pad)
}
}?; }?;
assert_eq!(command.to_string(), parsed_command.to_string()); assert_eq!(command.to_string(), parsed_command.to_string());
@ -732,6 +709,21 @@ impl ToCommandString for HlwmCommand {
HlwmCommand::Try(cmd) | HlwmCommand::Silent(cmd) => { HlwmCommand::Try(cmd) | HlwmCommand::Silent(cmd) => {
format!("{self}\t{}", cmd.to_command_string()) format!("{self}\t{}", cmd.to_command_string())
} }
HlwmCommand::MonitorRect {
monitor,
without_pad,
} => {
let mut parts = Vec::with_capacity(3);
parts.push(self.to_string());
if let Some(monitor) = monitor {
parts.push(monitor.to_string());
}
if *without_pad {
parts.push("-p".to_string());
}
parts.join("\t")
}
HlwmCommand::Pad { monitor, pad } => format!("{self}\t{monitor}\t{pad}"),
}; };
if let Some(s) = cmd_string.strip_suffix('\t') { if let Some(s) = cmd_string.strip_suffix('\t') {
return s.to_string(); return s.to_string();
@ -751,6 +743,7 @@ mod test {
hlwmbool::ToggleBool, hlwmbool::ToggleBool,
hook::Hook, hook::Hook,
key::{Key, KeyUnbind, MouseButton, Mousebind, MousebindAction}, key::{Key, KeyUnbind, MouseButton, Mousebind, MousebindAction},
pad::Pad,
rule::{Condition, Consequence, Rule, RuleOperator}, rule::{Condition, Consequence, Rule, RuleOperator},
setting::{Setting, SettingName}, setting::{Setting, SettingName},
window::Window, window::Window,
@ -994,6 +987,23 @@ mod test {
})), })),
"silent\tmerge_tag\tdefault".into(), "silent\tmerge_tag\tdefault".into(),
), ),
HlwmCommand::MonitorRect {
monitor: _,
without_pad: _,
} => (
HlwmCommand::MonitorRect {
monitor: Some(1),
without_pad: true,
},
"monitor_rect\t1\t-p".into(),
),
HlwmCommand::Pad { monitor: _, pad: _ } => (
HlwmCommand::Pad {
monitor: 1,
pad: Pad::UpRightDownLeft(2, 3, 4, 5),
},
"pad\t1\t2\t3\t4\t5".into(),
),
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
for (command, expected_string) in commands { for (command, expected_string) in commands {

View File

@ -48,6 +48,17 @@ macro_rules! gen_parse {
}) })
} }
}; };
(Argument $$args:ident: [Flag($pat:literal)]) => {
{
let mut args = $$args.clone().into_iter();
let mut flag = false;
if args.any(|i| i == $pat) {
$$args = $$args.filter(|a| a != $pat).collect::<Vec<_>>().into_iter();
flag = true;
}
flag
}
};
(Argument $$args:ident: String) => { (Argument $$args:ident: String) => {
$$args $$args
.next() .next()

View File

@ -2,7 +2,6 @@ use std::{
convert::Infallible, convert::Infallible,
fmt::Display, fmt::Display,
num::{ParseFloatError, ParseIntError}, num::{ParseFloatError, ParseIntError},
os::unix::process::ExitStatusExt,
process::{self, Stdio}, process::{self, Stdio},
str::FromStr, str::FromStr,
}; };
@ -12,6 +11,8 @@ use serde::{Deserialize, Serialize};
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use thiserror::Error; use thiserror::Error;
use crate::cmd;
use self::{ use self::{
attribute::{Attribute, AttributeError}, attribute::{Attribute, AttributeError},
command::{CommandError, HlwmCommand}, command::{CommandError, HlwmCommand},
@ -28,6 +29,7 @@ mod hlwmbool;
pub mod hook; pub mod hook;
pub mod key; pub mod key;
mod octal; mod octal;
pub mod pad;
pub mod rule; pub mod rule;
pub mod setting; pub mod setting;
mod split; mod split;
@ -61,46 +63,15 @@ impl Client {
let args = command.args(); let args = command.args();
debug!("running command: [{}]", (&args).join(" "),); debug!("running command: [{}]", (&args).join(" "),);
let output = Self::herbstclient() let output = Self::herbstclient()
.arg("--no-newline")
.args(args) .args(args)
.stderr(Stdio::null()) .stderr(Stdio::null())
.stdin(Stdio::null()) .stdin(Stdio::null())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn()? .spawn()?
.wait_with_output()?; .wait_with_output()?;
let exit_status = output.status; cmd::check_status(&output)?;
if let Some(code) = exit_status.code() { Ok(output)
if code == 0 {
return Ok(output);
} else {
let output = String::from_utf8(output.stdout)
.unwrap_or_default()
.trim()
.to_string();
error!("command failed with error code [{code}]");
if !output.is_empty() {
error!("command output: {output}");
}
return Err(CommandError::StatusCode(
code,
if output.is_empty() {
None
} else {
Some(output)
},
));
}
}
if let Some(signal) = exit_status.signal() {
return Err(CommandError::KilledBySignal {
signal,
core_dumped: exit_status.core_dumped(),
});
}
if let Some(signal) = exit_status.stopped_signal() {
return Err(CommandError::StoppedBySignal(signal));
}
Err(CommandError::OtherExitStatus(exit_status))
} }
pub fn execute_iter<I>(&self, commands: I) -> Result<(), (HlwmCommand, CommandError)> pub fn execute_iter<I>(&self, commands: I) -> Result<(), (HlwmCommand, CommandError)>

58
src/hlwm/pad.rs Normal file
View File

@ -0,0 +1,58 @@
use std::{fmt::Display, str::FromStr};
use crate::hlwm::split;
use super::StringParseError;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Pad {
Unchanged,
Up(u32),
UpRight(u32, u32),
UpRightDown(u32, u32, u32),
UpRightDownLeft(u32, u32, u32, u32),
}
impl FromStr for Pad {
type Err = StringParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts = split::tab_or_space(s);
match parts.len() {
0 => Err(StringParseError::InvalidLength(0, "pad")),
1 => Ok(Pad::Up(parts[0].parse()?)),
2 => Ok(Pad::UpRight(parts[0].parse()?, parts[1].parse()?)),
3 => Ok(Pad::UpRightDown(
parts[0].parse()?,
parts[1].parse()?,
parts[2].parse()?,
)),
_ => Ok(Pad::UpRightDownLeft(
parts[0].parse()?,
parts[1].parse()?,
parts[2].parse()?,
parts[3].parse()?,
)),
}
}
}
impl Display for Pad {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Pad::Unchanged => Ok(()),
Pad::Up(up) => f.write_str(&up.to_string()),
Pad::UpRight(up, right) => write!(f, "{up}\t{right}"),
Pad::UpRightDown(up, right, down) => write!(f, "{up}\t{right}\t{down}"),
Pad::UpRightDownLeft(up, right, down, left) => {
write!(f, "{up}\t{right}\t{down}\t{left}")
}
}
}
}
impl Default for Pad {
fn default() -> Self {
Pad::Unchanged
}
}

View File

@ -5,8 +5,10 @@ use clap::{Parser, Subcommand};
use config::Config; use config::Config;
use log::{error, info}; use log::{error, info};
pub mod cmd;
mod config; mod config;
mod hlwm; mod hlwm;
mod panel;
#[derive(Parser, Debug, Clone)] #[derive(Parser, Debug, Clone)]
#[command(name = "hlctl")] #[command(name = "hlctl")]
@ -19,13 +21,6 @@ struct Args {
enum HlctlCommand { enum HlctlCommand {
/// Initialize herbstluftwm, should be run from the autostart script /// Initialize herbstluftwm, should be run from the autostart script
Init, Init,
#[command(arg_required_else_help = true)]
/// Draws notifications using dzen2
Notify {
/// Amount of seconds to persist for
#[arg(short = 's', long = None, default_value_t = 1)]
seconds: u8,
},
/// Save the currently loaded configuration to file /// Save the currently loaded configuration to file
#[command(long_about = r#" #[command(long_about = r#"
`save` Tries to find an existing config file. If not present, the default config is used. `save` Tries to find an existing config file. If not present, the default config is used.
@ -34,7 +29,9 @@ enum HlctlCommand {
The configuration file located at $HOME/.config/herbstluftwm/hlctl.toml"#)] The configuration file located at $HOME/.config/herbstluftwm/hlctl.toml"#)]
Save, Save,
/// Print the toml config to stdout
PrintConfig, PrintConfig,
/// Start the top panel
Panel, Panel,
} }
@ -44,10 +41,13 @@ fn main() {
let args = Args::parse(); let args = Args::parse();
match args.command { match args.command {
HlctlCommand::Init => init(), HlctlCommand::Init => init(),
HlctlCommand::Notify { seconds } => println!("notify for {seconds}"),
HlctlCommand::Save => save(), HlctlCommand::Save => save(),
HlctlCommand::PrintConfig => print_config(), HlctlCommand::PrintConfig => print_config(),
HlctlCommand::Panel => todo!(), HlctlCommand::Panel => {
if let Err(err) = panel::panel(&merged_config()) {
error!("panel: {err}");
}
}
} }
} }

81
src/panel.rs Normal file
View File

@ -0,0 +1,81 @@
use cnx::{
text::{Attributes, Font, Padding, PagerAttributes},
widgets::{ActiveWindowTitle, Clock, Pager},
Cnx,
};
use thiserror::Error;
use crate::{
config::Config,
hlwm::{
color::{Color, X11Color},
command::CommandError,
},
};
type Result<T> = std::result::Result<T, PanelError>;
#[derive(Debug, Error)]
pub enum PanelError {
#[error("command error: [{0}]")]
CommandError(#[from] CommandError),
#[error("io lib error: [{0}]")]
IoError(#[from] std::io::Error),
#[error("cnx panel error: [{0}]")]
CnxPanelError(#[from] anyhow::Error),
}
pub fn panel(config: &Config) -> Result<()> {
let font = Font::new(&config.font_pango);
let font_bold = Font::new(&config.font_pango_bold);
let (active, normal, text) = (
config
.theme
.active_color()
.expect("could not get theme's active color"),
config
.theme
.normal_color()
.expect("could not get theme's normal color"),
config
.theme
.text_color()
.expect("could not get theme's text color"),
);
let attr = Attributes {
font: font.clone(),
fg_color: text.into(),
bg_color: Some(Color::BLACK.into()),
padding: Padding::new(10.0, 10.0, 0.0, 0.0),
};
let pager_attr = PagerAttributes {
active_attr: Attributes {
font: font_bold.clone(),
fg_color: text.into(),
bg_color: Some(active.into()),
padding: Padding::new(8.0, 8.0, 0.0, 0.0),
},
inactive_attr: Attributes {
font: font.clone(),
fg_color: Color::X11(X11Color::DimGray).into(),
bg_color: Some(Color::BLACK.into()),
padding: Padding::new(5.0, 5.0, 0.0, 0.0),
},
non_empty_attr: Attributes {
font: font.clone(),
fg_color: text.into(),
bg_color: Some(normal.into()),
padding: Padding::new(5.0, 5.0, 0.0, 0.0),
},
};
let mut cnx = Cnx::new(cnx::Position::Top);
cnx.add_widget(Pager::new(pager_attr));
cnx.add_widget(ActiveWindowTitle::new(attr.clone()));
cnx.add_widget(Clock::new(
attr.clone(),
Some(String::from("%H:%M %a %Y-%m-%d")),
));
cnx.run()?;
Ok(())
}