diff --git a/crates/eww/Cargo.lock b/Cargo.lock similarity index 83% rename from crates/eww/Cargo.lock rename to Cargo.lock index cf1c575..1349726 100644 --- a/crates/eww/Cargo.lock +++ b/Cargo.lock @@ -10,9 +10,9 @@ checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" [[package]] name = "aho-corasick" -version = "0.7.15" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ "memchr", ] @@ -42,10 +42,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486" [[package]] -name = "arrayvec" -version = "0.5.2" +name = "ascii-canvas" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +dependencies = [ + "term", +] [[package]] name = "async-stream" @@ -123,6 +126,12 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "474a626a67200bd107d44179bb3d4fc61891172d11696609264589be6a0e6a43" +[[package]] +name = "beef" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6736e2428df2ca2848d846c43e88745121a6654696e349ce0054a420815a7409" + [[package]] name = "bincode" version = "1.3.3" @@ -132,24 +141,27 @@ dependencies = [ "serde", ] +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -[[package]] -name = "bitvec" -version = "0.19.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - [[package]] name = "bytes" version = "1.0.1" @@ -215,6 +227,29 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e769b5c8c8283982a987c6e948e540254f1058d5a74b8794914d4ef5fc2a24" +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "console" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "terminal_size", + "winapi", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -271,6 +306,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "ctor" version = "0.1.20" @@ -310,12 +351,39 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "doc-comment" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "dtoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" + [[package]] name = "dyn-clone" version = "1.0.4" @@ -328,6 +396,21 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "ena" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +dependencies = [ + "log", +] + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "env_logger" version = "0.7.1" @@ -371,16 +454,15 @@ dependencies = [ "log", "maplit", "nix", - "nom", "notify", "num", "pretty_assertions", "pretty_env_logger", "regex", - "roxmltree", "serde", "serde_json", "simple-signal", + "simplexpr", "smart-default", "structopt", "sysinfo", @@ -390,6 +472,7 @@ dependencies = [ "unescape", "wait-timeout", "x11rb", + "yuck", ] [[package]] @@ -416,6 +499,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "fixedbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "fsevent-sys" version = "4.0.0" @@ -425,12 +520,6 @@ dependencies = [ "libc", ] -[[package]] -name = "funty" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" - [[package]] name = "futures" version = "0.3.15" @@ -647,7 +736,18 @@ checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.10.2+wasi-snapshot-preview1", ] [[package]] @@ -746,7 +846,7 @@ version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "352df9cd46a5538323ba016fdbff8baee4a55011a7349120b0d7280992276fa7" dependencies = [ - "beef", + "beef 0.4.4", "clap", "codemap", "indexmap", @@ -907,6 +1007,22 @@ dependencies = [ "libc", ] +[[package]] +name = "insta" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1b21a2971cea49ca4613c0e9fe8225ecaf5de64090fddc6002284726e9244" +dependencies = [ + "console", + "lazy_static", + "ron", + "serde", + "serde_json", + "serde_yaml", + "similar", + "uuid", +] + [[package]] name = "instant" version = "0.1.10" @@ -940,6 +1056,38 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +[[package]] +name = "lalrpop" +version = "0.19.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15174f1c529af5bf1283c3bc0058266b483a67156f79589fab2a25e23cf8988" +dependencies = [ + "ascii-canvas", + "atty", + "bit-set", + "diff", + "ena", + "itertools 0.10.1", + "lalrpop-util", + "petgraph", + "pico-args", + "regex", + "regex-syntax", + "string_cache", + "term", + "tiny-keccak", + "unicode-xid 0.2.2", +] + +[[package]] +name = "lalrpop-util" +version = "0.19.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e58cce361efcc90ba8a0a5f982c741ff86b603495bb15a998412e957dcd278" +dependencies = [ + "regex", +] + [[package]] name = "lasso" version = "0.3.1" @@ -955,25 +1103,18 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "lexical-core" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" -dependencies = [ - "arrayvec", - "bitflags", - "cfg-if", - "ryu", - "static_assertions", -] - [[package]] name = "libc" version = "0.2.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + [[package]] name = "lock_api" version = "0.4.4" @@ -992,6 +1133,30 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "logos" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427e2abca5be13136da9afdbf874e6b34ad9001dd70f2b103b083a85daa7b345" +dependencies = [ + "logos-derive", +] + +[[package]] +name = "logos-derive" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56a7d287fd2ac3f75b11f19a1c8a874a7d55744bd91f7a1b3e7cf87d4343c36d" +dependencies = [ + "beef 0.5.0", + "fnv", + "proc-macro2", + "quote 1.0.9", + "regex-syntax", + "syn 1.0.73", + "utf8-ranges", +] + [[package]] name = "maplit" version = "1.0.2" @@ -1000,9 +1165,9 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "memchr" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" [[package]] name = "memoffset" @@ -1035,6 +1200,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" + [[package]] name = "nix" version = "0.20.0" @@ -1047,19 +1218,6 @@ dependencies = [ "libc", ] -[[package]] -name = "nom" -version = "6.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c5c51b9083a3c620fa67a2a635d1ce7d95b897e957d6b28ff9a5da960a103a6" -dependencies = [ - "bitvec", - "funty", - "lexical-core", - "memchr", - "version_check", -] - [[package]] name = "notify" version = "5.0.0-pre.10" @@ -1280,6 +1438,16 @@ dependencies = [ "ucd-trie", ] +[[package]] +name = "petgraph" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "phf" version = "0.8.0" @@ -1324,6 +1492,12 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pico-args" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" + [[package]] name = "pin-project-lite" version = "0.2.7" @@ -1348,6 +1522,12 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "pretty_assertions" version = "0.7.2" @@ -1445,19 +1625,13 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "radium" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" - [[package]] name = "rand" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom", + "getrandom 0.1.16", "libc", "rand_chacha", "rand_core", @@ -1481,7 +1655,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom", + "getrandom 0.1.16", ] [[package]] @@ -1537,10 +1711,20 @@ dependencies = [ ] [[package]] -name = "regex" -version = "1.4.6" +name = "redox_users" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759" +checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +dependencies = [ + "getrandom 0.2.3", + "redox_syscall", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" dependencies = [ "aho-corasick", "memchr", @@ -1554,12 +1738,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] -name = "roxmltree" -version = "0.14.1" +name = "ron" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" +checksum = "064ea8613fb712a19faf920022ec8ddf134984f100090764a4e1d768f3827f1f" dependencies = [ - "xmlparser", + "base64", + "bitflags", + "serde", ] [[package]] @@ -1571,6 +1757,12 @@ dependencies = [ "semver", ] +[[package]] +name = "rustversion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" + [[package]] name = "ryu" version = "1.0.5" @@ -1641,6 +1833,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23" +dependencies = [ + "dtoa", + "linked-hash-map", + "serde", + "yaml-rust", +] + [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -1650,6 +1854,12 @@ dependencies = [ "libc", ] +[[package]] +name = "similar" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad1d488a557b235fc46dae55512ffbfc429d2482b08b4d9435ab07384ca8aec" + [[package]] name = "simple-signal" version = "1.1.1" @@ -1660,6 +1870,23 @@ dependencies = [ "libc", ] +[[package]] +name = "simplexpr" +version = "0.1.0" +dependencies = [ + "insta", + "itertools 0.10.1", + "lalrpop", + "lalrpop-util", + "logos", + "maplit", + "regex", + "serde", + "serde_json", + "strum 0.21.0", + "thiserror", +] + [[package]] name = "siphasher" version = "0.3.5" @@ -1695,6 +1922,18 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "string_cache" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ddb1139b5353f96e429e1a5e19fbaf663bddedaa06d1dbd49f82e352601209a" +dependencies = [ + "lazy_static", + "new_debug_unreachable", + "phf_shared", + "precomputed-hash", +] + [[package]] name = "strsim" version = "0.8.0" @@ -1731,6 +1970,15 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" +[[package]] +name = "strum" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2" +dependencies = [ + "strum_macros 0.21.1", +] + [[package]] name = "strum_macros" version = "0.18.0" @@ -1743,6 +1991,18 @@ dependencies = [ "syn 1.0.73", ] +[[package]] +name = "strum_macros" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" +dependencies = [ + "heck", + "proc-macro2", + "quote 1.0.9", + "syn 1.0.73", +] + [[package]] name = "syn" version = "0.11.11" @@ -1798,18 +2058,23 @@ checksum = "0f3ecc17269a19353b3558b313bba738b25d82993e30d62a18406a24aba4649b" dependencies = [ "heck", "pkg-config", - "strum", - "strum_macros", + "strum 0.18.0", + "strum_macros 0.18.0", "thiserror", "toml", "version-compare", ] [[package]] -name = "tap" -version = "1.0.1" +name = "term" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] [[package]] name = "termcolor" @@ -1820,6 +2085,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -1849,6 +2124,15 @@ dependencies = [ "syn 1.0.73", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tokio" version = "1.8.2" @@ -1950,6 +2234,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "utf8-ranges" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" + +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" + [[package]] name = "vec_map" version = "0.8.2" @@ -1994,6 +2290,12 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + [[package]] name = "winapi" version = "0.3.9" @@ -2034,12 +2336,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "wyz" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" - [[package]] name = "x11" version = "2.18.2" @@ -2063,7 +2359,34 @@ dependencies = [ ] [[package]] -name = "xmlparser" -version = "0.13.3" +name = "yaml-rust" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "yuck" +version = "0.1.0" +dependencies = [ + "anyhow", + "codespan-reporting", + "derive_more", + "insta", + "itertools 0.10.1", + "lalrpop", + "lalrpop-util", + "lazy_static", + "maplit", + "pretty_assertions", + "regex", + "serde", + "serde_json", + "simplexpr", + "smart-default", + "static_assertions", + "strum 0.21.0", + "thiserror", +] diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index c89444f..cb57aa2 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -231,7 +231,7 @@ impl App { log::info!("Opening window {}", window_name); let mut window_def = self.eww_config.get_window(window_name)?.clone(); - window_def.geometry = window_def.geometry.override_if_given(anchor, pos, size); + window_def.geometry = window_def.geometry.map(|x| x.override_if_given(anchor, pos, size)); let root_widget = window_def.widget.render(&mut self.eww_state, window_name, &self.eww_config.get_widget_definitions())?; @@ -245,8 +245,7 @@ impl App { // initialize script var handlers for variables that where not used before opening this window. // TODO somehow make this less shit - for newly_used_var in - self.variables_only_used_in(window_name).filter_map(|var| self.eww_config.get_script_var(var).ok()) + for newly_used_var in self.variables_only_used_in(window_name).filter_map(|var| self.eww_config.get_script_var(var).ok()) { self.script_var_handler.add(newly_used_var.clone()); } @@ -306,55 +305,53 @@ fn initialize_window( root_widget: gtk::Widget, window_def: config::EwwWindowDefinition, ) -> Result { - let actual_window_rect = window_def.geometry.get_window_rectangle(monitor_geometry); - if let Some(window) = display_backend::initialize_window(&window_def, monitor_geometry) { - window.set_title(&format!("Eww - {}", window_def.name)); - let wm_class_name = format!("eww-{}", window_def.name); - window.set_wmclass(&wm_class_name, &wm_class_name); - window.set_position(gtk::WindowPosition::Center); + let window = display_backend::initialize_window(&window_def, monitor_geometry) + .with_context(|| format!("monitor {} is unavailable", window_def.screen_number.unwrap()))?; + + window.set_title(&format!("Eww - {}", window_def.name)); + window.set_position(gtk::WindowPosition::None); + window.set_gravity(gdk::Gravity::Center); + + if let Some(geometry) = window_def.geometry { + let actual_window_rect = geometry.get_window_rectangle(monitor_geometry); window.set_size_request(actual_window_rect.width, actual_window_rect.height); window.set_default_size(actual_window_rect.width, actual_window_rect.height); - window.set_decorated(false); - // run on_screen_changed to set the visual correctly initially. - on_screen_changed(&window, None); - window.connect_screen_changed(on_screen_changed); - - window.add(&root_widget); - - window.show_all(); - - apply_window_position(window_def.clone(), monitor_geometry, &window)?; - let gdk_window = window.get_window().context("couldn't get gdk window from gtk window")?; - gdk_window.set_override_redirect(!window_def.focusable); - - #[cfg(feature = "x11")] - display_backend::set_xprops(&window, monitor_geometry, &window_def)?; - - // this should only be required on x11, as waylands layershell should manage the margins properly anways. - #[cfg(feature = "x11")] - window.connect_configure_event({ - let window_def = window_def.clone(); - move |window, _evt| { - let _ = apply_window_position(window_def.clone(), monitor_geometry, &window); - false - } - }); - Ok(EwwWindow { name: window_def.name.clone(), definition: window_def, gtk_window: window }) - } else { - Err(anyhow!("monitor {} is unavailable", window_def.screen_number.unwrap())) } + window.set_decorated(false); + window.set_skip_taskbar_hint(true); + window.set_skip_pager_hint(true); + + // run on_screen_changed to set the visual correctly initially. + on_screen_changed(&window, None); + window.connect_screen_changed(on_screen_changed); + + window.add(&root_widget); + + window.show_all(); + + #[cfg(feature = "x11")] + { + if let Some(geometry) = window_def.geometry { + let _ = apply_window_position(geometry, monitor_geometry, &window); + window.connect_configure_event(move |window, _| { + let _ = apply_window_position(geometry, monitor_geometry, &window); + false + }); + } + display_backend::set_xprops(&window, monitor_geometry, &window_def)?; + } + Ok(EwwWindow { name: window_def.name.clone(), definition: window_def, gtk_window: window }) } /// Apply the provided window-positioning rules to the window. fn apply_window_position( - mut window_def: config::EwwWindowDefinition, + mut window_geometry: config::EwwWindowGeometry, monitor_geometry: gdk::Rectangle, window: >k::Window, ) -> Result<()> { - let (gtk_window_width, gtk_window_height) = window.get_size(); - window_def.geometry.size = Coords { x: NumWithUnit::Pixels(gtk_window_width), y: NumWithUnit::Pixels(gtk_window_height) }; let gdk_window = window.get_window().context("Failed to get gdk window from gtk window")?; - let actual_window_rect = window_def.geometry.get_window_rectangle(monitor_geometry); + window_geometry.size = Coords::from_pixels(window.get_size()); + let actual_window_rect = window_geometry.get_window_rectangle(monitor_geometry); gdk_window.move_(actual_window_rect.x, actual_window_rect.y); Ok(()) } @@ -367,11 +364,13 @@ fn on_screen_changed(window: >k::Window, _old_screen: Option<&gdk::Screen>) { } fn get_default_monitor_index() -> i32 { + #[allow(deprecated)] gdk::Display::get_default().expect("could not get default display").get_default_screen().get_primary_monitor() } /// Get the monitor geometry of a given monitor number fn get_monitor_geometry(n: i32) -> gdk::Rectangle { + #[allow(deprecated)] gdk::Display::get_default().expect("could not get default display").get_default_screen().get_monitor_geometry(n) } diff --git a/crates/eww/src/config/backend_window_options.rs b/crates/eww/src/config/backend_window_options.rs new file mode 100644 index 0000000..f194b3c --- /dev/null +++ b/crates/eww/src/config/backend_window_options.rs @@ -0,0 +1,69 @@ +use crate::config::xml_ext::XmlElement; +use anyhow::*; + +pub use backend::*; + +#[cfg(feature = "x11")] +mod backend { + + use super::*; + use crate::config::{EwwWindowType, StrutDefinition}; + + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct BackendWindowOptions { + pub wm_ignore: bool, + pub sticky: bool, + pub window_type: EwwWindowType, + pub struts: StrutDefinition, + } + + impl BackendWindowOptions { + pub fn from_xml_element(xml: &XmlElement) -> Result { + let struts: Option = xml + .child("reserve") + .ok() + .map(StrutDefinition::from_xml_element) + .transpose() + .context("Failed to parse ")?; + + let window_type = xml.parse_optional_attr("windowtype")?; + + Ok(BackendWindowOptions { + wm_ignore: xml.parse_optional_attr("wm-ignore")?.unwrap_or(window_type.is_none() && struts.is_none()), + window_type: window_type.unwrap_or_default(), + sticky: xml.parse_optional_attr("sticky")?.unwrap_or(true), + struts: struts.unwrap_or_default(), + }) + } + } +} + +#[cfg(feature = "wayland")] +mod backend { + use super::*; + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct BackendWindowOptions { + pub exclusive: bool, + pub focusable: bool, + } + impl BackendWindowOptions { + pub fn from_xml_element(xml: &XmlElement) -> Result { + Ok(BackendWindowOptions { + exclusive: xml.parse_optional_attr("exclusive")?.unwrap_or(false), + focusable: xml.parse_optional_attr("focusable")?.unwrap_or(false), + }) + } + } +} + +#[cfg(feature = "no-x11-wayland")] +mod backend { + use super::*; + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct BackendWindowOptions; + impl BackendWindowOptions { + pub fn from_xml_element(xml: &XmlElement) -> Result { + Ok(BackendWindowOptions) + } + } +} diff --git a/crates/eww/src/config/mod.rs b/crates/eww/src/config/mod.rs index bacbf46..66fe9bd 100644 --- a/crates/eww/src/config/mod.rs +++ b/crates/eww/src/config/mod.rs @@ -8,6 +8,7 @@ use anyhow::*; use element::*; use xml_ext::*; +pub mod backend_window_options; pub mod element; pub mod eww_config; pub mod inbuilt; diff --git a/crates/eww/src/config/window_geometry.rs b/crates/eww/src/config/window_geometry.rs index 75308d4..6e11bfe 100644 --- a/crates/eww/src/config/window_geometry.rs +++ b/crates/eww/src/config/window_geometry.rs @@ -9,12 +9,12 @@ use super::xml_ext::XmlElement; #[derive(Debug, derive_more::Display, Clone, Copy, Eq, PartialEq, SmartDefault, Serialize, Deserialize)] pub enum AnchorAlignment { - #[display("start")] + #[display(fmt = "start")] #[default] START, - #[display("center")] + #[display(fmt = "center")] CENTER, - #[display("end")] + #[display(fmt = "end")] END, } @@ -117,7 +117,7 @@ impl EwwWindowGeometry { }) } - pub fn override_if_given(&mut self, anchor_point: Option, offset: Option, size: Option) -> Self { + pub fn override_if_given(&self, anchor_point: Option, offset: Option, size: Option) -> Self { EwwWindowGeometry { anchor_point: anchor_point.unwrap_or(self.anchor_point), offset: offset.unwrap_or(self.offset), diff --git a/crates/eww/src/display_backend.rs b/crates/eww/src/display_backend.rs index cac9484..8734842 100644 --- a/crates/eww/src/display_backend.rs +++ b/crates/eww/src/display_backend.rs @@ -7,32 +7,13 @@ mod platform { use gtk::{self, prelude::*}; pub fn initialize_window(window_def: &EwwWindowDefinition, _monitor: gdk::Rectangle) -> Option { - let window = if window_def.focusable { - gtk::Window::new(gtk::WindowType::Toplevel) - } else { - gtk::Window::new(gtk::WindowType::Popup) - }; - window.set_resizable(true); - if !window_def.focusable { - window.set_type_hint(gdk::WindowTypeHint::Dock); - } - if window_def.stacking == WindowStacking::Foreground { - window.set_keep_above(true); - } else { - window.set_keep_below(true); - } - Some(window) - } - - pub fn reserve_space_for(_window: >k::Window, _monitor: gdk::Rectangle, _strut_def: StrutDefinition) -> Result<()> { - Err(anyhow!("Cannot reserve space on non X11 or and wayland backends")) + Some(gtk::Window::new(gtk::WindowType::Toplevel)) } } #[cfg(feature = "wayland")] mod platform { - use crate::config::{AnchorAlignment, EwwWindowDefinition, Side, WindowStacking}; - use anyhow::*; + use crate::config::{AnchorAlignment, EwwWindowDefinition, WindowStacking}; use gdk; use gtk::prelude::*; @@ -46,12 +27,12 @@ mod platform { if let Some(monitor) = gdk::Display::get_default().expect("could not get default display").get_monitor(index) { gtk_layer_shell::set_monitor(&window, &monitor); } else { - return None + return None; } } - None => {}, + None => {} }; - window.set_resizable(true); + window.set_resizable(window_def.resizable); // Sets the layer where the layer shell surface will spawn match window_def.stacking { @@ -62,44 +43,46 @@ mod platform { } // Sets the keyboard interactivity - gtk_layer_shell::set_keyboard_interactivity(&window, window_def.focusable); - // Positioning surface - let mut top = false; - let mut left = false; - let mut right = false; - let mut bottom = false; + gtk_layer_shell::set_keyboard_interactivity(&window, window_def.backend_options.focusable); - match window_def.geometry.anchor_point.x { - AnchorAlignment::START => left = true, - AnchorAlignment::CENTER => {} - AnchorAlignment::END => right = true, + if let Some(geometry) = window_def.geometry { + // Positioning surface + let mut top = false; + let mut left = false; + let mut right = false; + let mut bottom = false; + + match geometry.anchor_point.x { + AnchorAlignment::START => left = true, + AnchorAlignment::CENTER => {} + AnchorAlignment::END => right = true, + } + match geometry.anchor_point.y { + AnchorAlignment::START => top = true, + AnchorAlignment::CENTER => {} + AnchorAlignment::END => bottom = true, + } + + gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Left, left); + gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Right, right); + gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Top, top); + gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Bottom, bottom); + + let xoffset = geometry.offset.x.relative_to(monitor.width); + let yoffset = geometry.offset.y.relative_to(monitor.height); + + if left { + gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Left, xoffset); + } else { + gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Right, xoffset); + } + if bottom { + gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Bottom, yoffset); + } else { + gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Top, yoffset); + } } - match window_def.geometry.anchor_point.y { - AnchorAlignment::START => top = true, - AnchorAlignment::CENTER => {} - AnchorAlignment::END => bottom = true, - } - - gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Left, left); - gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Right, right); - gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Top, top); - gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Bottom, bottom); - - let xoffset = window_def.geometry.offset.x.relative_to(monitor.width); - let yoffset = window_def.geometry.offset.y.relative_to(monitor.height); - - if left { - gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Left, xoffset); - } else { - gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Right, xoffset); - } - if bottom { - gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Bottom, yoffset); - } else { - gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Top, yoffset); - } - - if window_def.exclusive { + if window_def.backend_options.exclusive { gtk_layer_shell::auto_exclusive_zone_enable(&window); } Some(window) @@ -122,19 +105,18 @@ mod platform { }; pub fn initialize_window(window_def: &EwwWindowDefinition, _monitor: gdk::Rectangle) -> Option { - let window = if window_def.focusable { - gtk::Window::new(gtk::WindowType::Toplevel) + let window_type = if window_def.backend_options.wm_ignore { gtk::WindowType::Popup } else { gtk::WindowType::Toplevel }; + let window = gtk::Window::new(window_type); + let wm_class_name = format!("eww-{}", window_def.name); + #[allow(deprecated)] + window.set_wmclass(&wm_class_name, &wm_class_name); + window.set_resizable(window_def.resizable); + window.set_keep_above(window_def.stacking == WindowStacking::Foreground); + window.set_keep_below(window_def.stacking == WindowStacking::Background); + if window_def.backend_options.sticky { + window.stick(); } else { - gtk::Window::new(gtk::WindowType::Popup) - }; - window.set_resizable(true); - if !window_def.focusable { - window.set_type_hint(gdk::WindowTypeHint::Dock); - } - if window_def.stacking == WindowStacking::Foreground { - window.set_keep_above(true); - } else { - window.set_keep_below(true); + window.unstick(); } Some(window) } @@ -172,7 +154,7 @@ mod platform { .ok() .context("Failed to get x11 window for gtk window")? .get_xid() as u32; - let strut_def = window_def.struts; + let strut_def = window_def.backend_options.struts; let root_window_geometry = self.conn.get_geometry(self.root_window)?.reply()?; let mon_end_x = (monitor_rect.x + monitor_rect.width) as u32 - 1u32; @@ -225,11 +207,12 @@ mod platform { win_id, self.atoms._NET_WM_WINDOW_TYPE, self.atoms.ATOM, - &[match window_def.window_type { + &[match window_def.backend_options.window_type { EwwWindowType::Dock => self.atoms._NET_WM_WINDOW_TYPE_DOCK, EwwWindowType::Normal => self.atoms._NET_WM_WINDOW_TYPE_NORMAL, EwwWindowType::Dialog => self.atoms._NET_WM_WINDOW_TYPE_DIALOG, EwwWindowType::Toolbar => self.atoms._NET_WM_WINDOW_TYPE_TOOLBAR, + EwwWindowType::Utility => self.atoms._NET_WM_WINDOW_TYPE_UTILITY, }], )? .check()?; @@ -245,6 +228,7 @@ mod platform { _NET_WM_WINDOW_TYPE_DOCK, _NET_WM_WINDOW_TYPE_DIALOG, _NET_WM_WINDOW_TYPE_TOOLBAR, + _NET_WM_WINDOW_TYPE_UTILITY, _NET_WM_STATE, _NET_WM_STATE_STICKY, _NET_WM_STATE_ABOVE, diff --git a/crates/eww/src/util.rs b/crates/eww/src/util.rs index 2048961..4f684f7 100644 --- a/crates/eww/src/util.rs +++ b/crates/eww/src/util.rs @@ -3,23 +3,6 @@ use extend::ext; use itertools::Itertools; use std::path::Path; -#[macro_export] -macro_rules! impl_try_from { - ($typ:ty { - $( - for $for:ty => |$arg:ident| $code:expr - );*; - }) => { - $(impl TryFrom<$typ> for $for { - type Error = anyhow::Error; - - fn try_from($arg: $typ) -> Result { - $code - } - })* - }; -} - #[macro_export] macro_rules! try_logging_errors { ($context:expr => $code:block) => {{ @@ -62,8 +45,8 @@ macro_rules! loop_select { #[macro_export] macro_rules! enum_parse { ($name:literal, $input:expr, $($($s:literal)|* => $val:expr),* $(,)?) => { - let input = $input; - match input { + let input = $input.to_lowercase(); + match input.as_str() { $( $( $s )|* => Ok($val) ),*, _ => Err(anyhow!(concat!("Couldn't parse ", $name, ": '{}'. Possible values are ", $($($s),*),*), input)) } diff --git a/crates/yuck/src/value/coords.rs b/crates/yuck/src/value/coords.rs index 11728d6..b3b49cb 100644 --- a/crates/yuck/src/value/coords.rs +++ b/crates/yuck/src/value/coords.rs @@ -76,7 +76,7 @@ impl fmt::Debug for Coords { } impl Coords { - pub fn from_pixels(x: i32, y: i32) -> Self { + pub fn from_pixels((x, y): (i32, i32)) -> Self { Coords { x: NumWithUnit::Pixels(x), y: NumWithUnit::Pixels(y) } } diff --git a/docs/src/configuration.md b/docs/src/configuration.md index 8f2261c..dc55bcb 100644 --- a/docs/src/configuration.md +++ b/docs/src/configuration.md @@ -206,7 +206,7 @@ The `` config should look something like this: ```xml - + @@ -220,7 +220,7 @@ For Wayland users the `` block is replaced by the exclusive field in ` The previous `` block would look like this. ```xml - +
@@ -237,9 +237,6 @@ There are a couple things you can optionally configure on the window itself: - `stacking`: stacking describes on what "layer" of the screen the window is shown. Possible values on the X11 backend: `foreground "fg"`, `background "bg"`. Default: `"fg"` Possible values on the Wayland backend: `foreground "fg"`, `bottom "bt"`, `background "bg"`, `overlay "ov"`. Default: `"fg"` -- `focusable`: whether the window should be focusable by the windowmanager. - This is necessary for things like text-input-fields to work properly. - Possible values: `"true"`, `"false"`. Default: `"false"` - `screen`: Specifies on which display to show the window in a multi-monitor setup. This can be any number, representing the index of your monitor. - `exclusive`: Specifies whether or not a surface can be occupied by another. @@ -247,10 +244,34 @@ There are a couple things you can optionally configure on the window itself: The details on how it is actually implemented are left to the compositor. This option is only valid on Wayland. Possible values: `"true"`, `"false"`. Default: `"false"` +- `focusable`: (Wayland only) whether the window should be able to capture keyboard input. + Possible values: `"true"`, `"false"`. Default: `"false"` +- `wm-ignore`: (X11 only) wether the window should be managed by the window manager. + For a centered widget setup this is recommended to be set to true. For a bar, set the windowtype to `dock` instead. + Note that setting `wm-ignore` will make some other options not work, as those rely on the window manager. + Possible values: `"true"`, `"false"`. Default: `"true"` except if `` is set. - `windowtype`: (X11 only) Can be used in determining the decoration, stacking position and other behavior of the window. - Possible values: + Window managers tend to interpret these differently, so play around with which one works for your usecase! + Possible values: - `"normal"`: indicates that this is a normal, top-level window - - `"dock"`: indicates a dock or panel feature + - `"dock"`: indicates a bar, dock, or panel window + - `"utility"`: indicates a pinned utility window - `"toolbar"`: toolbars "torn off" from the main application - `"dialog"`: indicates that this is a dialog window - - Default: `"dock"` if reserve is set, else `"normal"` + - Default: `"dock"` +- `sticky`: (X11 only) If the window should show up on all workspaces. Note that this may not have any effect, depending on your window manager and the window type. + Possible values: `"true"`, `"false"`. Default: `"true"` +- `resizable`: (X11 only) If the window should be resizable. Note that this may not have any effect, depending on your window manager and the window type. + Possible values: `"true"`, `"false"`. Default: `"true"` + + +### Recommendations for different use-cases on X + +Window positioning is... weird on X11. Different window-managers handle things differently, and some things are just not compatible. +Thus, the following setups are recommendations that will _probably_ work. If they don't try to play around with different settings for any of the X11 only properties. + +- For a bar: + - Set `windowtype` to `dock`, and provide a `reserve` configuration to match your window geometry to make the WM reserve space. + - Set `wm-ignore` to `false`. +- For a centered, full-screen widget setup: + - Set `wm-ignore` to `true`. diff --git a/src/config/window_definition.rs b/src/config/window_definition.rs new file mode 100644 index 0000000..e2c85ec --- /dev/null +++ b/src/config/window_definition.rs @@ -0,0 +1,167 @@ +use super::{backend_window_options::*, *}; +use crate::{ensure_xml_tag_is, enum_parse, value::NumWithUnit, widgets::widget_node}; +use derive_more::*; +use serde::{Deserialize, Serialize}; +use smart_default::SmartDefault; +use std::{collections::HashMap, str::FromStr}; + +/// Full window-definition containing the fully expanded widget tree. +/// **Use this** rather than [RawEwwWindowDefinition]. +#[derive(Debug, Clone)] +pub struct EwwWindowDefinition { + pub name: WindowName, + + pub geometry: Option, + pub stacking: WindowStacking, + pub screen_number: Option, + pub widget: Box, + pub resizable: bool, + pub backend_options: BackendWindowOptions, +} + +impl EwwWindowDefinition { + pub fn generate(defs: &HashMap, window: RawEwwWindowDefinition) -> Result { + Ok(EwwWindowDefinition { + name: window.name, + geometry: window.geometry, + stacking: window.stacking, + screen_number: window.screen_number, + resizable: window.resizable, + widget: widget_node::generate_generic_widget_node(defs, &HashMap::new(), window.widget)?, + backend_options: window.backend_options, + }) + } +} + +/// Window-definition storing the raw WidgetUse, as received directly from parsing. +#[derive(Debug, Clone, PartialEq)] +pub struct RawEwwWindowDefinition { + pub name: WindowName, + pub geometry: Option, + pub stacking: WindowStacking, + pub widget: WidgetUse, + pub resizable: bool, + pub backend_options: BackendWindowOptions, + pub screen_number: Option, +} + +impl RawEwwWindowDefinition { + pub fn from_xml_element(xml: &XmlElement) -> Result { + ensure_xml_tag_is!(xml, "window"); + let geometry = match xml.child("geometry") { + Ok(node) => Some(EwwWindowGeometry::from_xml_element(node)?), + Err(_) => None, + }; + + Ok(RawEwwWindowDefinition { + name: WindowName(xml.attr("name")?), + geometry, + widget: WidgetUse::from_xml_node(xml.child("widget")?.only_child()?)?, + stacking: xml.parse_optional_attr("stacking")?.unwrap_or_default(), + // TODO maybe rename this to monitor? + screen_number: xml.parse_optional_attr("screen")?, + resizable: xml.parse_optional_attr("resizable")?.unwrap_or(true), + backend_options: BackendWindowOptions::from_xml_element(xml)?, + }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, SmartDefault)] +pub enum EwwWindowType { + #[default] + Dock, + Dialog, + Toolbar, + Normal, + Utility, +} +impl FromStr for EwwWindowType { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + enum_parse! { "window type", s, + "dock" => Self::Dock, + "toolbar" => Self::Toolbar, + "dialog" => Self::Dialog, + "normal" => Self::Normal, + "utility" => Self::Utility, + } + } +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, smart_default::SmartDefault)] +pub enum Side { + #[default] + Top, + Left, + Right, + Bottom, +} + +impl std::str::FromStr for Side { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + enum_parse! { "side", s, + "l" | "left" => Side::Left, + "r" | "right" => Side::Right, + "t" | "top" => Side::Top, + "b" | "bottom" => Side::Bottom, + } + } +} + +// Surface definition if the backend for X11 is enable +#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] +pub struct StrutDefinition { + pub side: Side, + pub dist: NumWithUnit, +} + +impl StrutDefinition { + pub fn from_xml_element(xml: XmlElement) -> Result { + Ok(StrutDefinition { side: xml.attr("side")?.parse()?, dist: xml.attr("distance")?.parse()? }) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display, SmartDefault)] +pub enum WindowStacking { + #[default] + Foreground, + Background, + Bottom, + Overlay, +} + +impl std::str::FromStr for WindowStacking { + type Err = anyhow::Error; + + #[cfg(not(feature = "wayland"))] + fn from_str(s: &str) -> Result { + enum_parse! { "WindowStacking", s, + "foreground" | "fg" | "f" => WindowStacking::Foreground, + "background" | "bg" | "b" => WindowStacking::Background, + } + } + + #[cfg(feature = "wayland")] + fn from_str(s: &str) -> Result { + enum_parse! { "WindowStacking", s, + "foreground" | "fg" => WindowStacking::Foreground, + "background" | "bg" => WindowStacking::Background, + "bottom" | "bt" => WindowStacking::Bottom, + "overlay" | "ov" => WindowStacking::Overlay, + } + } +} + +#[repr(transparent)] +#[derive(Clone, Hash, PartialEq, Eq, AsRef, FromStr, Display, Serialize, Deserialize, Default, From, DebugCustom)] +#[debug(fmt = "WindowName(\".0\")")] +pub struct WindowName(String); + +impl std::borrow::Borrow for WindowName { + fn borrow(&self) -> &str { + &self.0 + } +} diff --git a/src/value/primitive.rs b/src/value/primitive.rs new file mode 100644 index 0000000..df49978 --- /dev/null +++ b/src/value/primitive.rs @@ -0,0 +1,165 @@ +use anyhow::*; +use itertools::Itertools; +use serde::{Deserialize, Serialize}; +use std::{convert::TryFrom, fmt, iter::FromIterator}; + +#[derive(Clone, Deserialize, Serialize, derive_more::From, Default)] +pub struct PrimVal(pub String); + +impl fmt::Display for PrimVal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} +impl fmt::Debug for PrimVal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "\"{}\"", self.0) + } +} + +/// Manually implement equality, to allow for values in different formats (i.e. "1" and "1.0") to still be considered as equal. +impl std::cmp::PartialEq for PrimVal { + fn eq(&self, other: &Self) -> bool { + if let (Ok(a), Ok(b)) = (self.as_f64(), other.as_f64()) { + a == b + } else { + self.0 == other.0 + } + } +} + +impl FromIterator for PrimVal { + fn from_iter>(iter: T) -> Self { + PrimVal(iter.into_iter().join("")) + } +} + +impl std::str::FromStr for PrimVal { + type Err = anyhow::Error; + + /// parses the value, trying to turn it into a number and a boolean first, + /// before deciding that it is a string. + fn from_str(s: &str) -> Result { + Ok(PrimVal::from_string(s.to_string())) + } +} + +macro_rules! impl_try_from { + (impl From<$typ:ty> { + $(for $for:ty => |$arg:ident| $code:expr);*; + }) => { + $(impl TryFrom<$typ> for $for { + type Error = anyhow::Error; + fn try_from($arg: $typ) -> Result { $code } + })* + }; +} +macro_rules! impl_primval_from { + ($($t:ty),*) => { + $(impl From<$t> for PrimVal { + fn from(x: $t) -> Self { PrimVal(x.to_string()) } + })* + }; +} + +impl_try_from!(impl From { + for String => |x| x.as_string(); + for f64 => |x| x.as_f64(); + for bool => |x| x.as_bool(); + for Vec => |x| x.as_vec(); +}); + +impl_primval_from!(bool, i32, u32, f32, u8, f64, &str); + +impl From<&serde_json::Value> for PrimVal { + fn from(v: &serde_json::Value) -> Self { + PrimVal( + v.as_str() + .map(|x| x.to_string()) + .or_else(|| serde_json::to_string(v).ok()) + .unwrap_or_else(|| "".to_string()), + ) + } +} + +impl PrimVal { + pub fn from_string(s: String) -> Self { + PrimVal(s) + } + + pub fn into_inner(self) -> String { + self.0 + } + + /// This will never fail + pub fn as_string(&self) -> Result { + Ok(self.0.to_owned()) + } + + pub fn as_f64(&self) -> Result { + self.0.parse().map_err(|e| anyhow!("couldn't convert {:?} to f64: {}", &self, e)) + } + + pub fn as_i32(&self) -> Result { + self.0.parse().map_err(|e| anyhow!("couldn't convert {:?} to i32: {}", &self, e)) + } + + pub fn as_bool(&self) -> Result { + self.0.parse().map_err(|e| anyhow!("couldn't convert {:?} to bool: {}", &self, e)) + } + + pub fn as_vec(&self) -> Result> { + parse_vec(self.0.to_owned()).map_err(|e| anyhow!("Couldn't convert {:#?} to a vec: {}", &self, e)) + } + + pub fn as_json_value(&self) -> Result { + serde_json::from_str::(&self.0) + .with_context(|| format!("Couldn't convert {:#?} to a json object", &self)) + } +} + +fn parse_vec(a: String) -> Result> { + match a.strip_prefix('[').and_then(|x| x.strip_suffix(']')) { + Some(content) => { + let mut items: Vec = content.split(',').map(|x: &str| x.to_string()).collect(); + let mut removed = 0; + for times_ran in 0..items.len() { + // escapes `,` if there's a `\` before em + if items[times_ran - removed].ends_with('\\') { + items[times_ran - removed].pop(); + let it = items.remove((times_ran + 1) - removed); + items[times_ran - removed] += ","; + items[times_ran - removed] += ⁢ + removed += 1; + } + } + Ok(items) + } + None => Err(anyhow!("Is your array built like this: '[these,are,items]'?")), + } +} + +#[cfg(test)] +mod test { + use super::*; + use pretty_assertions::assert_eq; + #[test] + fn test_parse_vec() { + assert_eq!(vec![""], parse_vec("[]".to_string()).unwrap(), "should be able to parse empty lists"); + assert_eq!(vec!["hi"], parse_vec("[hi]".to_string()).unwrap(), "should be able to parse single element list"); + assert_eq!( + vec!["hi", "ho", "hu"], + parse_vec("[hi,ho,hu]".to_string()).unwrap(), + "should be able to parse three element list" + ); + assert_eq!(vec!["hi,ho"], parse_vec("[hi\\,ho]".to_string()).unwrap(), "should be able to parse list with escaped comma"); + assert_eq!( + vec!["hi,ho", "hu"], + parse_vec("[hi\\,ho,hu]".to_string()).unwrap(), + "should be able to parse two element list with escaped comma" + ); + assert!(parse_vec("".to_string()).is_err(), "Should fail when parsing empty string"); + assert!(parse_vec("[a,b".to_string()).is_err(), "Should fail when parsing unclosed list"); + assert!(parse_vec("a]".to_string()).is_err(), "Should fail when parsing unopened list"); + } +}