aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock679
-rw-r--r--Cargo.toml3
-rw-r--r--alsa/Cargo.toml18
-rw-r--r--alsa/src/communication.rs68
-rw-r--r--alsa/src/communication/pipe_chan.rs172
-rw-r--r--alsa/src/config.rs104
-rw-r--r--alsa/src/lib.rs37
-rw-r--r--alsa/src/runnable.rs397
-rw-r--r--formatable-float/Cargo.toml9
-rw-r--r--formatable-float/src/lib.rs154
-rw-r--r--pulse/Cargo.toml1
-rw-r--r--pulse/src/config.rs175
-rw-r--r--pulse/src/runnable/mod.rs1
-rw-r--r--swaystatus/Cargo.toml2
-rw-r--r--swaystatus/src/commandline/mod.rs14
-rw-r--r--testconfig24
17 files changed, 1466 insertions, 393 deletions
diff --git a/.gitignore b/.gitignore
index b0c2531..83fa555 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
/target
*~
*.swp
+*/target
diff --git a/Cargo.lock b/Cargo.lock
index 03e4f00..766e28b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,53 +4,45 @@ version = 3
[[package]]
name = "aho-corasick"
-version = "0.7.15"
+version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
-name = "arrayref"
-version = "0.3.6"
+name = "android-tzdata"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
-name = "arrayvec"
-version = "0.5.2"
+name = "android_system_properties"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
[[package]]
name = "autocfg"
-version = "1.0.1"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
-
-[[package]]
-name = "base64"
-version = "0.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
-version = "1.2.1"
+version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
-name = "blake2b_simd"
-version = "0.5.11"
+name = "bitflags"
+version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
-dependencies = [
- "arrayref",
- "arrayvec",
- "constant_time_eq",
-]
+checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "block"
@@ -59,10 +51,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
+name = "bumpalo"
+version = "3.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
+
+[[package]]
name = "cc"
-version = "1.0.67"
+version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "libc",
+]
[[package]]
name = "cfg-if"
@@ -72,62 +73,67 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
-version = "0.4.19"
+version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
+checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb"
dependencies = [
- "libc",
- "num-integer",
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
"num-traits",
- "time",
- "winapi",
+ "wasm-bindgen",
+ "windows-targets 0.52.0",
]
[[package]]
name = "clap"
-version = "3.0.0-beta.5"
+version = "3.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "feff3878564edb93745d58cf63e17b63f24142506e7a20c87a5521ed7bfb1d63"
+checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
+ "clap_lex",
"indexmap",
- "lazy_static",
- "os_str_bytes",
+ "once_cell",
"terminal_size",
"textwrap",
]
[[package]]
-name = "constant_time_eq"
-version = "0.1.5"
+name = "clap_lex"
+version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
+checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
+dependencies = [
+ "os_str_bytes",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "crossbeam-utils"
-version = "0.8.3"
+version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
-dependencies = [
- "autocfg",
- "cfg-if",
- "lazy_static",
-]
+checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]]
name = "dirs"
-version = "3.0.1"
+version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff"
+checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
-version = "0.3.5"
+version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a"
+checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
dependencies = [
"libc",
"redox_users",
@@ -136,33 +142,40 @@ dependencies = [
[[package]]
name = "erased-serde"
-version = "0.3.13"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0465971a8cc1fa2455c8465aaa377131e1f1cf4983280f474a13e68793aa770c"
+checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c"
dependencies = [
"serde",
]
[[package]]
-name = "getrandom"
-version = "0.1.16"
+name = "errno"
+version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
- "cfg-if",
"libc",
- "wasi 0.9.0+wasi-snapshot-preview1",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "formatable-float"
+version = "0.1.0"
+dependencies = [
+ "erased-serde",
+ "serde",
]
[[package]]
name = "getrandom"
-version = "0.2.2"
+version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
+checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
dependencies = [
"cfg-if",
"libc",
- "wasi 0.10.2+wasi-snapshot-preview1",
+ "wasi",
]
[[package]]
@@ -177,31 +190,80 @@ dependencies = [
[[package]]
name = "gettext-sys"
-version = "0.21.0"
+version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "885d118016f633f99f741afe6c1433c040813a3cbc755cbfdf85f963e02fad80"
+checksum = "c63ce2e00f56a206778276704bbe38564c8695249fdc8f354b4ef71c57c3839d"
dependencies = [
"cc",
- "tempfile",
+ "temp-dir",
]
[[package]]
name = "hashbrown"
-version = "0.9.1"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3"
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
+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 = "1.6.2"
+version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
+name = "io-lifetimes"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[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"
@@ -209,21 +271,38 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.93"
+version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
+checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "libloading"
-version = "0.7.0"
+version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a"
+checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
+name = "libredox"
+version = "0.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
+dependencies = [
+ "bitflags 2.4.2",
+ "libc",
+ "redox_syscall",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
+
+[[package]]
name = "locale_config"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -237,6 +316,12 @@ dependencies = [
]
[[package]]
+name = "log"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+
+[[package]]
name = "malloc_buf"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -247,25 +332,15 @@ dependencies = [
[[package]]
name = "memchr"
-version = "2.4.1"
+version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
-
-[[package]]
-name = "num-integer"
-version = "0.1.44"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
-dependencies = [
- "autocfg",
- "num-traits",
-]
+checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "num-traits"
-version = "0.2.14"
+version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
dependencies = [
"autocfg",
]
@@ -300,141 +375,83 @@ dependencies = [
]
[[package]]
-name = "os_str_bytes"
-version = "4.2.0"
+name = "once_cell"
+version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "addaa943333a514159c80c97ff4a93306530d965d27e139188283cd13e06a799"
-dependencies = [
- "memchr",
-]
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
-name = "ppv-lite86"
-version = "0.2.10"
+name = "os_str_bytes"
+version = "6.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
+checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1"
[[package]]
name = "proc-macro2"
-version = "1.0.26"
+version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
+checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
- "unicode-xid",
+ "unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.9"
+version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
-name = "rand"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
-dependencies = [
- "libc",
- "rand_chacha",
- "rand_core",
- "rand_hc",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
-dependencies = [
- "ppv-lite86",
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
-dependencies = [
- "getrandom 0.2.2",
-]
-
-[[package]]
-name = "rand_hc"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
-dependencies = [
- "rand_core",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.1.57"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
-
-[[package]]
name = "redox_syscall"
-version = "0.2.6"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
]
[[package]]
name = "redox_users"
-version = "0.3.5"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
+checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"
dependencies = [
- "getrandom 0.1.16",
- "redox_syscall 0.1.57",
- "rust-argon2",
+ "getrandom",
+ "libredox",
+ "thiserror",
]
[[package]]
name = "regex"
-version = "1.4.5"
+version = "1.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19"
+checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
dependencies = [
"aho-corasick",
"memchr",
+ "regex-automata",
"regex-syntax",
]
[[package]]
-name = "regex-syntax"
-version = "0.6.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
-
-[[package]]
-name = "remove_dir_all"
-version = "0.5.3"
+name = "regex-automata"
+version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
dependencies = [
- "winapi",
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
]
[[package]]
-name = "rust-argon2"
-version = "0.8.3"
+name = "regex-syntax"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
-dependencies = [
- "base64",
- "blake2b_simd",
- "constant_time_eq",
- "crossbeam-utils",
-]
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "rustc_version"
@@ -446,25 +463,39 @@ dependencies = [
]
[[package]]
+name = "rustix"
+version = "0.37.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2"
+dependencies = [
+ "bitflags 1.3.2",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
name = "semver"
-version = "1.0.4"
+version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
+checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
[[package]]
name = "serde"
-version = "1.0.125"
+version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
+checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.125"
+version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
+checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
dependencies = [
"proc-macro2",
"quote",
@@ -473,9 +504,9 @@ dependencies = [
[[package]]
name = "signal-hook"
-version = "0.3.8"
+version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef33d6d0cd06e0840fba9985aab098c147e67e05cee14d412d3345ed14ff30ac"
+checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
dependencies = [
"libc",
"signal-hook-registry",
@@ -483,9 +514,9 @@ dependencies = [
[[package]]
name = "signal-hook-registry"
-version = "1.3.0"
+version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
dependencies = [
"libc",
]
@@ -508,6 +539,18 @@ dependencies = [
]
[[package]]
+name = "swaystatus-alsa"
+version = "0.1.0"
+dependencies = [
+ "erased-serde",
+ "errno",
+ "formatable-float",
+ "libc",
+ "serde",
+ "swaystatus-plugin",
+]
+
+[[package]]
name = "swaystatus-clock"
version = "0.1.0"
dependencies = [
@@ -530,90 +573,141 @@ name = "swaystatus-pulse"
version = "0.1.0"
dependencies = [
"erased-serde",
+ "formatable-float",
"serde",
"swaystatus-plugin",
]
[[package]]
name = "syn"
-version = "1.0.69"
+version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb"
+checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [
"proc-macro2",
"quote",
- "unicode-xid",
+ "unicode-ident",
]
[[package]]
-name = "tempfile"
-version = "3.2.0"
+name = "temp-dir"
+version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
-dependencies = [
- "cfg-if",
- "libc",
- "rand",
- "redox_syscall 0.2.6",
- "remove_dir_all",
- "winapi",
-]
+checksum = "dd16aa9ffe15fe021c6ee3766772132c6e98dfa395a167e16864f61a9cfb71d6"
[[package]]
name = "terminal_size"
-version = "0.1.16"
+version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86ca8ced750734db02076f44132d802af0b33b09942331f4459dde8636fd2406"
+checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
dependencies = [
- "libc",
- "winapi",
+ "rustix",
+ "windows-sys 0.48.0",
]
[[package]]
name = "textwrap"
-version = "0.14.2"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
+checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
dependencies = [
"terminal_size",
]
[[package]]
-name = "time"
-version = "0.1.43"
+name = "thiserror"
+version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
+checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
dependencies = [
- "libc",
- "winapi",
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
name = "toml"
-version = "0.5.8"
+version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [
"serde",
]
[[package]]
-name = "unicode-xid"
-version = "0.2.1"
+name = "unicode-ident"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "wasi"
-version = "0.9.0+wasi-snapshot-preview1"
+version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
-name = "wasi"
-version = "0.10.2+wasi-snapshot-preview1"
+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",
+ "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",
+ "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 = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
[[package]]
name = "winapi"
@@ -636,3 +730,144 @@ 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.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 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]]
+name = "windows-targets"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
+dependencies = [
+ "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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
diff --git a/Cargo.toml b/Cargo.toml
index a450197..e59390a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,6 +3,9 @@
members = [
"swaystatus",
"swaystatus-plugin",
+ "formatable-float",
+ "alsa",
"clock",
"pulse"
]
+resolver = "2"
diff --git a/alsa/Cargo.toml b/alsa/Cargo.toml
new file mode 100644
index 0000000..d70a07e
--- /dev/null
+++ b/alsa/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "swaystatus-alsa"
+version = "0.1.0"
+authors = ["Andreas Grois <andi@grois.info>"]
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+swaystatus-plugin = { path = '../swaystatus-plugin', version = '*'}
+formatable-float = { path = '../formatable-float', version = '*'}
+serde = { version = "1.0", features = ["derive"] }
+erased-serde = "0.3"
+libc = "0.2.152"
+errno = "0.3.8"
+
+[lib]
+crate-type = ["cdylib"]
diff --git a/alsa/src/communication.rs b/alsa/src/communication.rs
new file mode 100644
index 0000000..3caf4cf
--- /dev/null
+++ b/alsa/src/communication.rs
@@ -0,0 +1,68 @@
+use swaystatus_plugin::*;
+
+use self::pipe_chan::{Sender, Receiver, SendError, ReceiveError, create_pipe_chan};
+
+pub(crate) mod pipe_chan;
+
+#[repr(C)]
+pub enum MessagesFromMain {
+ Quit,
+ Refresh
+}
+
+pub(crate) struct MessagesFromMainReceiver{
+ receiver : Receiver
+}
+
+pub(crate) struct MessagesFromMainSender{
+ sender: Sender
+}
+
+pub(crate) fn make_sender_for_main() -> Result<(MessagesFromMainSender, MessagesFromMainReceiver),()>{
+ create_pipe_chan().map(|(sender,receiver)| (MessagesFromMainSender{sender}, MessagesFromMainReceiver{receiver}))
+}
+
+impl MessagesFromMainSender {
+ pub(crate) fn send(&self, message : MessagesFromMain) -> Result<(), SendError>{
+ self.sender.send_byte(match message {
+ MessagesFromMain::Quit => 0,
+ MessagesFromMain::Refresh => 1,
+ })
+ }
+}
+
+impl MessagesFromMainReceiver {
+ pub(crate) fn receive(&self) -> Result<Option<MessagesFromMain>, ReceiveError> {
+ self.receiver.read_byte().map(|o| o.map(|b| match b {
+ 0 => MessagesFromMain::Quit,
+ 1 => MessagesFromMain::Refresh,
+ _ => unreachable!()
+ }))
+ }
+ pub(crate) fn file_handle(&self) -> &pipe_chan::FileHandle{
+ self.receiver.file_handle()
+ }
+}
+
+pub struct SenderForMain {
+ sender : MessagesFromMainSender,
+}
+
+impl<'p> SenderForMain {
+ pub fn new(sender : MessagesFromMainSender) -> Self {
+ Self { sender }
+ }
+
+ fn send(&self, message : MessagesFromMain) -> Result<(), PluginCommunicationError> {
+ self.sender.send(message).map_err(|_| PluginCommunicationError)
+ }
+}
+
+impl MsgMainToModule for SenderForMain {
+ fn send_quit(&self) -> Result<(),PluginCommunicationError> {
+ self.send(MessagesFromMain::Quit)
+ }
+ fn send_refresh(&self) -> Result<(),PluginCommunicationError> {
+ self.send(MessagesFromMain::Refresh)
+ }
+}
diff --git a/alsa/src/communication/pipe_chan.rs b/alsa/src/communication/pipe_chan.rs
new file mode 100644
index 0000000..5f0f9ec
--- /dev/null
+++ b/alsa/src/communication/pipe_chan.rs
@@ -0,0 +1,172 @@
+use std::{ffi::{c_int, c_void}, fmt::Display, error::Error};
+use libc::{read, close, pipe2, O_NONBLOCK, EINTR, EAGAIN, EWOULDBLOCK, EPIPE };
+use errno::{errno, set_errno, Errno}; //Why isn't this in libc?!?
+
+/// Sends byte data to the corresponding receiver.
+pub(crate) struct Sender {
+ handle : FileHandle,
+}
+/// Receives byte data from the corresponding sender.
+pub(crate) struct Receiver {
+ handle : FileHandle,
+}
+
+impl Receiver {
+ pub(crate) fn read_byte(&self) -> Result<Option<u8>, ReceiveError> {
+ set_errno(Errno(0));
+ let mut buf : u8 = 0;
+ let status = unsafe {read(self.handle.get_raw(),&mut buf as *mut u8 as *mut c_void, 1)};
+ let e = errno();
+ if status > 0 {
+ Ok(Some(buf))
+ } else if status == 0 && e == Errno(0) {
+ Err(ReceiveError::SenderHasHungUp)
+ } else if e.0 == EINTR {
+ self.read_byte() //got interrupted by a signal, try again.
+ } else if e.0 == EAGAIN || e.0 == EWOULDBLOCK {
+ Ok(None) //nothing to receive
+ } else {
+ Err(ReceiveError::UnknownError)
+ }
+ }
+ pub(crate) fn file_handle(&self) -> &FileHandle {
+ &self.handle
+ }
+}
+
+impl Sender {
+ pub(crate) fn send_byte(&self, byte : u8) -> Result<(), SendError> {
+ set_errno(Errno(0));
+ let status = unsafe {libc::write(self.handle.get_raw(), &byte as *const u8 as *const c_void, 1)};
+ let e = errno();
+ if status > 0 {
+ Ok(())
+ } else if e.0 == EINTR {
+ self.send_byte(byte) //interrupted, retry
+ } else if e.0 == EPIPE {
+ Err(SendError::ReceiverHasHungUp)
+ } else if e.0 == EAGAIN {
+ Err(SendError::ChannelFullWouldBlock)
+ } else {
+ Err(SendError::UnknownError)
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) enum SendError{
+ ReceiverHasHungUp,
+ ChannelFullWouldBlock,
+ UnknownError
+}
+
+impl Display for SendError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ SendError::ReceiverHasHungUp => write!(f, "Write failed, Receiver has closed their end of the pipe."),
+ SendError::ChannelFullWouldBlock => write!(f, "Write failed, the pipe is clogged."),
+ SendError::UnknownError => write!(f, "Write failed for unknown reasons. Probably a bug."),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) enum ReceiveError {
+ SenderHasHungUp,
+ UnknownError,
+}
+
+impl Display for ReceiveError{
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ ReceiveError::SenderHasHungUp => write!(f, "Read failed, Sender has closed their end of the pipe."),
+ ReceiveError::UnknownError => write!(f, "Read failed for unknown reasons. Probably a bug."),
+ }
+
+ }
+}
+
+impl Error for ReceiveError {}
+
+pub(crate) fn create_pipe_chan() -> Result<(Sender, Receiver),()> {
+ let mut handles : [c_int;2] = [0;2];
+ let result = unsafe { pipe2(handles.as_mut_ptr(), O_NONBLOCK) };
+ if result == -1 {
+ Err(())
+ } else {
+ Ok((
+ Sender{handle : FileHandle{raw : handles[1]}},
+ Receiver{handle : FileHandle{ raw : handles[0]}}
+ ))
+ }
+}
+
+pub(crate) struct FileHandle {
+ raw : c_int,
+}
+
+impl FileHandle {
+ pub(crate) fn get_raw(&self) -> c_int {
+ self.raw
+ }
+}
+
+impl Drop for FileHandle {
+ fn drop(&mut self) {
+ unsafe {
+ close(self.raw);
+ }
+ }
+}
+
+#[cfg(test)]
+mod pipe_chan_test {
+ use libc::fcntl;
+
+ use super::*;
+
+ #[test]
+ fn simple_send_read(){
+ let (send, recv) = create_pipe_chan().unwrap();
+ assert_eq!(recv.read_byte(), Ok(None));
+ send.send_byte(5).unwrap();
+ send.send_byte(27).unwrap();
+ assert_eq!(recv.read_byte(), Ok(Some(5)));
+ assert_eq!(recv.read_byte(), Ok(Some(27)));
+ assert_eq!(recv.read_byte(), Ok(None));
+ }
+
+ #[test]
+ fn simple_drop_sender(){
+ let (send, recv) = create_pipe_chan().unwrap();
+ assert_eq!(recv.read_byte(), Ok(None));
+ send.send_byte(5).unwrap();
+ send.send_byte(27).unwrap();
+ drop(send);
+ assert_eq!(recv.read_byte(), Ok(Some(5)));
+ assert_eq!(recv.read_byte(), Ok(Some(27)));
+ assert_eq!(recv.read_byte(), Err(ReceiveError::SenderHasHungUp));
+ }
+
+ #[test]
+ fn simple_drop_receiver(){
+ let (send, recv) = create_pipe_chan().unwrap();
+ assert_eq!(recv.read_byte(), Ok(None));
+ send.send_byte(5).unwrap();
+ drop(recv);
+ assert_eq!(send.send_byte(27), Err(SendError::ReceiverHasHungUp));
+ }
+
+ #[test]
+ fn overfill_sender(){
+ let (send, recv) = create_pipe_chan().unwrap();
+ assert_eq!(recv.read_byte(), Ok(None));
+
+ let capacity = unsafe {fcntl(send.handle.get_raw(), libc::F_GETPIPE_SZ)};
+ for _ in 0..capacity {
+ assert!(send.send_byte(3).is_ok());
+ }
+ assert_eq!(send.send_byte(3), Err(SendError::ChannelFullWouldBlock));
+
+ }
+} \ No newline at end of file
diff --git a/alsa/src/config.rs b/alsa/src/config.rs
new file mode 100644
index 0000000..93bcab9
--- /dev/null
+++ b/alsa/src/config.rs
@@ -0,0 +1,104 @@
+use std::ffi::CString;
+
+use formatable_float::{FormatableFloatValue, KeyBackingTypeMetadata, FormattingError};
+use serde::{Serialize, Deserialize};
+use swaystatus_plugin::*;
+
+use crate::{runnable::AlsaVolumeRunnable, communication::{SenderForMain, make_sender_for_main}};
+
+#[derive(Serialize, Deserialize)]
+pub struct AlsaVolumeConfig{
+ pub(crate) device : CString,
+ pub(crate) element : CString,
+ pub(crate) abstraction : SElemAbstraction,
+ sorting: FieldSorting,
+ mute: FormatableMute,
+ volume: FormatableFloatValue<VolumeKeyVolume>,
+}
+
+#[derive(Serialize, Deserialize, Clone, Copy)]
+pub(crate) enum SElemAbstraction{
+ None,
+ Basic,
+}
+
+
+#[derive(Serialize, Deserialize)]
+enum FieldSorting {
+ MuteVolume,
+ VolumeMute,
+}
+
+impl AlsaVolumeConfig {
+ pub(crate) fn format_volume(&self, volume : f32, mute : bool) -> Result<String,FormattingError> {
+ let formatted_mute = self.mute.format_mute(mute).unwrap_or(String::new());
+ let join_strings = |v : String,m : String| match self.sorting {
+ FieldSorting::MuteVolume => m + &v,
+ FieldSorting::VolumeMute => v + &m,
+ };
+ match self.volume.format_float(volume)
+ {
+ Ok(v) => Ok(join_strings(v.unwrap_or_default(), formatted_mute)),
+ Err(FormattingError::EmptyMap { numeric_fallback }) => Err(FormattingError::EmptyMap { numeric_fallback: join_strings(numeric_fallback, formatted_mute) }),
+ }
+ }
+}
+
+#[derive(Serialize, Deserialize)]
+#[serde(tag = "Format")]
+enum FormatableMute {
+ Off,
+ Symbol {
+ #[serde(rename = "Label")]
+ label : String,
+ #[serde(rename = "MuteSymbol")]
+ mute_symbol : String,
+ #[serde(rename = "UnmuteSymbol")]
+ unmute_symbol : String
+ }
+}
+
+impl FormatableMute {
+ fn format_mute(&self, mute : bool) -> Option<String> {
+ match self {
+ FormatableMute::Off => { None }
+ FormatableMute::Symbol{ label, mute_symbol, unmute_symbol} => { Some(format!("{}{}", label, { if mute { mute_symbol } else { unmute_symbol }}))}
+ }
+ }
+}
+
+#[derive(PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
+struct VolumeKeyVolume;
+impl KeyBackingTypeMetadata for VolumeKeyVolume{
+ type BackingType = u8;
+ const MIN : Self::BackingType = 0;
+ const MAX : Self::BackingType = 100;
+ const FLOAT_MIN : f32 = 0.0;
+ const FLOAT_MAX : f32 = 1.0;
+}
+
+
+impl SwayStatusModuleInstance for AlsaVolumeConfig {
+ fn make_runnable<'p>(&'p self, to_main : Box<dyn MsgModuleToMain + 'p>) -> (Box<dyn SwayStatusModuleRunnable + 'p>, Box<dyn MsgMainToModule + 'p>) {
+ if let Ok((s,r)) = make_sender_for_main() {
+ (Box::new(AlsaVolumeRunnable::new(to_main, r, self)), Box::new(SenderForMain::new(s)))
+ } else {
+ to_main.send_update(Err(PluginError::ShowInsteadOfText("Pipe creation failed. Call your plumber.".to_owned())))
+ .expect("Tried to send an error to main, but main is not listening any more.");
+ panic!("Pipe creation failed. Call your plumber.")
+ }
+ }
+}
+
+impl Default for AlsaVolumeConfig {
+ fn default() -> Self {
+ Self {
+ device: CString::new("default").unwrap(),
+ element: CString::new("Master").unwrap(),
+ abstraction : SElemAbstraction::None,
+ volume: FormatableFloatValue::Numeric { label: " ".into(), digits: 0 },
+ mute: FormatableMute::Symbol { label : String::new(), mute_symbol : String::from("🔇"), unmute_symbol : String::from("🔊") },
+ sorting: FieldSorting::MuteVolume,
+ }
+ }
+} \ No newline at end of file
diff --git a/alsa/src/lib.rs b/alsa/src/lib.rs
new file mode 100644
index 0000000..8b10036
--- /dev/null
+++ b/alsa/src/lib.rs
@@ -0,0 +1,37 @@
+use swaystatus_plugin::*;
+
+mod config;
+mod communication;
+mod runnable;
+
+use config::AlsaVolumeConfig;
+
+pub struct AlsaVolumePlugin;
+impl SwayStatusModule for AlsaVolumePlugin {
+ fn get_name(&self) -> &str {
+ "AlsaVolume"
+ }
+ fn deserialize_config<'de, 'p>(&'p self, deserializer : &mut (dyn erased_serde::Deserializer + 'de)) -> Result<Box<dyn SwayStatusModuleInstance + 'p>,erased_serde::Error> {
+ erased_serde::deserialize::<AlsaVolumeConfig>(deserializer)
+ .map(|c| Box::new(c) as Box<dyn SwayStatusModuleInstance>)
+ }
+ fn get_default_config<'p>(&'p self) -> Box<dyn SwayStatusModuleInstance + 'p> {
+ Box::new(config::AlsaVolumeConfig::default())
+ }
+ fn print_help(&self) {
+ println!(
+r#"Swaystatus Alsa Volume plugin.
+
+This is a volume display for ALSA. Currently quite limited, but hey, you're free to extend it. You must set the device and element name in config.
+Blanace is not supported at the moment, just volume of a single element."#
+ );
+ }
+}
+
+impl AlsaVolumePlugin {
+ fn new() -> Self {
+ Self
+ }
+}
+
+declare_swaystatus_module!(AlsaVolumePlugin, AlsaVolumePlugin::new);
diff --git a/alsa/src/runnable.rs b/alsa/src/runnable.rs
new file mode 100644
index 0000000..4835436
--- /dev/null
+++ b/alsa/src/runnable.rs
@@ -0,0 +1,397 @@
+use std::{cell::RefCell, fmt::Display, error::Error, ffi::CStr};
+
+use formatable_float::FormattingError;
+use libc::{c_int, c_char, c_uint, c_void, c_long, c_ushort, c_short};
+use swaystatus_plugin::*;
+
+use crate::{communication::MessagesFromMainReceiver, config::SElemAbstraction};
+
+use super::config::AlsaVolumeConfig;
+
+pub struct AlsaVolumeRunnable<'r>{
+ to_main : Box<dyn MsgModuleToMain + 'r>,
+ from_main : MessagesFromMainReceiver,
+ config : &'r AlsaVolumeConfig,
+}
+
+impl<'r> AlsaVolumeRunnable<'r> {
+ pub fn new(to_main : Box<dyn MsgModuleToMain + 'r>, from_main : MessagesFromMainReceiver, config : &'r AlsaVolumeConfig) -> Self {
+ Self { to_main, from_main, config }
+ }
+ fn send_error_to_main<E>(&self, err : E) where E : std::error::Error {
+ self.to_main.send_update(Err(PluginError::ShowInsteadOfText(String::from("Error")))).expect("Tried to tell main thread that an error occured. Main thread isn't listening any more.");
+ self.to_main.send_update(Err(PluginError::PrintToStdErr(err.to_string()))).expect("Tried to tell main thread that an error occured. Main thread isn't listening any more.");
+ }
+ fn run_internal(&self) -> Result<(), AlsaVolumeError>{
+ //Using C callbacks in Rust is a minefield.
+ //However, we can take the easy way out here, namely we only care about a single element, so we can just make a single data field ;-)
+ //It still needs to be in a Cell though.
+ let elem_scratch_space = RefCell::new(None);
+ let mixer_scratch_space = MixerScratchSpace{
+ elem_name: &self.config.element,
+ elem_scratch: &elem_scratch_space,
+ };
+ let mixer = open_mixer(0)?;
+ register_selem(mixer.handle, &self.config.device, self.config.abstraction)?;
+ unsafe { snd_mixer_set_callback(mixer.handle, Some(Self::mixer_callback)) };
+ unsafe { snd_mixer_set_callback_private(mixer.handle, &mixer_scratch_space as *const MixerScratchSpace as *const c_void)};
+ load_mixer(mixer.handle)?;
+
+ let mut should_update_main_even_if_unchanged = true;
+ loop {
+
+ let descriptor_count = unsafe{snd_mixer_poll_descriptors_count(mixer.handle)};
+ if descriptor_count < 0 {
+ return Err(AlsaVolumeError::FailedToGetPollDescriptors);
+ }
+ let mut descriptors : Vec<libc::pollfd> = std::iter::once(libc::pollfd{
+ fd: self.from_main.file_handle().get_raw(),
+ events: libc::POLLIN,
+ revents: 0
+ })
+ .chain(std::iter::repeat(
+ libc::pollfd{
+ fd: 0,
+ events: 0,
+ revents: 0,
+ }
+ ))
+ .take(descriptor_count as usize + 1)
+ .collect();
+ let descriptor_count = if descriptor_count > 0 { unsafe {snd_mixer_poll_descriptors(mixer.handle, &mut descriptors[1] , descriptor_count as c_uint)} } else { 0 };
+ if descriptor_count < 0 {
+ return Err(AlsaVolumeError::FailedToGetPollDescriptors);
+ }
+ let n = unsafe {libc::poll(descriptors.as_mut_ptr(), descriptors.len() as u64, -1)};
+ if n < 0 && n != libc::EINTR {
+ return Err(AlsaVolumeError::UnexpectedPollError);
+ }
+ //first check if there's any data on our pipe from main.
+ loop {
+ match self.from_main.receive(){
+ Ok(Some(message)) => match message{
+ crate::communication::MessagesFromMain::Quit => { return Ok(())},
+ crate::communication::MessagesFromMain::Refresh => { should_update_main_even_if_unchanged = true; },
+ },
+ Ok(None) => break, //main has nothing more to say.
+ Err(e) => match e {
+ crate::communication::pipe_chan::ReceiveError::SenderHasHungUp => { return Err(AlsaVolumeError::MainHungUpWithoutQuit) },
+ crate::communication::pipe_chan::ReceiveError::UnknownError => { return Err(AlsaVolumeError::ErrorInPluginCommunication) },
+ },
+ }
+ }
+ let old_values = mixer_scratch_space.elem_scratch.borrow().clone();
+ let anything_new_from_alsa = n > (if descriptors[0].revents != 0 { 1 } else { 0 });
+ if anything_new_from_alsa {
+ let mut revents = 0;
+ let worked = unsafe {snd_mixer_poll_descriptors_revents(mixer.handle, &descriptors[1], descriptor_count as c_uint,&mut revents) };
+ if worked < 0{
+ return Err(AlsaVolumeError::UnexpectedPollError);
+ }
+ if (revents as c_short) & (libc::POLLERR | libc::POLLNVAL) != 0 {
+ return Err(AlsaVolumeError::DeviceRemoved);
+ }
+ if (revents as c_short) & libc::POLLIN != 0 {
+ let handling_worked = unsafe {snd_mixer_handle_events(mixer.handle)};
+ if handling_worked < 0 {
+ return Err(AlsaVolumeError::EventHandlingError);
+ }
+ }
+ }
+ let new_values = mixer_scratch_space.elem_scratch.borrow().clone();
+ if new_values != old_values || should_update_main_even_if_unchanged {
+ self.send_updated_values_to_main(new_values).expect("Tried to update main thread, but it seems to be gone?");
+ }
+ should_update_main_even_if_unchanged = false;
+ }
+ }
+
+ fn send_updated_values_to_main(&self, volume : Option<ElemVolumeInfo>) -> Result<(),PluginCommunicationError> {
+ match volume{
+ Some(volume) => {
+ let formatted_volume = self.config.format_volume(volume.volume, volume.mute);
+ match formatted_volume {
+ Ok(msg) => { self.to_main.send_update(Ok(msg)) }
+ Err(e) => {
+ let full_message = e.to_string();
+ match e {
+ FormattingError::EmptyMap{ numeric_fallback } => {
+ self.to_main.send_update(Err(PluginError::ShowInsteadOfText(numeric_fallback)))?;
+ self.to_main.send_update(Err(PluginError::PrintToStdErr(full_message)))
+ }
+ }
+ }
+ }
+ },
+ None => {
+ self.to_main.send_update(Err(PluginError::ShowInsteadOfText("Unknown".into())))
+ },
+ }
+ }
+
+ extern "C" fn mixer_callback(mixer : SndMixerHandle, flags : c_uint, element : SndMixerElemHandle) -> c_int {
+ if flags & (1<<2) != 0 { //SND_CTL_EVENT_MASK_ADD
+ //check if the newly added element is the one we are looking for.
+ let scratch : &MixerScratchSpace = unsafe{&*(snd_mixer_get_callback_private(mixer) as *const MixerScratchSpace)};
+ let elem_name = unsafe { CStr::from_ptr(snd_mixer_selem_get_name(element)) };
+ if elem_name == scratch.elem_name {
+ unsafe {snd_mixer_elem_set_callback(element, Some(Self::element_callback))};
+ unsafe {snd_mixer_elem_set_callback_private(element,scratch.elem_scratch as *const ElemScratchSpace as *const c_void)};
+ 0
+ } else {
+ 0
+ }
+ } else {
+ 0
+ }
+ }
+
+ extern "C" fn element_callback(element : SndMixerElemHandle, flags : c_uint) -> c_int {
+ //okay, we hit the right element, sooo
+ if flags == (!0) { //SND_CTL_EVENT_MASK_REMOVE
+ 0
+ } else { //could check further to exclude more spurious wake-ups, but for now...
+ //we don't do any magic here. Just sum up all channel's values and call it a day.
+ let (count, volume_sum) = ALL_CHANNELS.iter()
+ .filter_map(|channel| get_volume_for_channel(element, *channel))
+ .fold((0,0), |(c, ov), v| (c+1, ov + v));
+ let average = if count == 0 { None } else { Some(volume_sum / count)};
+ let range = get_volume_range(element);
+ let normalized = average.zip(range).map(|(average, range)| ((average - range.0)*100000) / (range.1 - range.0));
+ let scratch = unsafe{&*(snd_mixer_elem_get_callback_private(element) as *const ElemScratchSpace)};
+ let has_mute = unsafe{ snd_mixer_selem_has_playback_switch(element) != 0};
+ let is_mute = if has_mute {
+ !ALL_CHANNELS.iter().any(|channel| get_switch_for_channel(element, *channel))
+ } else { false };
+ *scratch.borrow_mut() = normalized.map(|volume| ElemVolumeInfo{volume : volume as f32 / 100000.0, mute : is_mute});
+ 0
+ }
+ }
+}
+
+#[derive(Clone, Debug, PartialEq)]
+struct ElemVolumeInfo{
+ volume : f32,
+ mute : bool
+}
+
+type ElemScratchSpace = RefCell<Option<ElemVolumeInfo>>;
+
+struct MixerScratchSpace<'a>{
+ elem_name : &'a CStr,
+ elem_scratch : &'a ElemScratchSpace,
+}
+
+impl<'r> SwayStatusModuleRunnable for AlsaVolumeRunnable<'r> {
+ fn run(&self) {
+ match self.run_internal(){
+ Ok(()) => {},
+ Err(e) => self.send_error_to_main(e),
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug)]
+enum AlsaVolumeError{
+ FailedToOpenMixer,
+ FailedToLoadElements,
+ FailedToGetPollDescriptors,
+ UnexpectedPollError,
+ ErrorInPluginCommunication,
+ MainHungUpWithoutQuit,
+ DeviceRemoved,
+ EventHandlingError,
+}
+
+impl Display for AlsaVolumeError{
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ AlsaVolumeError::FailedToOpenMixer => write!(f, "Failed to open Mixer"),
+ AlsaVolumeError::FailedToLoadElements => write!(f, "Failed to load Mixer elements"),
+ AlsaVolumeError::FailedToGetPollDescriptors => write!(f, "Failed to get poll descriptors"),
+ AlsaVolumeError::UnexpectedPollError => write!(f, "Polling for updates failed for an unhandled reason. Debug."),
+ AlsaVolumeError::ErrorInPluginCommunication => write!(f, "Something went wrong with the pipe from main thread. Debug."),
+ AlsaVolumeError::MainHungUpWithoutQuit => write!(f, "Main thread ended communication without saying goodbye. Debug."),
+ AlsaVolumeError::DeviceRemoved => write!(f, "Device removed. Unsupported for now."),
+ AlsaVolumeError::EventHandlingError => write!(f, "Failure while handling mixer events. Debug."),
+ }
+ }
+}
+impl Error for AlsaVolumeError{}
+
+struct MixerHandleScopeGuard{
+ handle : SndMixerHandle
+}
+impl Drop for MixerHandleScopeGuard{
+ fn drop(&mut self) {
+ if !self.handle.is_null() {
+ unsafe {snd_mixer_close(self.handle)};
+ }
+ }
+}
+
+fn open_mixer(mode : c_int) -> Result<MixerHandleScopeGuard,AlsaVolumeError>{
+ let mut handle : SndMixerHandle = std::ptr::null();
+ let error_code = unsafe {snd_mixer_open(&mut handle, mode)};
+ if error_code == 0 {
+ Ok(MixerHandleScopeGuard { handle })
+ } else {
+ Err(AlsaVolumeError::FailedToOpenMixer)
+ }
+}
+
+fn load_mixer(mixer : SndMixerHandle) -> Result<(), AlsaVolumeError>{
+ let error_code = unsafe { snd_mixer_load(mixer)};
+ if error_code == 0 {
+ Ok(())
+ } else {
+ Err(AlsaVolumeError::FailedToLoadElements)
+ }
+}
+
+fn register_selem(mixer : SndMixerHandle, device : &CStr, abstraction : SElemAbstraction) -> Result<(), AlsaVolumeError>{
+ let options = SndMixerSelemRegopt{
+ ver: 1,
+ abstraction: match abstraction {
+ SElemAbstraction::None => SndMixerSelemRegoptAbstract::None,
+ SElemAbstraction::Basic => SndMixerSelemRegoptAbstract::Basic,
+ },
+ device: device.as_ptr(),
+ playback_pcm: std::ptr::null(),
+ capture_pcm: std::ptr::null(),
+ };
+ let error_code = unsafe {snd_mixer_selem_register(mixer, &options, std::ptr::null_mut())};
+ if error_code == 0 {
+ Ok(())
+ } else {
+ Err(AlsaVolumeError::FailedToOpenMixer)
+ }
+}
+
+fn get_volume_for_channel(element : SndMixerElemHandle, channel : SndMixerSelemChannelIdT) -> Option<c_long>{
+ if unsafe {snd_mixer_selem_has_playback_channel(element, channel) > 0} {
+ let mut value : c_long = 0;
+ if unsafe { snd_mixer_selem_get_playback_volume(element, channel, &mut value) == 0}{
+ Some(value)
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+}
+
+fn get_switch_for_channel(element : SndMixerElemHandle, channel : SndMixerSelemChannelIdT) -> bool {
+ let mut switch = 0;
+ let worked = unsafe { snd_mixer_selem_get_playback_switch(element, channel, &mut switch)};
+ worked == 0 && switch != 0
+}
+
+fn get_volume_range(element : SndMixerElemHandle) -> Option<(c_long, c_long)>{
+ let mut min = 0;
+ let mut max = 0;
+ if unsafe { snd_mixer_selem_get_playback_volume_range(element, &mut min, &mut max) == 0 } {
+ Some((min, max))
+ } else {
+ None
+ }
+}
+
+#[repr(C)] struct SndMixerT { _private: [u8; 0]}
+#[repr(C)] struct SndPcmT { _private: [u8; 0]}
+#[repr(C)] struct SndMixerClassT { _private: [u8; 0]}
+#[repr(C)] struct SndMixerElemT { _private: [u8; 0]}
+
+type SndMixerHandle = *const SndMixerT;
+type SndMixerElemHandle = *const SndMixerElemT;
+
+#[repr(C)] enum SndMixerSelemRegoptAbstract {
+ None,
+ Basic,
+}
+#[repr(C)] struct SndMixerSelemRegopt {
+ ver : c_int,
+ abstraction : SndMixerSelemRegoptAbstract,
+ device : *const c_char,
+ playback_pcm : *const SndPcmT,
+ capture_pcm : *const SndPcmT,
+}
+
+#[derive(Clone,Copy)]
+#[repr(C)] enum SndMixerSelemChannelIdT {
+ FrontLeft,
+ FrontRight,
+ RearLeft,
+ RearRight,
+ FrontCenter,
+ Woofer,
+ SideLeft,
+ SideRight,
+ RearCenter,
+}
+
+static ALL_CHANNELS : [SndMixerSelemChannelIdT;9] = [
+ SndMixerSelemChannelIdT::FrontLeft,
+ SndMixerSelemChannelIdT::FrontRight,
+ SndMixerSelemChannelIdT::RearLeft,
+ SndMixerSelemChannelIdT::RearRight,
+ SndMixerSelemChannelIdT::FrontCenter,
+ SndMixerSelemChannelIdT::Woofer,
+ SndMixerSelemChannelIdT::SideLeft,
+ SndMixerSelemChannelIdT::SideRight,
+ SndMixerSelemChannelIdT::RearCenter,
+];
+
+
+type SndMixerCallbackT = extern "C" fn(SndMixerHandle, c_uint, SndMixerElemHandle) -> c_int;
+type SndMixerElemCallbackT = extern "C" fn(SndMixerElemHandle, c_uint) -> c_int;
+#[link(name = "asound")]
+extern "C" {
+ //int snd_mixer_open ( snd_mixer_t ** mixerp, int mode )
+ fn snd_mixer_open(mixer : *mut SndMixerHandle, mode : c_int) -> c_int;
+ //int snd_mixer_close ( snd_mixer_t * mixer )
+ fn snd_mixer_close(mixer : SndMixerHandle) -> c_int;
+ //int snd_mixer_selem_register(snd_mixer_t *mixer, struct snd_mixer_selem_regopt *options, snd_mixer_class_t **classp);
+ fn snd_mixer_selem_register(mixer : SndMixerHandle, options: *const SndMixerSelemRegopt, class: *mut *const SndMixerClassT) -> c_int;
+ //void snd_mixer_set_callback ( snd_mixer_t * obj, snd_mixer_callback_t val )
+ fn snd_mixer_set_callback(mixer : SndMixerHandle, callback : Option<SndMixerCallbackT>);
+ //void snd_mixer_set_callback_private ( snd_mixer_t * mixer, void * val )
+ fn snd_mixer_set_callback_private(mixer : SndMixerHandle, value : *const c_void); //the *const is a lie, but one that we need for Stacked Borrows sanity.
+ //void* snd_mixer_get_callback_private ( const snd_mixer_t * mixer )
+ fn snd_mixer_get_callback_private(mixer : SndMixerHandle) -> *const c_void; //the *const is a lie, but one that we need for Stacked Borrows sanity.
+
+ //int snd_mixer_load ( snd_mixer_t * mixer )
+ fn snd_mixer_load(mixer : SndMixerHandle) -> c_int;
+
+ //const char* snd_mixer_selem_get_name ( snd_mixer_elem_t * elem )
+ fn snd_mixer_selem_get_name(element: SndMixerElemHandle) -> *const c_char;
+ //void snd_mixer_elem_set_callback ( snd_mixer_elem_t * mixer, snd_mixer_elem_callback_t val )
+ fn snd_mixer_elem_set_callback(element : SndMixerElemHandle, callback : Option<SndMixerElemCallbackT>);
+ //void snd_mixer_elem_set_callback_private ( snd_mixer_elem_t * mixer, void * val )
+ fn snd_mixer_elem_set_callback_private(element : SndMixerElemHandle, value : *const c_void);
+ //void* snd_mixer_elem_get_callback_private ( const snd_mixer_elem_t * mixer )
+ fn snd_mixer_elem_get_callback_private(element : SndMixerElemHandle) -> *const c_void;
+
+ //int snd_mixer_selem_has_playback_channel ( snd_mixer_elem_t * elem, snd_mixer_selem_channel_id_t channel )
+ fn snd_mixer_selem_has_playback_channel(element : SndMixerElemHandle, channel : SndMixerSelemChannelIdT) -> c_int;
+ //int snd_mixer_selem_get_playback_volume ( snd_mixer_elem_t * elem, snd_mixer_selem_channel_id_t channel, long * value )
+ fn snd_mixer_selem_get_playback_volume(element : SndMixerElemHandle, channel : SndMixerSelemChannelIdT, value : *mut c_long) -> c_int;
+ //int snd_mixer_selem_get_playback_volume_range ( snd_mixer_elem_t * elem, long * min, long * max )
+ fn snd_mixer_selem_get_playback_volume_range(element : SndMixerElemHandle, min: *mut c_long, max : *mut c_long) -> c_int;
+
+ //int snd_mixer_selem_has_playback_switch ( snd_mixer_elem_t * elem )
+ fn snd_mixer_selem_has_playback_switch(element : SndMixerElemHandle) -> c_int;
+ //int snd_mixer_selem_get_playback_switch ( snd_mixer_elem_t * elem, snd_mixer_selem_channel_id_t channel, int * value )
+ fn snd_mixer_selem_get_playback_switch(element : SndMixerElemHandle, channel : SndMixerSelemChannelIdT, value : *mut c_int) -> c_int;
+
+ //int snd_mixer_poll_descriptors_count ( snd_mixer_t * mixer )
+ fn snd_mixer_poll_descriptors_count(mixer : SndMixerHandle) -> c_int;
+ //int snd_mixer_poll_descriptors ( snd_mixer_t * mixer, struct pollfd * pfds, unsigned int space )
+ fn snd_mixer_poll_descriptors(mixer : SndMixerHandle, fds : *mut libc::pollfd, space : c_uint) -> c_int;
+
+ //int snd_mixer_poll_descriptors_revents ( snd_mixer_t * mixer, struct pollfd * pfds, unsigned int nfds, unsigned short * revents )
+ fn snd_mixer_poll_descriptors_revents(mixer : SndMixerHandle, fds : *const libc::pollfd, nfds : c_uint, revents : *mut c_ushort) -> c_int;
+
+ //int snd_mixer_handle_events ( snd_mixer_t * mixer )
+ fn snd_mixer_handle_events(mixer : SndMixerHandle) -> c_int;
+} \ No newline at end of file
diff --git a/formatable-float/Cargo.toml b/formatable-float/Cargo.toml
new file mode 100644
index 0000000..323bc07
--- /dev/null
+++ b/formatable-float/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "formatable-float"
+version = "0.1.0"
+authors = ["Andreas Grois <andi@grois.info>"]
+edition = "2021"
+
+[dependencies]
+serde = { version = "1.0", features = ["derive"] }
+erased-serde = "0.3"
diff --git a/formatable-float/src/lib.rs b/formatable-float/src/lib.rs
new file mode 100644
index 0000000..7b1db21
--- /dev/null
+++ b/formatable-float/src/lib.rs
@@ -0,0 +1,154 @@
+use serde::{Serialize,Deserialize,Serializer,Deserializer};
+use serde::de::Error as DeError;
+use serde::de::Unexpected as DeUnexpect;
+use std::collections::BTreeMap;
+use std::ops::{Add, Sub};
+use std::str::FromStr;
+use std::num::{ParseIntError,IntErrorKind};
+
+#[derive(Serialize, Deserialize)]
+#[serde(tag = "Format")]
+pub enum FormatableFloatValue<KeyTypeMetadata : KeyBackingTypeMetadata> {
+ Off,
+ Numeric {
+ #[serde(rename = "Label")]
+ label : String,
+ #[serde(rename = "DecimalDigits")]
+ digits : u8
+ },
+ Binned {
+ #[serde(rename = "Label")]
+ label: String,
+ #[serde(rename = "PercentToSymbolMap")]
+ bin_symbol_map : BTreeMap<FormatableFloatKey<KeyTypeMetadata>,String>
+ }
+}
+
+#[derive(Debug)]
+pub enum FormattingError {
+ EmptyMap {
+ numeric_fallback : String
+ }
+}
+impl std::fmt::Display for FormattingError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ FormattingError::EmptyMap{numeric_fallback} => { write!(f, "Formatting failed. Empty PercentToSymbolMap. Numeric value: {}", numeric_fallback) }
+ }
+ }
+}
+impl std::error::Error for FormattingError {}
+
+impl<KeyTypeMetadata : KeyBackingTypeMetadata> FormatableFloatValue<KeyTypeMetadata> {
+ pub fn format_float(&self, float : f32) -> Result<Option<String>, FormattingError> {
+ match self {
+ FormatableFloatValue::Numeric{ label, digits } => { Ok(Some(Self::format_float_numeric(float, label, *digits))) }
+ FormatableFloatValue::Binned{ label, bin_symbol_map } => { Some(Self::format_float_binned(float, label, bin_symbol_map)).transpose()}
+ FormatableFloatValue::Off => {Ok(None)}
+ }
+ }
+ pub fn format_float_binned(float : f32, label : &str, bin_symbol_map : &BTreeMap<FormatableFloatKey<KeyTypeMetadata>, String>) -> Result<String,FormattingError> {
+ let value_to_match = FormatableFloatKey::<KeyTypeMetadata>::match_float(float);
+ //first try to find the next lower value.
+ if let Some((_,msg)) = bin_symbol_map.range(..=value_to_match).next_back() {
+ Ok(format!("{}{}",label,msg))
+ }
+ else if let Some((_,msg)) = bin_symbol_map.iter().next() {
+ Ok(format!("{}{}",label,msg))
+ }
+ else {
+ Err(FormattingError::EmptyMap{numeric_fallback : Self::format_float_numeric(float, label, 0) })
+ }
+ }
+ pub fn format_float_numeric(float : f32, label : &str, digits : u8) -> String {
+ let percentage = 100.0*float;
+ format!("{}{:.*}%", label, digits as usize, percentage)
+ }
+}
+
+///Helper trait for conversion from float to integer backing type for binning keys.
+///Needed because Rust seems not to offer a trait that indicates "can be rounded from float"
+///in the standard library. There are thir-party crates that do this, but using a full crate
+///for a few lines of code sounds a bit excessive...
+pub trait BackingTypeFromFloat {
+ fn round_from_float(float : f32) -> Self;
+}
+
+///Metadata description for Keys. Basically a workaround for Rust's lack of
+///constant generics. Having Ord as supertrait is because of the BTreeMap's trait
+///bounds.
+pub trait KeyBackingTypeMetadata : Ord {
+ type BackingType
+ : Ord
+ + Add<Output = Self::BackingType>
+ + Sub<Output = Self::BackingType>
+ + Into<f32>
+ + BackingTypeFromFloat
+ + ToString //TOML needs map keys to be strings...
+ + FromStr<Err = ParseIntError>
+ + std::fmt::Display;
+ const MIN : Self::BackingType;
+ const MAX : Self::BackingType;
+ const FLOAT_MIN : f32;
+ const FLOAT_MAX : f32;
+}
+
+#[derive(Debug,PartialEq,Eq,PartialOrd,Ord)]
+pub struct FormatableFloatKey<BackingType : KeyBackingTypeMetadata>(pub BackingType::BackingType);
+
+impl<BackingType : KeyBackingTypeMetadata> FormatableFloatKey<BackingType> {
+ fn match_float(float : f32) -> Self {
+ let x = (float - BackingType::FLOAT_MIN) / (BackingType::FLOAT_MAX - BackingType::FLOAT_MIN);
+ let cx = x.clamp(0.0,1.0);
+ let interval = BackingType::MAX.into() - BackingType::MIN.into();
+ let offset = interval * cx;
+ let result = BackingType::BackingType::round_from_float(offset) + BackingType::MIN;
+ Self(result)
+ }
+
+}
+/// Custom serializer, as TOML only supports string map keys.
+impl<Metadata : KeyBackingTypeMetadata> Serialize for FormatableFloatKey<Metadata> {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where S : Serializer
+ {
+ let string = self.0.to_string();
+ serializer.serialize_str(&string)
+ }
+}
+impl<'de, Metadata> Deserialize<'de> for FormatableFloatKey<Metadata>
+ where Metadata : KeyBackingTypeMetadata,
+{
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where D: Deserializer<'de>
+ {
+ let a = String::deserialize(deserializer)?;
+ match a.parse() {
+ Ok(x) => {
+ if x >= Metadata::MIN && x <= Metadata::MAX {
+ Ok(Self(x))
+ }
+ else {
+ Err(DeError::invalid_value(DeUnexpect::Str(&a), &&*format!("an integer equal or larger {} and equal or smaller {}", Metadata::MIN, Metadata::MAX)))
+ }
+ }
+ Err(e) => {
+ match e.kind() {
+ IntErrorKind::Empty => { Err(DeError::missing_field("Bin Map Key")) }
+ IntErrorKind::InvalidDigit => { Err(DeError::invalid_type(DeUnexpect::Str(&a), &"an integer value")) }
+ IntErrorKind::NegOverflow | IntErrorKind::PosOverflow => { Err(DeError::invalid_value(DeUnexpect::Str(&a), &&*format!("an integer equal or larger {} and equal or smaller {}", Metadata::MIN, Metadata::MAX))) }
+ IntErrorKind::Zero => { Err(DeError::invalid_value(DeUnexpect::Str(&a), &&*format!("a nonzero integer between {} and {}", Metadata::MIN, Metadata::MAX)))}
+ _ => { Err(DeError::custom("Value could not be parsed")) }
+ }
+ }
+ }
+ }
+}
+macro_rules! impl_key_backing_type_from_float_for {
+ ($( $t:ty ), *) => {
+ $( impl BackingTypeFromFloat for $t {
+ fn round_from_float(float : f32) -> $t { float.round() as $t }
+ } )*
+ }
+}
+impl_key_backing_type_from_float_for!(i8, u8, i16, u16, i32, u32, i64, u64, isize, usize, i128, u128);
diff --git a/pulse/Cargo.toml b/pulse/Cargo.toml
index c6cff4b..c3b4c12 100644
--- a/pulse/Cargo.toml
+++ b/pulse/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2021"
swaystatus-plugin = { path = '../swaystatus-plugin', version = '*'}
serde = { version = "1.0", features = ["derive"] }
erased-serde = "0.3"
+formatable-float = { path = '../formatable-float', version = '*'}
[lib]
crate-type = ["cdylib"]
diff --git a/pulse/src/config.rs b/pulse/src/config.rs
index 0be7367..fb123e0 100644
--- a/pulse/src/config.rs
+++ b/pulse/src/config.rs
@@ -1,12 +1,8 @@
-use serde::{Serialize,Deserialize,Serializer,Deserializer};
-use serde::de::Error as DeError;
-use serde::de::Unexpected as DeUnexpect;
use std::collections::BTreeMap;
-use swaystatus_plugin::*;
-use std::ops::{Add, Sub};
-use std::str::FromStr;
-use std::num::{ParseIntError,IntErrorKind};
+use serde::{Serialize,Deserialize};
+use swaystatus_plugin::*;
+use formatable_float::{FormatableFloatValue, FormattingError, KeyBackingTypeMetadata, FormatableFloatKey};
#[derive(Serialize, Deserialize)]
#[serde(tag = "Sink")]
@@ -19,24 +15,6 @@ pub(crate) enum Sink {
}
#[derive(Serialize, Deserialize)]
-#[serde(tag = "Format")]
-enum FormatableVolume<KeyTypeMetadata : VolumeKeyBackingTypeMetadata> {
- Off,
- Numeric {
- #[serde(rename = "Label")]
- label : String,
- #[serde(rename = "DecimalDigits")]
- digits : u8
- },
- Binned {
- #[serde(rename = "Label")]
- label: String,
- #[serde(rename = "PercentToSymbolMap")]
- bin_symbol_map : BTreeMap<VolumeKey<KeyTypeMetadata>,String>
- }
-}
-
-#[derive(Serialize, Deserialize)]
enum FieldSorting {
MuteVolumeBalance,
MuteBalanceVolume,
@@ -65,8 +43,8 @@ enum FormatableMute {
pub struct PulseVolumeConfig {
sorting : FieldSorting,
pub(crate) sink : Sink,
- volume : FormatableVolume<VolumeKeyVolume>,
- balance : FormatableVolume<VolumeKeyBalance>,
+ volume : FormatableFloatValue<VolumeKeyVolume>,
+ balance : FormatableFloatValue<VolumeKeyBalance>,
mute : FormatableMute,
}
@@ -109,14 +87,14 @@ impl Default for PulseVolumeConfig {
fn default() -> Self {
PulseVolumeConfig {
sink : Sink::Default,
- volume : FormatableVolume::Numeric { label : String::from(""), digits : 0 },
- balance : FormatableVolume::Binned {
+ volume : FormatableFloatValue::Numeric { label : String::from(""), digits : 0 },
+ balance : FormatableFloatValue::Binned {
label : String::from(" "),
bin_symbol_map : {
let mut a = BTreeMap::new();
- a.insert(VolumeKey(-100),String::from("|.."));
- a.insert(VolumeKey(-10), String::from(".|."));
- a.insert(VolumeKey(10), String::from("..|"));
+ a.insert(FormatableFloatKey(-100),String::from("|.."));
+ a.insert(FormatableFloatKey(-10), String::from(".|."));
+ a.insert(FormatableFloatKey(10), String::from("..|"));
a
}
},
@@ -133,48 +111,6 @@ impl SwayStatusModuleInstance for PulseVolumeConfig {
}
}
-#[derive(Debug)]
-pub(crate) enum FormattingError {
- EmptyMap {
- numeric_fallback : String
- }
-}
-impl std::fmt::Display for FormattingError {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- FormattingError::EmptyMap{numeric_fallback} => { write!(f, "Formatting failed. Empty PercentToSymbolMap. Numeric value: {}", numeric_fallback) }
- }
- }
-}
-impl std::error::Error for FormattingError {}
-
-impl<KeyTypeMetadata : VolumeKeyBackingTypeMetadata> FormatableVolume<KeyTypeMetadata> {
- fn format_float(&self, float : f32) -> Result<Option<String>, FormattingError> {
- match self {
- FormatableVolume::Numeric{ label, digits } => { Ok(Some(Self::format_float_numeric(float, label, *digits))) }
- FormatableVolume::Binned{ label, bin_symbol_map } => { Some(Self::format_float_binned(float, label, bin_symbol_map)).transpose()}
- FormatableVolume::Off => {Ok(None)}
- }
- }
- fn format_float_binned(float : f32, label : &str, bin_symbol_map : &BTreeMap<VolumeKey<KeyTypeMetadata>, String>) -> Result<String,FormattingError> {
- let value_to_match = VolumeKey::<KeyTypeMetadata>::match_float(float);
- //first try to find the next lower value.
- if let Some((_,msg)) = bin_symbol_map.range(..=value_to_match).next_back() {
- Ok(format!("{}{}",label,msg))
- }
- else if let Some((_,msg)) = bin_symbol_map.iter().next() {
- Ok(format!("{}{}",label,msg))
- }
- else {
- Err(FormattingError::EmptyMap{numeric_fallback : Self::format_float_numeric(float, label, 0) })
- }
- }
- fn format_float_numeric(float : f32, label : &str, digits : u8) -> String {
- let percentage = 100.0*float;
- format!("{}{:.*}%", label, digits as usize, percentage)
- }
-}
-
impl FormatableMute {
fn format_mute(&self, mute : bool) -> Option<String> {
match self {
@@ -183,45 +119,12 @@ impl FormatableMute {
}
}
}
-///Helper trait for conversion from float to integer backing type for volume binning keys.
-///Needed because Rust seems not to offer a trait that indicates "can be rounded from float"
-///in the standard library. There are thir-party crates that do this, but using a full crate
-///for a few lines of code sounds a bit excessive...
-trait VolumeKeyBackingTypeFromFloat {
- fn round_from_float(float : f32) -> Self;
-}
-macro_rules! impl_volume_key_backing_type_from_float_for {
- ($( $t:ty ), *) => {
- $( impl VolumeKeyBackingTypeFromFloat for $t {
- fn round_from_float(float : f32) -> $t { float.round() as $t }
- } )*
- }
-}
-
-///Metadata description for VolumeKeys. Basically a workaround for Rust's lack of
-///constant generics. Having Ord as supertrait is because of the BTreeMap's trait
-///bounds.
-trait VolumeKeyBackingTypeMetadata : Ord {
- type BackingType
- : Ord
- + Add<Output = Self::BackingType>
- + Sub<Output = Self::BackingType>
- + Into<f32>
- + VolumeKeyBackingTypeFromFloat
- + ToString //TOML needs map keys to be strings...
- + FromStr<Err = ParseIntError>
- + std::fmt::Display;
- const MIN : Self::BackingType;
- const MAX : Self::BackingType;
- const FLOAT_MIN : f32;
- const FLOAT_MAX : f32;
-}
#[derive(PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
struct VolumeKeyVolume;
#[derive(PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
struct VolumeKeyBalance;
-impl VolumeKeyBackingTypeMetadata for VolumeKeyVolume{
+impl KeyBackingTypeMetadata for VolumeKeyVolume{
type BackingType = u8;
const MIN : Self::BackingType = 0;
const MAX : Self::BackingType = 100;
@@ -229,64 +132,10 @@ impl VolumeKeyBackingTypeMetadata for VolumeKeyVolume{
const FLOAT_MAX : f32 = 1.0;
}
-impl VolumeKeyBackingTypeMetadata for VolumeKeyBalance{
+impl KeyBackingTypeMetadata for VolumeKeyBalance{
type BackingType = i8;
const MIN : Self::BackingType = -100;
const MAX : Self::BackingType = 100;
const FLOAT_MIN : f32 = -1.0;
const FLOAT_MAX : f32 = 1.0;
}
-
-impl_volume_key_backing_type_from_float_for!(i8, u8);
-
-#[derive(Debug,PartialEq,Eq,PartialOrd,Ord)]
-struct VolumeKey<BackingType : VolumeKeyBackingTypeMetadata>(BackingType::BackingType);
-
-impl<BackingType : VolumeKeyBackingTypeMetadata> VolumeKey<BackingType> {
- fn match_float(float : f32) -> Self {
- let x = (float - BackingType::FLOAT_MIN) / (BackingType::FLOAT_MAX - BackingType::FLOAT_MIN);
- let cx = x.clamp(0.0,1.0);
- let interval = BackingType::MAX.into() - BackingType::MIN.into();
- let offset = interval * cx;
- let result = BackingType::BackingType::round_from_float(offset) + BackingType::MIN;
- Self(result)
- }
-
-}
-/// Custom serializer, as TOML only supports string map keys.
-impl<Metadata : VolumeKeyBackingTypeMetadata> Serialize for VolumeKey<Metadata> {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where S : Serializer
- {
- let string = self.0.to_string();
- serializer.serialize_str(&string)
- }
-}
-impl<'de, Metadata> Deserialize<'de> for VolumeKey<Metadata>
- where Metadata : VolumeKeyBackingTypeMetadata,
-{
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where D: Deserializer<'de>
- {
- let a = String::deserialize(deserializer)?;
- match a.parse() {
- Ok(x) => {
- if x >= Metadata::MIN && x <= Metadata::MAX {
- Ok(Self(x))
- }
- else {
- Err(DeError::invalid_value(DeUnexpect::Str(&a), &&*format!("an integer equal or larger {} and equal or smaller {}", Metadata::MIN, Metadata::MAX)))
- }
- }
- Err(e) => {
- match e.kind() {
- IntErrorKind::Empty => { Err(DeError::missing_field("Bin Map Key")) }
- IntErrorKind::InvalidDigit => { Err(DeError::invalid_type(DeUnexpect::Str(&a), &"an integer value")) }
- IntErrorKind::NegOverflow | IntErrorKind::PosOverflow => { Err(DeError::invalid_value(DeUnexpect::Str(&a), &&*format!("an integer equal or larger {} and equal or smaller {}", Metadata::MIN, Metadata::MAX))) }
- IntErrorKind::Zero => { Err(DeError::invalid_value(DeUnexpect::Str(&a), &&*format!("a nonzero integer between {} and {}", Metadata::MIN, Metadata::MAX)))}
- _ => { Err(DeError::custom("Value could not be parsed")) }
- }
- }
- }
- }
-}
diff --git a/pulse/src/runnable/mod.rs b/pulse/src/runnable/mod.rs
index b207874..4a89c5c 100644
--- a/pulse/src/runnable/mod.rs
+++ b/pulse/src/runnable/mod.rs
@@ -3,6 +3,7 @@ use crate::config::*;
use swaystatus_plugin::*;
use crate::communication::*;
use std::convert::TryFrom;
+use formatable_float::FormattingError;
pub mod pulse;
use pulse::{Pulse,MainLoopCreationError, PulseContext, PaContextState, SinkHandle, PulseOperation};
diff --git a/swaystatus/Cargo.toml b/swaystatus/Cargo.toml
index d32b6ae..7faab6f 100644
--- a/swaystatus/Cargo.toml
+++ b/swaystatus/Cargo.toml
@@ -15,7 +15,7 @@ erased-serde = "0.3"
toml = "0.5"
libloading = "0.7"
signal-hook = { version = "0.3", default-features = false, features = ["iterator"]}
-clap = { version = "3.0.0-beta.5", default-features = false, features = ["std", "cargo", "wrap_help"] }
+clap = { version = "3.2.23", default-features = false, features = ["std", "cargo", "wrap_help"] }
dirs = "3.0"
swaystatus-plugin = { path = '../swaystatus-plugin', version = '*'}
diff --git a/swaystatus/src/commandline/mod.rs b/swaystatus/src/commandline/mod.rs
index d615355..229ed98 100644
--- a/swaystatus/src/commandline/mod.rs
+++ b/swaystatus/src/commandline/mod.rs
@@ -32,20 +32,20 @@ pub fn parse_commandline() -> CommandlineParameters {
Arg::new("help")
.short('h')
.long("help")
- .about(&*gettext("Prints help information."))
+ .help(&*gettext("Prints help information."))
.global(true))
.arg(
Arg::new("version")
.short('v')
.long("version")
- .about(&*gettext("Prints version information."))
+ .help(&*gettext("Prints version information."))
.global(true))
.arg(
Arg::new("config")
.short('c')
.long("config")
.value_name(&*gettext("FILE"))
- .about(&*gettext("Path to the configuration file"))
+ .help(&*gettext("Path to the configuration file"))
.display_order(0)
.takes_value(true))
.arg(
@@ -53,14 +53,14 @@ pub fn parse_commandline() -> CommandlineParameters {
.short('p')
.long("plugins")
.value_name(&*gettext("FOLDER"))
- .about(&*gettext("Directory from which the plugins should be loaded"))
+ .help(&*gettext("Directory from which the plugins should be loaded"))
.display_order(0)
.takes_value(true))
.arg(
Arg::new("sampleconfig")
.long("print-sample-config")
.short('s')
- .about(&*gettext("Prints a sample config file. Beware that the contents of the sample file depend on the loaded plugins, so don't forget to supply the plugins parameter as needed."))
+ .help(&*gettext("Prints a sample config file. Beware that the contents of the sample file depend on the loaded plugins, so don't forget to supply the plugins parameter as needed."))
.display_order(2)
.takes_value(false)
.conflicts_with_all(&["pluginhelp","pluginlist"]))
@@ -69,7 +69,7 @@ pub fn parse_commandline() -> CommandlineParameters {
.long("plugin-help")
.short('g')
.value_name(&*gettext("PLUGINS"))
- .about(&*gettext("Prints plugin help messages. Either for a given list of plugins, or if no list given, for all loadable plugins."))
+ .help(&*gettext("Prints plugin help messages. Either for a given list of plugins, or if no list given, for all loadable plugins."))
.min_values(0)
.setting(ArgSettings::MultipleValues)
.conflicts_with("pluginlist"))
@@ -77,7 +77,7 @@ pub fn parse_commandline() -> CommandlineParameters {
Arg::new("pluginlist")
.long("list-plugins")
.short('l')
- .about(&*gettext("Prints a list of plugin names in the plugin folder."))
+ .help(&*gettext("Prints a list of plugin names in the plugin folder."))
.display_order(1)
.takes_value(false))
.after_help(&*gettext!("If no config path is given, the code looks for the \"swaystatus/config\" file in your XDG config folder (typically \"$HOME/.config/\"). If that lookup fails, loading of \"/etc/swaystatus/config\" is attempted. Similarly, if no plugin folder is given, first the existence of a folder named \"$HOME/.local/lib/swaystatus\" is checked. If this folder does not exist, a default path set at compile time is used, which in your case is \"{}\"." , get_hardcoded_default_library_path()))
diff --git a/testconfig b/testconfig
index 1d668fe..66530d9 100644
--- a/testconfig
+++ b/testconfig
@@ -47,3 +47,27 @@ UnmuteSymbol = "🔊"
[Element.General]
BeforeText = ""
AfterText = ""
+
+[[Element]]
+Plugin = "AlsaVolume"
+
+[Element.Config]
+device = [100, 101, 102, 97, 117, 108, 116]
+element = [77, 97, 115, 116, 101, 114]
+abstraction = "None"
+sorting = "MuteVolume"
+
+[Element.Config.mute]
+Format = "Symbol"
+Label = ""
+MuteSymbol = "🔇"
+UnmuteSymbol = "🔊"
+
+[Element.Config.volume]
+Format = "Numeric"
+Label = " "
+DecimalDigits = 0
+
+[Element.General]
+BeforeText = ""
+AfterText = "" \ No newline at end of file