From 4036ed42f01329d9e5a03688263d06df7819d1bf Mon Sep 17 00:00:00 2001 From: emilis Date: Fri, 1 Mar 2024 18:06:52 +0000 Subject: [PATCH] add panel using cnx --- Cargo.lock | 886 +++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 6 + src/cmd.rs | 42 ++ src/config.rs | 70 +++- src/hlwm/color.rs | 426 -------------------- src/hlwm/color/mod.rs | 151 +++++++ src/hlwm/color/x11.rs | 728 ++++++++++++++++++++++++++++++++++ src/hlwm/command.rs | 106 ++--- src/hlwm/macros.rs | 11 + src/hlwm/mod.rs | 41 +- src/hlwm/pad.rs | 58 +++ src/main.rs | 18 +- src/panel.rs | 81 ++++ 13 files changed, 2072 insertions(+), 552 deletions(-) create mode 100644 src/cmd.rs delete mode 100644 src/hlwm/color.rs create mode 100644 src/hlwm/color/mod.rs create mode 100644 src/hlwm/color/x11.rs create mode 100644 src/hlwm/pad.rs create mode 100644 src/panel.rs diff --git a/Cargo.lock b/Cargo.lock index 7bdab86..d0a356e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" version = "1.1.2" @@ -11,6 +26,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.8" @@ -46,7 +76,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -56,7 +86,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -65,12 +95,128 @@ version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +[[package]] +name = "bumpalo" +version = "3.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" + +[[package]] +name = "cairo-rs" +version = "0.16.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3125b15ec28b84c238f6f476c6034016a5f6cc0221cb514ca46c532139fc97d" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c48f4af05fabdcfa9658178e1326efa061853f040ce7d72e33af6885196f421" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "cc" +version = "1.0.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" + +[[package]] +name = "cfg-expr" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa50868b64a9a6fda9d593ce778849ea8715cd2a3d2cc17ffdb4a2f2f2f1961d" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.0", +] + [[package]] name = "clap" version = "4.4.18" @@ -102,7 +248,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.48", ] [[package]] @@ -111,12 +257,46 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +[[package]] +name = "cnx" +version = "0.3.0" +source = "git+https://github.com/mjkillough/cnx.git?rev=7845d99baa296901171c083db61588a62f9a8b34#7845d99baa296901171c083db61588a62f9a8b34" +dependencies = [ + "anyhow", + "async-stream", + "cairo-rs", + "cairo-sys-rs", + "chrono", + "colors-transform", + "futures", + "lazy_static", + "ordered-float", + "pango", + "pangocairo", + "tokio", + "tokio-stream", + "xcb", + "xcb-util", +] + [[package]] name = "colorchoice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "colors-transform" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9226dbc05df4fb986f48d730b001532580883c4c06c5d1c213f4b34c1c157178" + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "diff" version = "0.1.13" @@ -155,7 +335,193 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "gio" +version = "0.16.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a1c84b4534a290a29160ef5c6eff2a9c95833111472e824fc5cb78b513dd092" +dependencies = [ + "bitflags 1.3.2", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9b693b8e39d042a95547fc258a7b07349b1f0b48f4b2fa3108ba3c51c0b5229" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16aa2475c9debed5a32832cb5ff2af5a3f9e1ab9e69df58eaadc1ab2004d6eba" +dependencies = [ + "bitflags 1.3.2", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb1a9325847aa46f1e96ffea37611b9d51fc4827e67f79e7de502a297560a67b" +dependencies = [ + "anyhow", + "heck", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "glib-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61a4f46316d06bfa33a7ac22df6f0524c8be58e3db2d9ca99ccb1f357b62a65" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "gobject-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3520bb9c07ae2a12c7f2fbb24d4efc11231c8146a86956413fb1a79bb760a0f1" +dependencies = [ + "glib-sys", + "libc", + "system-deps", ] [[package]] @@ -182,6 +548,7 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", + "cnx", "log", "paste", "pretty_assertions", @@ -200,7 +567,7 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -209,6 +576,29 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "indexmap" version = "2.1.0" @@ -227,7 +617,7 @@ checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ "hermit-abi", "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -236,6 +626,21 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +[[package]] +name = "js-sys" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.153" @@ -260,18 +665,146 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "ordered-float" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7" +dependencies = [ + "num-traits", +] + +[[package]] +name = "pango" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdff66b271861037b89d028656184059e03b0b6ccb36003820be19f7200b1e94" +dependencies = [ + "bitflags 1.3.2", + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e134909a9a293e04d2cc31928aa95679c5e4df954d0b85483159bd20d8f047f" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "pangocairo" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16ad2ec87789371b551fd2367c10aa37060412ffd3e60abd99491b21b93a3f9b" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "glib", + "libc", + "pango", + "pangocairo-sys", +] + +[[package]] +name = "pangocairo-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "848d2df9b7f1a8c7a19d994de443bcbe5d4382610ccb8e64247f932be74fcf76" +dependencies = [ + "cairo-sys-rs", + "glib-sys", + "libc", + "pango-sys", + "system-deps", +] + [[package]] name = "paste" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + [[package]] name = "pretty_assertions" version = "1.4.0" @@ -292,6 +825,40 @@ dependencies = [ "log", ] +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.76" @@ -339,17 +906,23 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustix" version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ - "bitflags", + "bitflags 2.4.2", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -381,7 +954,7 @@ checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.48", ] [[package]] @@ -404,6 +977,31 @@ dependencies = [ "serde", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "strsim" version = "0.10.0" @@ -429,7 +1027,18 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.48", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] @@ -443,6 +1052,25 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "system-deps" +version = "6.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2d580ff6a20c55dfb86be5f9c238f67835d0e81cbdea8bf5680e0897320331" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + [[package]] name = "termcolor" version = "1.4.1" @@ -469,7 +1097,45 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.48", +] + +[[package]] +name = "tokio" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +dependencies = [ + "backtrace", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", ] [[package]] @@ -481,7 +1147,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.21.0", ] [[package]] @@ -493,6 +1159,17 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + [[package]] name = "toml_edit" version = "0.21.0" @@ -518,6 +1195,78 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" + [[package]] name = "which" version = "6.0.0" @@ -528,7 +1277,7 @@ dependencies = [ "home", "once_cell", "rustix", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -562,13 +1311,46 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -577,51 +1359,93 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.0" @@ -637,6 +1461,26 @@ dependencies = [ "memchr", ] +[[package]] +name = "xcb" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62056f63138b39116f82a540c983cc11f1c90cd70b3d492a70c25eaa50bd22a6" +dependencies = [ + "libc", + "log", +] + +[[package]] +name = "xcb-util" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43893e47f27bf7d81d489feef3a0e34a457e90bc314b7e74ad9bb3980e4c1c48" +dependencies = [ + "libc", + "xcb", +] + [[package]] name = "yansi" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 705926a..bb0b545 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,12 @@ toml = "0.8.8" which = "6.0.0" log = "0.4" pretty_env_logger = "0.5" +cnx = { git = "https://github.com/mjkillough/cnx.git", rev = "7845d99baa296901171c083db61588a62f9a8b34" } [dev-dependencies] pretty_assertions = "1.4.0" + +[profile.release] +opt-level = 3 +strip = "debuginfo" +lto = "fat" diff --git a/src/cmd.rs b/src/cmd.rs new file mode 100644 index 0000000..7828e68 --- /dev/null +++ b/src/cmd.rs @@ -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)) +} diff --git a/src/config.rs b/src/config.rs index d593bb2..8162f22 100644 --- a/src/config.rs +++ b/src/config.rs @@ -60,8 +60,13 @@ pub enum ConfigError { #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct Config { - pub font: Option, - pub font_bold: Option, + pub font: 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 keybinds: Vec, pub mousebinds: Vec, @@ -210,9 +215,9 @@ impl Config { fn attrs_set(&self) -> Result, ConfigError> { info!("loading attr settings command set"); Ok([ - (Self::FONT, self.font.clone()), + (Self::FONT, Some(self.font.clone())), (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, if self.services.len() == 0 { @@ -393,16 +398,14 @@ impl Config { Config { font: setting( Self::FONT, - |f| -> Result<_, Infallible> { Ok(Some(f.to_string())) }, + |f| -> Result<_, Infallible> { Ok(f.to_string()) }, default.font, ), mod_key: setting(Self::MOD_KEY, Key::from_str, default.mod_key), - font_bold: Some( - client - .get_attr(ThemeAttr::TitleFont(String::new()).attr_path()) - .map(|a| a.to_string()) - .unwrap_or(default.font_bold.unwrap()), - ), + font_bold: client + .get_attr(ThemeAttr::TitleFont(String::new()).attr_path()) + .map(|a| a.to_string()) + .unwrap_or(default.font_bold), services: setting( Self::SERVICES, |v| serde_json::de::from_str(v), @@ -508,6 +511,45 @@ pub struct Theme { pub attributes: Vec, } +#[allow(unused)] +impl Theme { + pub fn active_color(&self) -> Option { + for attr in &self.attributes { + if let ThemeAttr::ActiveColor(col) = attr { + return Some(col.clone()); + } + } + None + } + + pub fn normal_color(&self) -> Option { + for attr in &self.attributes { + if let ThemeAttr::NormalColor(col) = attr { + return Some(col.clone()); + } + } + None + } + + pub fn urgent_color(&self) -> Option { + for attr in &self.attributes { + if let ThemeAttr::UrgentColor(col) = attr { + return Some(col.clone()); + } + } + None + } + + pub fn text_color(&self) -> Option { + for attr in &self.attributes { + if let ThemeAttr::TitleColor(col) = attr { + return Some(col.clone()); + } + } + None + } +} + #[derive(Debug, Clone, Copy, PartialEq, Error)] pub enum InclusiveError { #[error("out of range")] @@ -712,8 +754,8 @@ impl Default for Config { Self { mod_key, - font: Some(String::from("-*-fixed-medium-*-*-*-12-*-*-*-*-*-*-*")), - font_bold: Some(font_bold.clone()), + font: String::from("-*-fixed-medium-*-*-*-12-*-*-*-*-*-*-*"), + font_bold: font_bold.clone(), keybinds: vec![ Keybind::new( [mod_key, Key::Shift, Key::Char('r')].into_iter(), @@ -944,6 +986,8 @@ impl Default for Config { Setting::FrameBorderActiveColor(active_color.clone()), Setting::FrameBgActiveColor(active_color.clone()), ], + font_pango: String::from("Monospace 9"), + font_pango_bold: String::from("Monospace Bold 9"), } } } diff --git a/src/hlwm/color.rs b/src/hlwm/color.rs deleted file mode 100644 index 8ba26e4..0000000 --- a/src/hlwm/color.rs +++ /dev/null @@ -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(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.to_string()) - } -} - -impl<'de> Deserialize<'de> for Color { - fn deserialize(deserializer: D) -> Result - 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 { - 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 { - 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::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)); - } -} diff --git a/src/hlwm/color/mod.rs b/src/hlwm/color/mod.rs new file mode 100644 index 0000000..30fbb1a --- /dev/null +++ b/src/hlwm/color/mod.rs @@ -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(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for Color { + fn deserialize(deserializer: D) -> Result + 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 { + 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 { + 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 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)); + } +} diff --git a/src/hlwm/color/x11.rs b/src/hlwm/color/x11.rs new file mode 100644 index 0000000..3891a6c --- /dev/null +++ b/src/hlwm/color/x11.rs @@ -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::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 { + 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, +} diff --git a/src/hlwm/command.rs b/src/hlwm/command.rs index 64b244e..5b40b0d 100644 --- a/src/hlwm/command.rs +++ b/src/hlwm/command.rs @@ -11,6 +11,7 @@ use super::{ hlwmbool::ToggleBool, hook::Hook, key::{KeyUnbind, Keybind, Mousebind}, + pad::Pad, rule::Rule, setting::{FrameLayout, Setting, SettingName}, split, @@ -164,6 +165,19 @@ pub enum HlwmCommand { Try(Box), /// executes the provided command, but discards its output and only returns its exit code. Silent(Box), + /// 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, + without_pad: bool, + }, + Pad { + monitor: Monitor, + pad: Pad, + }, } impl FromStr for Box { @@ -469,64 +483,27 @@ impl HlwmCommand { index: _, skip_visible: _, } => { - if args.contains(&"--skip-visible".to_string()) { - 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, - }) - } + parse!(skip_visible: [Flag("--skip-visible")], index: FromStr => MoveIndex) } HlwmCommand::UseIndex { index: _, skip_visible: _, } => { - if args.contains(&"--skip-visible".to_string()) { - 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, - }) - } + parse!(skip_visible: [Flag("--skip-visible")], index: FromStr => UseIndex) } HlwmCommand::UseTag(_) => parse!(String => UseTag), HlwmCommand::MoveTag(_) => parse!(String => MoveTag), HlwmCommand::Try(_) => parse!(FromStrAll => Try), HlwmCommand::Silent(_) => parse!(FromStrAll => Silent), + HlwmCommand::MonitorRect { + monitor: _, + without_pad: _, + } => { + parse!(without_pad: [Flag("-p")], monitor: [Option] => MonitorRect) + } + HlwmCommand::Pad { monitor: _, pad: _ } => { + parse!(monitor: FromStr, pad: FromStrAll => Pad) + } }?; assert_eq!(command.to_string(), parsed_command.to_string()); @@ -732,6 +709,21 @@ impl ToCommandString for HlwmCommand { HlwmCommand::Try(cmd) | HlwmCommand::Silent(cmd) => { 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') { return s.to_string(); @@ -751,6 +743,7 @@ mod test { hlwmbool::ToggleBool, hook::Hook, key::{Key, KeyUnbind, MouseButton, Mousebind, MousebindAction}, + pad::Pad, rule::{Condition, Consequence, Rule, RuleOperator}, setting::{Setting, SettingName}, window::Window, @@ -994,6 +987,23 @@ mod test { })), "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::>(); for (command, expected_string) in commands { diff --git a/src/hlwm/macros.rs b/src/hlwm/macros.rs index c265555..439ad1e 100644 --- a/src/hlwm/macros.rs +++ b/src/hlwm/macros.rs @@ -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::>().into_iter(); + flag = true; + } + flag + } + }; (Argument $$args:ident: String) => { $$args .next() diff --git a/src/hlwm/mod.rs b/src/hlwm/mod.rs index e55ad19..b6337f3 100644 --- a/src/hlwm/mod.rs +++ b/src/hlwm/mod.rs @@ -2,7 +2,6 @@ use std::{ convert::Infallible, fmt::Display, num::{ParseFloatError, ParseIntError}, - os::unix::process::ExitStatusExt, process::{self, Stdio}, str::FromStr, }; @@ -12,6 +11,8 @@ use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; use thiserror::Error; +use crate::cmd; + use self::{ attribute::{Attribute, AttributeError}, command::{CommandError, HlwmCommand}, @@ -28,6 +29,7 @@ mod hlwmbool; pub mod hook; pub mod key; mod octal; +pub mod pad; pub mod rule; pub mod setting; mod split; @@ -61,46 +63,15 @@ impl Client { let args = command.args(); debug!("running command: [{}]", (&args).join(" "),); let output = Self::herbstclient() + .arg("--no-newline") .args(args) .stderr(Stdio::null()) .stdin(Stdio::null()) .stdout(Stdio::piped()) .spawn()? .wait_with_output()?; - let exit_status = output.status; - if let Some(code) = exit_status.code() { - 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)) + cmd::check_status(&output)?; + Ok(output) } pub fn execute_iter(&self, commands: I) -> Result<(), (HlwmCommand, CommandError)> diff --git a/src/hlwm/pad.rs b/src/hlwm/pad.rs new file mode 100644 index 0000000..aafb723 --- /dev/null +++ b/src/hlwm/pad.rs @@ -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 { + 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 + } +} diff --git a/src/main.rs b/src/main.rs index f34e26f..ad3a6a8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,8 +5,10 @@ use clap::{Parser, Subcommand}; use config::Config; use log::{error, info}; +pub mod cmd; mod config; mod hlwm; +mod panel; #[derive(Parser, Debug, Clone)] #[command(name = "hlctl")] @@ -19,13 +21,6 @@ struct Args { enum HlctlCommand { /// Initialize herbstluftwm, should be run from the autostart script 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 #[command(long_about = r#" `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"#)] Save, + /// Print the toml config to stdout PrintConfig, + /// Start the top panel Panel, } @@ -44,10 +41,13 @@ fn main() { let args = Args::parse(); match args.command { HlctlCommand::Init => init(), - HlctlCommand::Notify { seconds } => println!("notify for {seconds}"), HlctlCommand::Save => save(), HlctlCommand::PrintConfig => print_config(), - HlctlCommand::Panel => todo!(), + HlctlCommand::Panel => { + if let Err(err) = panel::panel(&merged_config()) { + error!("panel: {err}"); + } + } } } diff --git a/src/panel.rs b/src/panel.rs new file mode 100644 index 0000000..151c141 --- /dev/null +++ b/src/panel.rs @@ -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 = std::result::Result; + +#[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(()) +}