diff --git a/Cargo.lock b/Cargo.lock index 23ed517..e921daa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,33 @@ 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 = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[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" @@ -41,6 +68,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + [[package]] name = "bitflags" version = "1.3.2" @@ -53,6 +86,12 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + [[package]] name = "bytemuck" version = "1.14.0" @@ -80,6 +119,19 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets", +] + [[package]] name = "color-eyre" version = "0.6.2" @@ -152,6 +204,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", + "strsim", "syn", ] @@ -171,8 +224,25 @@ name = "deckster" version = "0.1.0" dependencies = [ "color-eyre", + "humantime-serde", "loupedeck_serial", + "piet", "rgb", + "serde", + "serde_regex", + "serde_with", + "thiserror", + "toml", +] + +[[package]] +name = "deranged" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +dependencies = [ + "powerfmt", + "serde", ] [[package]] @@ -216,6 +286,12 @@ dependencies = [ "syn", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "eyre" version = "0.6.11" @@ -238,6 +314,63 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime", + "serde", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +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 = "ident_case" version = "1.0.1" @@ -250,6 +383,28 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + [[package]] name = "io-kit-sys" version = "0.4.0" @@ -260,6 +415,30 @@ dependencies = [ "mach2", ] +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kurbo" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b" +dependencies = [ + "arrayvec", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -292,6 +471,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + [[package]] name = "loupedeck_serial" version = "0.1.0" @@ -314,6 +499,12 @@ dependencies = [ "libc", ] +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + [[package]] name = "memchr" version = "2.6.4" @@ -340,6 +531,15 @@ dependencies = [ "libc", ] +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.32.2" @@ -361,6 +561,16 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +[[package]] +name = "piet" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e381186490a3e2017a506d62b759ea8eaf4be14666b13ed53973e8ae193451b1" +dependencies = [ + "kurbo", + "unic-bidi", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -373,6 +583,12 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "proc-macro2" version = "1.0.71" @@ -435,12 +651,97 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_regex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf" +dependencies = [ + "regex", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_with" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.1.0", + "serde", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "serialport" version = "4.3.0" @@ -469,6 +770,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "syn" version = "2.0.43" @@ -510,6 +817,69 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +dependencies = [ + "deranged", + "itoa", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +dependencies = [ + "time-core", +] + +[[package]] +name = "toml" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap 2.1.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tracing" version = "0.1.40" @@ -560,6 +930,57 @@ dependencies = [ "thiserror", ] +[[package]] +name = "unic-bidi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1356b759fb6a82050666f11dce4b6fe3571781f1449f3ef78074e408d468ec09" +dependencies = [ + "matches", + "unic-ucd-bidi", +] + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-bidi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1d568b51222484e1f8209ce48caa6b430bf352962b877d592c29ab31fb53d8c" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -572,6 +993,60 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + [[package]] name = "winapi" version = "0.3.9" @@ -593,3 +1068,78 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +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", +] + +[[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_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[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_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[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_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winnow" +version = "0.5.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c" +dependencies = [ + "memchr", +] diff --git a/deckster/Cargo.toml b/deckster/Cargo.toml index 2251420..f21100f 100644 --- a/deckster/Cargo.toml +++ b/deckster/Cargo.toml @@ -4,6 +4,13 @@ version = "0.1.0" edition = "2021" [dependencies] -loupedeck_serial = { path = "../loupedeck_serial" } color-eyre = "0.6.2" -rgb = "0.8.37" \ No newline at end of file +humantime-serde = "1.1.1" +loupedeck_serial = { path = "../loupedeck_serial" } +piet = "0.6.2" +rgb = "0.8.37" +serde = { version = "1.0.193", features = ["derive"] } +serde_regex = "1.1.0" +serde_with = "3.4.0" +thiserror = "1.0.52" +toml = "0.8.8" \ No newline at end of file diff --git a/deckster/examples/full/deckster.toml b/deckster/examples/full/deckster.toml index b0734ec..8beee1c 100644 --- a/deckster/examples/full/deckster.toml +++ b/deckster/examples/full/deckster.toml @@ -1,5 +1,6 @@ key_pages = ["./key-pages/*"] -button_pages = ["./button-page.toml"] +button_pages = ["./key-pages/*"] +knob_pages = ["./button-page.toml"] [initial] key_page = "default" diff --git a/deckster/examples/full/knob-pages/default.toml b/deckster/examples/full/knob-pages/default.toml index b0a50b1..b93397f 100644 --- a/deckster/examples/full/knob-pages/default.toml +++ b/deckster/examples/full/knob-pages/default.toml @@ -3,7 +3,7 @@ icon = "@ph/microphone-light" mode.audio_volume.mode = "input" mode.audio_volume.regex = "Microphone" mode.audio_volume.label.muted = "Muted" -mode.audio_volume.icon.inactive = "@ph/microphone-sllash-light[color=red,opacity=90]" +mode.audio_volume.icon.inactive = "@ph/microphone-sllash-light[brighten=50,huerotate=red,opacity=90]" mode.audio_volume.circle_indicator.color = "#ffffff" mode.audio_volume.circle_indicator.width = 2 mode.audio_volume.circle_indicator.radius = 40 diff --git a/deckster/src/main.rs b/deckster/src/main.rs index 2c0e905..87271c8 100644 --- a/deckster/src/main.rs +++ b/deckster/src/main.rs @@ -1,3 +1,5 @@ +mod model; + use std::thread::sleep; use std::time::{Duration, Instant}; use color_eyre::eyre::ContextCompat; diff --git a/deckster/src/model/deckster_file.rs b/deckster/src/model/deckster_file.rs new file mode 100644 index 0000000..18090f0 --- /dev/null +++ b/deckster/src/model/deckster_file.rs @@ -0,0 +1,25 @@ +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +pub struct DecksterFile { + key_pages: Vec, + button_pages: Vec, + knob_pages: Vec, + + initial: DecksterFileInitial, + icon_packs: Vec +} + +#[derive(Debug, Deserialize)] +pub struct DecksterFileInitial { + key_page: String, + button_page: String, + knob_page: String +} + +#[derive(Debug, Deserialize)] +pub struct DecksterFileIconPack { + id: String, + path: String, + global_filter: String +} \ No newline at end of file diff --git a/deckster/src/model/image_filter.rs b/deckster/src/model/image_filter.rs new file mode 100644 index 0000000..2e81ae7 --- /dev/null +++ b/deckster/src/model/image_filter.rs @@ -0,0 +1,138 @@ +use piet::kurbo::Rect; +use serde_with::DeserializeFromStr; +use std::fmt::{Display, Formatter, Write}; +use std::str::FromStr; +use thiserror::Error; + +#[derive(Debug, Clone)] +pub enum ImageTransform { + Invert, + Brighten(f32), + Crop(Rect), + HorizontalFlip, + VerticalFlip, + Rotate { quarter_clockwise_turns: u8 }, +} + +impl Display for ImageTransform { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + ImageTransform::Invert => f.write_str("invert"), + ImageTransform::Brighten(value) => f.write_fmt(format_args!("brighten={}", value)), + ImageTransform::Crop(_) => f.write_str("crop=<...>"), + ImageTransform::HorizontalFlip => f.write_str("horizontal_flip"), + ImageTransform::VerticalFlip => f.write_str("vertical_flip"), + ImageTransform::Rotate { quarter_clockwise_turns } => f.write_fmt(format_args!("rotate={}", quarter_clockwise_turns)), + }?; + + Ok(()) + } +} + +#[derive(DeserializeFromStr)] +pub struct ImageFilter { + scale: f32, + alpha: f32, + blur: f32, + transforms: Vec, +} + +#[derive(Debug, Error)] +pub enum ImageFilterFromStringError { + #[error("Unknown filter: {name}")] + UnknownFilter { name: String }, + + #[error("Filter {name} can only be used once.")] + SingletonSpecifiedMoreThanOnce { name: String }, + + #[error("All singleton filters must be placed before any transforms, but {singleton} was placed after {transform}.")] + SingletonSpecifiedAfterTransform { singleton: String, transform: ImageTransform }, + + #[error("{quarter_clockwise_turns} 90° turns can be simplified.")] + PointlessRotation { quarter_clockwise_turns: u8 }, + + #[error("Filter {filter_name} requires a value ({filter_name}=).")] + FilterValueMissing { filter_name: String }, + + #[error("The following value supplied to {filter_name} could not be parsed: {raw_value}")] + FilterValueNotParsable { filter_name: String, raw_value: String }, + + #[error("{value} is not in the range ({range}) required by {filter_name}.")] + FilterValueNotInRange { filter_name: String, value: String, range: String }, +} + +impl FromStr for ImageFilter { + type Err = ImageFilterFromStringError; + + fn from_str(s: &str) -> Result { + let filters: Vec<&str> = s.split('|').map(|f| f.trim()).collect(); + + let scale: Option = None; + let alpha: Option = None; + let blur: Option = None; + + let mut transforms: Vec = Vec::new(); + + for filter in filters { + let split_filter = filter.split_once('='); + let (filter_name, optional_raw_value) = if let Some((filter_name, raw_value)) = split_filter { + (filter_name.to_owned(), Some(raw_value.to_owned())) + } else { + (filter.to_owned(), None) + }; + + let use_raw_value = || -> Result { + optional_raw_value.ok_or_else(|| ImageFilterFromStringError::FilterValueMissing { + filter_name: filter_name.clone(), + }) + }; + + match filter_name.as_str() { + "scale" | "alpha" | "blur" => { + if let Some(transform) = transforms.first() { + return Err(ImageFilterFromStringError::SingletonSpecifiedAfterTransform { + singleton: filter_name.clone(), + transform: transform.clone(), + }); + } + + #[allow(clippy::type_complexity)] + let (mut variable, check_range, range_string): (Option, Box bool>, &'static str) = match filter_name.as_str() { + "scale" => (scale, Box::new(|v| (0.0..=10.0_f32).contains(v)), "0..=10"), + "alpha" => (alpha, Box::new(|v| (0.0..1.0_f32).contains(v)), "0..<1"), + "blur" => (blur, Box::new(|v| (0.0..=10.0_f32).contains(v)), "0..=10"), + _ => unreachable!("filter_name is narrowed down in the outer match statement"), + }; + + if variable.is_some() { + return Err(ImageFilterFromStringError::SingletonSpecifiedMoreThanOnce { name: filter_name.clone() }); + } + + let raw_value = use_raw_value()?; + let value = f32::from_str(&raw_value).map_err(|_| ImageFilterFromStringError::FilterValueNotParsable { + filter_name: filter_name.clone(), + raw_value, + })?; + + if !check_range(&value) { + return Err(ImageFilterFromStringError::FilterValueNotInRange { + filter_name: filter_name.clone(), + range: range_string.to_owned(), + value: value.to_string(), + }); + } + + let _ = variable.insert(value); + } + _ => return Err(ImageFilterFromStringError::UnknownFilter { name: filter_name }), + } + } + + Ok(ImageFilter { + scale: scale.unwrap_or(1.0), + alpha: alpha.unwrap_or(1.0), + blur: blur.unwrap_or(0.0), + transforms, + }) + } +} diff --git a/deckster/src/model/mod.rs b/deckster/src/model/mod.rs new file mode 100644 index 0000000..32c0c98 --- /dev/null +++ b/deckster/src/model/mod.rs @@ -0,0 +1,2 @@ +mod deckster_file; +mod image_filter; \ No newline at end of file diff --git a/loupedeck_serial/src/device.rs b/loupedeck_serial/src/device.rs index 1eb5637..6db8119 100644 --- a/loupedeck_serial/src/device.rs +++ b/loupedeck_serial/src/device.rs @@ -1,17 +1,17 @@ -use std::{io, thread}; -use std::io::{Read, Write}; -use std::sync::mpsc; -use std::thread::{sleep, spawn, Thread}; -use std::time::Duration; -use bytes::Bytes; -use rgb::{ComponentSlice, RGB8}; -use serialport::{ClearBuffer, DataBits, FlowControl, Parity, SerialPortType, StopBits}; -use thiserror::Error; -use crate::characteristics::{CHARACTERISTICS, LoupedeckButton, LoupedeckDeviceCharacteristics, LoupedeckDeviceDisplayConfiguration}; +use crate::characteristics::{LoupedeckButton, LoupedeckDeviceCharacteristics, LoupedeckDeviceDisplayConfiguration, CHARACTERISTICS}; use crate::commands::{LoupedeckCommand, VibrationPattern}; use crate::events::{LoupedeckEvent, LoupedeckInternalEvent}; use crate::messages::{read_messages_worker, write_messages_worker, WS_UPGRADE_REQUEST, WS_UPGRADE_RESPONSE_START}; use crate::util::convert_rgb888_to_rgb565; +use bytes::Bytes; +use rgb::{ComponentSlice, RGB8}; +use serialport::{ClearBuffer, DataBits, FlowControl, Parity, SerialPortType, StopBits}; +use std::io::{Read, Write}; +use std::sync::mpsc; +use std::thread::sleep; +use std::time::Duration; +use std::{io, thread}; +use thiserror::Error; #[derive(Debug)] pub struct AvailableLoupedeckDevice { @@ -72,7 +72,7 @@ pub enum SetButtonColorError { UnknownButton, #[error("The button does not allow setting a color.")] - ColorNotSupported + ColorNotSupported, } #[derive(Debug, Error)] @@ -84,10 +84,7 @@ pub enum ReplaceFramebufferAreaError { OutOfBounds, #[error("Given the specified dimensions, the buffer size must be {expected} but it was {actual}.")] - WrongBufferSize { - expected: usize, - actual: usize, - }, + WrongBufferSize { expected: usize, actual: usize }, } impl LoupedeckDevice { @@ -139,10 +136,10 @@ impl LoupedeckDevice { y: u16, width: u16, height: u16, - buffer: &[RGB8] + buffer: &[RGB8], ) -> Result<(), ReplaceFramebufferAreaError> { - if !std::ptr::eq(display, &self.characteristics.key_grid_display) && - !self.characteristics.additional_displays.iter().any(|d| std::ptr::eq(display, d)) { + if !std::ptr::eq(display, &self.characteristics.key_grid_display) && !self.characteristics.additional_displays.iter().any(|d| std::ptr::eq(display, d)) + { return Err(ReplaceFramebufferAreaError::UnknownDisplay); } @@ -171,55 +168,55 @@ impl LoupedeckDevice { let local_x = display.local_offset_x + x; let local_y = display.local_offset_y + y; - self.commands_sender.send(LoupedeckCommand::ReplaceFramebufferArea { - display_id: display.id, - x: local_x, - y: local_y, - width, - height, - buffer: converted_buffer.freeze(), - }).unwrap(); + self.commands_sender + .send(LoupedeckCommand::ReplaceFramebufferArea { + display_id: display.id, + x: local_x, + y: local_y, + width, + height, + buffer: converted_buffer.freeze(), + }) + .unwrap(); Ok(()) } - pub fn refresh_display(&self, display: &LoupedeckDeviceDisplayConfiguration) -> Result<(), RefreshDisplayError>{ - if !std::ptr::eq(display, &self.characteristics.key_grid_display) && - !self.characteristics.additional_displays.iter().any(|d| std::ptr::eq(display, d)) { + pub fn refresh_display(&self, display: &LoupedeckDeviceDisplayConfiguration) -> Result<(), RefreshDisplayError> { + if !std::ptr::eq(display, &self.characteristics.key_grid_display) && !self.characteristics.additional_displays.iter().any(|d| std::ptr::eq(display, d)) + { return Err(RefreshDisplayError::UnknownDisplay); } - self.commands_sender.send(LoupedeckCommand::RefreshDisplay { - display_id: display.id - }).unwrap(); + self.commands_sender.send(LoupedeckCommand::RefreshDisplay { display_id: display.id }).unwrap(); Ok(()) } pub fn vibrate(&self, pattern: VibrationPattern) { - self.commands_sender.send(LoupedeckCommand::Vibrate { - pattern - }).unwrap(); + self.commands_sender.send(LoupedeckCommand::Vibrate { pattern }).unwrap(); } pub fn discover() -> Result, serialport::Error> { let ports = serialport::available_ports()?; - Ok(ports.iter().filter_map(|port| { - if let SerialPortType::UsbPort(info) = &port.port_type { - let characteristics = CHARACTERISTICS.iter() - .find(|c| c.vendor_id == info.vid && c.product_id == info.pid); + Ok(ports + .iter() + .filter_map(|port| { + if let SerialPortType::UsbPort(info) = &port.port_type { + let characteristics = CHARACTERISTICS.iter().find(|c| c.vendor_id == info.vid && c.product_id == info.pid); - if let Some(characteristics) = characteristics { - return Some(AvailableLoupedeckDevice { - port_name: port.port_name.clone(), - characteristics, - }); + if let Some(characteristics) = characteristics { + return Some(AvailableLoupedeckDevice { + port_name: port.port_name.clone(), + characteristics, + }); + } } - } - None - }).collect::>()) + None + }) + .collect::>()) } pub(crate) fn connect(AvailableLoupedeckDevice { port_name, characteristics }: &AvailableLoupedeckDevice) -> Result { @@ -262,13 +259,13 @@ impl LoupedeckDevice { commands_sender.send(LoupedeckCommand::RequestSerialNumber).unwrap(); let serial_number = match internal_events_receiver.recv_timeout(Duration::from_secs(1)) { Ok(LoupedeckInternalEvent::GetSerialNumberResponse { serial_number }) => Ok(serial_number), - _ => Err(ConnectError::WrongLateHandshakeResponse) + _ => Err(ConnectError::WrongLateHandshakeResponse), }?; commands_sender.send(LoupedeckCommand::RequestFirmwareVersion).unwrap(); let firmware_version = match internal_events_receiver.recv_timeout(Duration::from_secs(1)) { Ok(LoupedeckInternalEvent::GetFirmwareVersionResponse { firmware_version }) => Ok(firmware_version), - _ => Err(ConnectError::WrongLateHandshakeResponse) + _ => Err(ConnectError::WrongLateHandshakeResponse), }?; drop(internal_events_receiver); diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..1cbd329 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +max_width = 160 \ No newline at end of file