1
0
Fork 0
mirror of https://github.com/Zedfrigg/ironbar.git synced 2025-10-06 04:31:55 +02:00

Compare commits

..

No commits in common. "54e91facd31e5c75081795e8cda8de2635d0abc0" and "7568b83deed97c02c42db571a4c5d26bb568b65a" have entirely different histories.

22 changed files with 611 additions and 411 deletions

250
Cargo.lock generated
View file

@ -265,9 +265,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.9.1" version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@ -309,7 +309,7 @@ version = "0.18.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"cairo-sys-rs", "cairo-sys-rs",
"glib", "glib",
"libc", "libc",
@ -330,11 +330,11 @@ dependencies = [
[[package]] [[package]]
name = "calloop" name = "calloop"
version = "0.13.0" version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"log", "log",
"polling", "polling",
"rustix 0.38.44", "rustix 0.38.44",
@ -344,9 +344,9 @@ dependencies = [
[[package]] [[package]]
name = "calloop-wayland-source" name = "calloop-wayland-source"
version = "0.3.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02"
dependencies = [ dependencies = [
"calloop", "calloop",
"rustix 0.38.44", "rustix 0.38.44",
@ -410,9 +410,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.42" version = "4.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -420,9 +420,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.42" version = "4.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -432,18 +432,18 @@ dependencies = [
[[package]] [[package]]
name = "clap_complete" name = "clap_complete"
version = "4.5.55" version = "4.5.54"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5abde44486daf70c5be8b8f8f1b66c49f86236edf6fa2abadb4d961c4c6229a" checksum = "aad5b1b4de04fead402672b48897030eec1f3bfe1550776322f59f6d6e6a5677"
dependencies = [ dependencies = [
"clap", "clap",
] ]
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.41" version = "4.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
dependencies = [ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
@ -821,11 +821,11 @@ dependencies = [
[[package]] [[package]]
name = "evdev-rs" name = "evdev-rs"
version = "0.6.2" version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53b9cb6084eed4e72c0306e1cbcd3fd4c8acb613044e66810f9f5d3c7896bfb7" checksum = "9812d5790fb6fcce449333eb6713dad335e8c979225ed98755c84a3987e06dba"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 1.3.2",
"evdev-sys", "evdev-sys",
"libc", "libc",
"log", "log",
@ -833,9 +833,9 @@ dependencies = [
[[package]] [[package]]
name = "evdev-sys" name = "evdev-sys"
version = "0.2.6" version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdcf0d489f4d9a80ac2b3b35b92fdd8fcf68d33bb67f947afe5cd36e482de576" checksum = "14ead42b547b15d47089c1243d907bcf0eb94e457046d3b315a26ac9c9e9ea6d"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@ -1203,7 +1203,7 @@ version = "0.18.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-executor", "futures-executor",
@ -1303,7 +1303,7 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc759b3184830a547b31549ab40c4b54450ab702bba79ba23f049bc1d1e3ca98" checksum = "bc759b3184830a547b31549ab40c4b54450ab702bba79ba23f049bc1d1e3ca98"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"gdk", "gdk",
"glib", "glib",
"glib-sys 0.18.1", "glib-sys 0.18.1",
@ -1525,7 +1525,7 @@ dependencies = [
"libc", "libc",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"socket2 0.5.10", "socket2",
"tokio", "tokio",
"tower-service", "tower-service",
"tracing", "tracing",
@ -1755,7 +1755,7 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"inotify-sys", "inotify-sys",
"libc", "libc",
] ]
@ -1775,7 +1775,7 @@ version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"cfg-if", "cfg-if",
"libc", "libc",
] ]
@ -1824,7 +1824,7 @@ dependencies = [
"notify", "notify",
"regex", "regex",
"reqwest", "reqwest",
"rustix 1.0.8", "rustix 1.0.7",
"schemars", "schemars",
"serde", "serde",
"serde_json", "serde_json",
@ -1842,7 +1842,7 @@ dependencies = [
"universal-config", "universal-config",
"walkdir", "walkdir",
"wayland-client", "wayland-client",
"wayland-protocols-wlr", "wayland-protocols-wlr 0.3.8",
"zbus", "zbus",
] ]
@ -1896,9 +1896,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.174" version = "0.2.173"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb"
[[package]] [[package]]
name = "libcorn" name = "libcorn"
@ -1929,7 +1929,7 @@ version = "2.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "909eb3049e16e373680fe65afe6e2a722ace06b671250cc4849557bc57d6a397" checksum = "909eb3049e16e373680fe65afe6e2a722ace06b671250cc4849557bc57d6a397"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"libc", "libc",
"libpulse-sys", "libpulse-sys",
"num-derive", "num-derive",
@ -1956,7 +1956,7 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"libc", "libc",
] ]
@ -1990,9 +1990,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.27" version = "0.4.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
[[package]] [[package]]
name = "lua-src" name = "lua-src"
@ -2065,9 +2065,9 @@ dependencies = [
[[package]] [[package]]
name = "mlua" name = "mlua"
version = "0.11.1" version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de25fc513588ac1273aa8c6dc0fffee6d32c12f38dc75f5cdc74547121a107ef" checksum = "c1f5f8fbebc7db5f671671134b9321c4b9aa9adeafccfd9a8c020ae45c6a35d0"
dependencies = [ dependencies = [
"bstr", "bstr",
"either", "either",
@ -2080,9 +2080,9 @@ dependencies = [
[[package]] [[package]]
name = "mlua-sys" name = "mlua-sys"
version = "0.8.2" version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcdf7c9e260ca82aaa32ac11148941952b856bb8c69aa5a9e65962f21fcb8637" checksum = "380c1f7e2099cafcf40e51d3a9f20a346977587aa4d012eae1f043149a728a93"
dependencies = [ dependencies = [
"cc", "cc",
"cfg-if", "cfg-if",
@ -2163,7 +2163,7 @@ version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"cfg-if", "cfg-if",
"cfg_aliases", "cfg_aliases",
"libc", "libc",
@ -2182,11 +2182,11 @@ dependencies = [
[[package]] [[package]]
name = "notify" name = "notify"
version = "8.2.0" version = "8.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" checksum = "3163f59cd3fa0e9ef8c32f242966a7b9994fd7378366099593e0e73077cd8c97"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"inotify", "inotify",
"kqueue", "kqueue",
"libc", "libc",
@ -2254,7 +2254,7 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
] ]
[[package]] [[package]]
@ -2288,7 +2288,7 @@ version = "0.10.72"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"cfg-if", "cfg-if",
"foreign-types", "foreign-types",
"libc", "libc",
@ -2658,7 +2658,7 @@ version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
] ]
[[package]] [[package]]
@ -2672,26 +2672,6 @@ dependencies = [
"thiserror 2.0.12", "thiserror 2.0.12",
] ]
[[package]]
name = "ref-cast"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf"
dependencies = [
"ref-cast-impl",
]
[[package]]
name = "ref-cast-impl"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7"
dependencies = [
"proc-macro2",
"quote 1.0.39",
"syn 2.0.99",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.11.1" version = "1.11.1"
@ -2795,7 +2775,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "beceb6f7bf81c73e73aeef6dd1356d9a1b2b4909e1f0fc3e59b034f9572d7b7f" checksum = "beceb6f7bf81c73e73aeef6dd1356d9a1b2b4909e1f0fc3e59b034f9572d7b7f"
dependencies = [ dependencies = [
"base64", "base64",
"bitflags 2.9.1", "bitflags 2.9.0",
"serde", "serde",
"serde_derive", "serde_derive",
"unicode-ident", "unicode-ident",
@ -2828,7 +2808,7 @@ version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.4.15", "linux-raw-sys 0.4.15",
@ -2837,15 +2817,15 @@ dependencies = [
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "1.0.8" version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.9.3", "linux-raw-sys 0.9.3",
"windows-sys 0.60.2", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@ -2910,13 +2890,11 @@ dependencies = [
[[package]] [[package]]
name = "schemars" name = "schemars"
version = "1.0.4" version = "0.8.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615"
dependencies = [ dependencies = [
"dyn-clone", "dyn-clone",
"indexmap",
"ref-cast",
"schemars_derive", "schemars_derive",
"serde", "serde",
"serde_json", "serde_json",
@ -2924,9 +2902,9 @@ dependencies = [
[[package]] [[package]]
name = "schemars_derive" name = "schemars_derive"
version = "1.0.4" version = "0.8.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d020396d1d138dc19f1165df7545479dcd58d93810dc5d646a16e55abefa80" checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.39", "quote 1.0.39",
@ -2946,7 +2924,7 @@ version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"core-foundation", "core-foundation",
"core-foundation-sys", "core-foundation-sys",
"libc", "libc",
@ -3002,9 +2980,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.142" version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
@ -3115,11 +3093,11 @@ checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
[[package]] [[package]]
name = "smithay-client-toolkit" name = "smithay-client-toolkit"
version = "0.19.2" version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"calloop", "calloop",
"calloop-wayland-source", "calloop-wayland-source",
"cursor-icon", "cursor-icon",
@ -3132,8 +3110,8 @@ dependencies = [
"wayland-client", "wayland-client",
"wayland-csd-frame", "wayland-csd-frame",
"wayland-cursor", "wayland-cursor",
"wayland-protocols", "wayland-protocols 0.31.2",
"wayland-protocols-wlr", "wayland-protocols-wlr 0.2.0",
"wayland-scanner", "wayland-scanner",
"xkeysym", "xkeysym",
] ]
@ -3148,16 +3126,6 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "socket2"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "stable_deref_trait" name = "stable_deref_trait"
version = "1.2.0" version = "1.2.0"
@ -3286,9 +3254,9 @@ dependencies = [
[[package]] [[package]]
name = "sysinfo" name = "sysinfo"
version = "0.36.1" version = "0.35.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "252800745060e7b9ffb7b2badbd8b31cfa4aa2e61af879d0a3bf2a317c20217d" checksum = "3c3ffa3e4ff2b324a57f7aeb3c349656c7b127c3c189520251a648102a92496e"
dependencies = [ dependencies = [
"libc", "libc",
"memchr", "memchr",
@ -3326,11 +3294,10 @@ dependencies = [
[[package]] [[package]]
name = "system-tray" name = "system-tray"
version = "0.8.1" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90d5d024b1573d22079347055d817863c21ea0903df404668095499c08800e4a" checksum = "c3397841ed755bf361606a845779e0f7333d35fb4e39627ef6f656d7cdad4c73"
dependencies = [ dependencies = [
"cfg-if",
"dbusmenu-gtk3-sys", "dbusmenu-gtk3-sys",
"futures-lite", "futures-lite",
"gtk", "gtk",
@ -3454,9 +3421,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.47.1" version = "1.46.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",
@ -3466,10 +3433,10 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
"signal-hook-registry", "signal-hook-registry",
"slab", "slab",
"socket2 0.6.0", "socket2",
"tokio-macros", "tokio-macros",
"tracing", "tracing",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -3604,7 +3571,7 @@ version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cc2d9e086a412a451384326f521c8123a99a466b329941a9403696bff9b0da2" checksum = "5cc2d9e086a412a451384326f521c8123a99a466b329941a9403696bff9b0da2"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"bytes", "bytes",
"futures-util", "futures-util",
"http", "http",
@ -3962,25 +3929,25 @@ dependencies = [
[[package]] [[package]]
name = "wayland-backend" name = "wayland-backend"
version = "0.3.11" version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121"
dependencies = [ dependencies = [
"cc", "cc",
"downcast-rs", "downcast-rs",
"rustix 1.0.8", "rustix 0.38.44",
"smallvec", "smallvec",
"wayland-sys", "wayland-sys",
] ]
[[package]] [[package]]
name = "wayland-client" name = "wayland-client"
version = "0.31.11" version = "0.31.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"rustix 1.0.8", "rustix 0.38.44",
"wayland-backend", "wayland-backend",
"wayland-scanner", "wayland-scanner",
] ]
@ -3991,7 +3958,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"cursor-icon", "cursor-icon",
"wayland-backend", "wayland-backend",
] ]
@ -4009,11 +3976,23 @@ dependencies = [
[[package]] [[package]]
name = "wayland-protocols" name = "wayland-protocols"
version = "0.32.9" version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"wayland-backend",
"wayland-client",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols"
version = "0.32.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a"
dependencies = [
"bitflags 2.9.0",
"wayland-backend", "wayland-backend",
"wayland-client", "wayland-client",
"wayland-scanner", "wayland-scanner",
@ -4021,22 +4000,35 @@ dependencies = [
[[package]] [[package]]
name = "wayland-protocols-wlr" name = "wayland-protocols-wlr"
version = "0.3.9" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
"wayland-backend", "wayland-backend",
"wayland-client", "wayland-client",
"wayland-protocols", "wayland-protocols 0.31.2",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols-wlr"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf"
dependencies = [
"bitflags 2.9.0",
"wayland-backend",
"wayland-client",
"wayland-protocols 0.32.8",
"wayland-scanner", "wayland-scanner",
] ]
[[package]] [[package]]
name = "wayland-scanner" name = "wayland-scanner"
version = "0.31.7" version = "0.31.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quick-xml", "quick-xml",
@ -4045,9 +4037,9 @@ dependencies = [
[[package]] [[package]]
name = "wayland-sys" name = "wayland-sys"
version = "0.31.7" version = "0.31.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615"
dependencies = [ dependencies = [
"pkg-config", "pkg-config",
] ]
@ -4382,7 +4374,7 @@ version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.0",
] ]
[[package]] [[package]]
@ -4435,9 +4427,9 @@ dependencies = [
[[package]] [[package]]
name = "zbus" name = "zbus"
version = "5.9.0" version = "5.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bb4f9a464286d42851d18a605f7193b8febaf5b0919d71c6399b7b26e5b0aad" checksum = "d3a7c7cee313d044fca3f48fa782cb750c79e4ca76ba7bc7718cd4024cdf6f68"
dependencies = [ dependencies = [
"async-broadcast", "async-broadcast",
"async-recursion", "async-recursion",
@ -4463,9 +4455,9 @@ dependencies = [
[[package]] [[package]]
name = "zbus_macros" name = "zbus_macros"
version = "5.9.0" version = "5.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef9859f68ee0c4ee2e8cde84737c78e3f4c54f946f2a38645d0d4c7a95327659" checksum = "a17e7e5eec1550f747e71a058df81a9a83813ba0f6a95f39c4e218bdc7ba366a"
dependencies = [ dependencies = [
"proc-macro-crate 3.2.0", "proc-macro-crate 3.2.0",
"proc-macro2", "proc-macro2",

View file

@ -115,7 +115,7 @@ schema = ["dep:schemars"]
gtk = "0.18.2" gtk = "0.18.2"
gtk-layer-shell = "0.8.2" gtk-layer-shell = "0.8.2"
glib = "0.18.5" glib = "0.18.5"
tokio = { version = "1.47.1", features = [ tokio = { version = "1.46.1", features = [
"macros", "macros",
"rt-multi-thread", "rt-multi-thread",
"time", "time",
@ -132,13 +132,13 @@ tracing-appender = "0.2.3"
strip-ansi-escapes = "0.2.0" strip-ansi-escapes = "0.2.0"
color-eyre = "0.6.5" color-eyre = "0.6.5"
serde = { version = "1.0.219", features = ["derive"] } serde = { version = "1.0.219", features = ["derive"] }
indexmap = { version = "2.10.0", features = ["serde"] } indexmap = "2.10.0"
dirs = "6.0.0" dirs = "6.0.0"
walkdir = "2.5.0" walkdir = "2.5.0"
notify = { version = "8.2.0", default-features = false } notify = { version = "8.1.0", default-features = false }
wayland-client = "0.31.1" wayland-client = "0.31.1"
wayland-protocols-wlr = { version = "0.3.9", features = ["client"] } wayland-protocols-wlr = { version = "0.3.8", features = ["client"] }
smithay-client-toolkit = { version = "0.19.2", default-features = false, features = [ smithay-client-toolkit = { version = "0.18.1", default-features = false, features = [
"calloop", "calloop",
] } ] }
universal-config = { version = "0.5.1", default-features = false } universal-config = { version = "0.5.1", default-features = false }
@ -146,14 +146,14 @@ ctrlc = "3.4.7"
cfg-if = "1.0.1" cfg-if = "1.0.1"
# cli # cli
clap = { version = "4.5.42", optional = true, features = ["derive"] } clap = { version = "4.5.40", optional = true, features = ["derive"] }
# http # http
reqwest = { version = "0.12.22", default-features = false, features = ["default-tls", "http2"], optional = true } reqwest = { version = "0.12.22", default-features = false, features = ["default-tls", "http2"], optional = true }
# cairo # cairo
lua-src = { version = "548.1.1", optional = true } lua-src = { version = "548.1.1", optional = true }
mlua = { version = "0.11.1", optional = true, features = ["luajit", "send"] } mlua = { version = "0.10.5", optional = true, features = ["luajit", "send"] }
cairo-rs = { version = "0.18.5", optional = true, features = ["png"] } cairo-rs = { version = "0.18.5", optional = true, features = ["png"] }
# clock # clock
@ -161,7 +161,7 @@ chrono = { version = "0.4.41", optional = true, default-features = false, featur
# keyboard # keyboard
colpetto = { version = "0.6.0", features = ["tokio", "tracing"], optional = true } colpetto = { version = "0.6.0", features = ["tokio", "tracing"], optional = true }
evdev-rs = { version = "0.6.2", optional = true } evdev-rs = { version = "0.6.1", optional = true }
# music # music
mpd-utils = { version = "0.2.1", optional = true } mpd-utils = { version = "0.2.1", optional = true }
@ -174,28 +174,27 @@ regex = { version = "1.11.1", default-features = false, features = [
tokio-stream = { version = "0.1.17", optional = true } tokio-stream = { version = "0.1.17", optional = true }
# sys_info # sys_info
sysinfo = { version = "0.36.1", optional = true } sysinfo = { version = "0.35.2", optional = true }
# tray # tray
system-tray = { version = "0.8.1", features = ["dbusmenu-gtk3"], optional = true } system-tray = { version = "0.7.0", features = ["dbusmenu-gtk3"], optional = true }
# volume # volume
libpulse-binding = { version = "2.30.1", optional = true } libpulse-binding = { version = "2.30.1", optional = true }
# shared # shared
futures-lite = { version = "2.6.0", optional = true } # network_manager, upower, workspaces, keyboard futures-lite = { version = "2.6.0", optional = true } # network_manager, upower, workspaces, keyboard
zbus = { version = "5.9.0", default-features = false, features = ["tokio"], optional = true } # network_manager, notifications, upower zbus = { version = "5.7.1", default-features = false, features = ["blocking-api", "tokio"], optional = true } # network_manager, notifications, upower
swayipc-async = { version = "2.1.0", optional = true } # workspaces, keyboard swayipc-async = { version = "2.1.0", optional = true } # workspaces, keyboard
hyprland = { version = "0.4.0-beta.2", optional = true } # workspaces, keyboard hyprland = { version = "0.4.0-beta.2", optional = true } # workspaces, keyboard
rustix = { version = "1.0.8", default-features = false, features = ["std", "fs", "pipe", "event"], optional = true } # clipboard, input rustix = { version = "1.0.7", default-features = false, features = ["std", "fs", "pipe", "event"], optional = true } # clipboard, input
serde_json = { version = "1.0.142", optional = true } # ipc, niri serde_json = { version = "1.0.140", optional = true } # ipc, niri
# schema # schema
schemars = { version = "0.8.22", optional = true }
schemars = { version = "1.0.4", optional = true, features = ["indexmap2"] }
[build-dependencies] [build-dependencies]
clap = { version = "4.5.42", features = ["derive"] } clap = { version = "4.5.40", features = ["derive"] }
clap_complete = "4.5.55" clap_complete = "4.5.54"
serde = { version = "1.0.219", features = ["derive"] } serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.142" serde_json = "1.0.140"

View file

@ -36,10 +36,12 @@ The server listens on a Unix socket.
The path is printed on startup, and can usually be found at `/run/user/$UID/ironbar-ipc.sock`. The path is printed on startup, and can usually be found at `/run/user/$UID/ironbar-ipc.sock`.
Commands and responses are sent as JSON objects. Commands and responses are sent as JSON objects.
The JSON should be minified and must NOT contain any `\n` characters.
Commands will have a `command` key, and a `subcommand` key when part of a sub-command. Commands will have a `command` key, and a `subcommand` key when part of a sub-command.
The message buffer is currently limited to `1024` bytes.
Particularly large messages will be truncated or cause an error.
The full spec can be found below. The full spec can be found below.
## Libraries ## Libraries

View file

@ -25,7 +25,7 @@ Displays the toggle state of the capslock, num lock and scroll lock keys, and th
| `icons.num_off` | `string` or [image](images) | `''` | Icon to show for disabled num lock indicator. | | `icons.num_off` | `string` or [image](images) | `''` | Icon to show for disabled num lock indicator. |
| `icons.scroll_on` | `string` or [image](images) | `` | Icon to show for enabled scroll lock indicator. | | `icons.scroll_on` | `string` or [image](images) | `` | Icon to show for enabled scroll lock indicator. |
| `icons.scroll_off` | `string` or [image](images) | `''` | Icon to show for disabled scroll lock indicator. | | `icons.scroll_off` | `string` or [image](images) | `''` | Icon to show for disabled scroll lock indicator. |
| `icons.layout_map` | `Map<string, string or image>` | `{}` | Map of icons or labels to show for a particular keyboard layout. Layouts use their actual name if not present in the map. Layouts are matched in the order they appear in the map. If a pattern to match ends with a `*`, it acts as a wildcard, matching any layout name that begins with the part before the `*`. | | `icons.layout_map` | `Map<string, string or image>` | `{}` | Map of icons or labels to show for a particular keyboard layout. Layouts use their actual name if not present in the map. |
| `seat` | `string` | `seat0` | ID of the Wayland seat to attach to. | | `seat` | `string` | `seat0` | ID of the Wayland seat to attach to. |
<details> <details>

View file

@ -195,9 +195,9 @@ The list of available functions is shown below:
It is also possible to get only a single value from the set by specifying a name instead of a function. It is also possible to get only a single value from the set by specifying a name instead of a function.
| Token category | Valid name | | Token category | Valid name |
|----------------|------------------------------------------| |----------------|-------------------------------------------------------------------------|
| CPU | A CPU thread, eg `cpu0`, `cpu1`, ... | | CPU | A CPU thread, eg `cpu0`, `cpu1`, ... |
| Temperature | A sensor name, eg `CPUTIN`. | | Temperature | A sensor name, eg `CPUTIN`. These line up with the output of `sensors`. |
| Disk | A disk mountpoint, eg `/`, `/home`, ... | | Disk | A disk mountpoint, eg `/`, `/home`, ... |
| Network | An adapter name, eg `eth0` or `enp30s0`. | | Network | An adapter name, eg `eth0` or `enp30s0`. |
@ -214,22 +214,6 @@ To show total CPU utilization where each core represents 100% (like `htop` etc):
"{cpu_percent@sum}%" "{cpu_percent@sum}%"
``` ```
> [!TIP]
> Available values can be queried over IPC using the CLI.
> This can be particularly useful for sensors, which tend not to have obvious names.
>
> ```shell
> ironbar var list sysinfo.temp_c
> ```
>
> Some usual cases to look out for:
>
> - `k10temp` is an AMD CPU internal sensor
> - Motherboard chipsets tend to prefix their sensors accordingly. For example, `CPUTIN`, `nct6687 CPU`, `asusec AMD`.
> - `amdgpu` is as it suggests.
>
> Sensor names are pulled from `hwmon` and should vaguely line up with the output of `sensors`
#### Prefixes and units #### Prefixes and units
For tokens which return an appropriate unit, you can specify the SI prefix (or unit in some special cases). For tokens which return an appropriate unit, you can specify the SI prefix (or unit in some special cases).

58
flake.lock generated
View file

@ -1,27 +1,5 @@
{ {
"nodes": { "nodes": {
"fenix": {
"inputs": {
"nixpkgs": [
"naersk",
"nixpkgs"
],
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1752475459,
"narHash": "sha256-z6QEu4ZFuHiqdOPbYss4/Q8B0BFhacR8ts6jO/F/aOU=",
"owner": "nix-community",
"repo": "fenix",
"rev": "bf0d6f70f4c9a9cf8845f992105652173f4b617f",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"flake-compat": { "flake-compat": {
"locked": { "locked": {
"lastModified": 1747046372, "lastModified": 1747046372,
@ -39,15 +17,14 @@
}, },
"naersk": { "naersk": {
"inputs": { "inputs": {
"fenix": "fenix",
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
}, },
"locked": { "locked": {
"lastModified": 1752689277, "lastModified": 1745925850,
"narHash": "sha256-uldUBFkZe/E7qbvxa3mH1ItrWZyT6w1dBKJQF/3ZSsc=", "narHash": "sha256-cyAAMal0aPrlb1NgzMxZqeN1mAJ2pJseDhm2m6Um8T0=",
"owner": "nix-community", "owner": "nix-community",
"repo": "naersk", "repo": "naersk",
"rev": "0e72363d0938b0208d6c646d10649164c43f4d64", "rev": "38bc60bbc157ae266d4a0c96671c6c742ee17a5f",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -73,11 +50,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1752077645, "lastModified": 1751251929,
"narHash": "sha256-HM791ZQtXV93xtCY+ZxG1REzhQenSQO020cu6rHtAPk=", "narHash": "sha256-IJWIzZSkBsDzS7iS/iwSwur+xFkWqeLYC4kdf8ObtOM=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "be9e214982e20b8310878ac2baa063a961c1bdf6", "rev": "b95255df2360a45ddbb03817a68869d5cb01bf96",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -89,11 +66,11 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1753694789, "lastModified": 1751011381,
"narHash": "sha256-cKgvtz6fKuK1Xr5LQW/zOUiAC0oSQoA9nOISB0pJZqM=", "narHash": "sha256-krGXKxvkBhnrSC/kGBmg5MyupUUT5R6IBCLEzx9jhMM=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "dc9637876d0dcc8c9e5e22986b857632effeb727", "rev": "30e2e2857ba47844aa71991daa6ed1fc678bcbb7",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -110,23 +87,6 @@
"nix-systems": "nix-systems", "nix-systems": "nix-systems",
"nixpkgs": "nixpkgs_2" "nixpkgs": "nixpkgs_2"
} }
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1752428706,
"narHash": "sha256-EJcdxw3aXfP8Ex1Nm3s0awyH9egQvB2Gu+QEnJn2Sfg=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "591e3b7624be97e4443ea7b5542c191311aa141d",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
} }
}, },
"root": "root", "root": "root",

View file

@ -44,7 +44,7 @@ impl Client {
spawn(watch_device(added_device.to_owned(), tx.clone())); spawn(watch_device(added_device.to_owned(), tx.clone()));
} }
let _removed_devices = devices.difference(&new_devices); let removed_devices = devices.difference(&new_devices);
// TODO: Cook up some way to notify closures for removed devices to exit // TODO: Cook up some way to notify closures for removed devices to exit
devices = new_devices; devices = new_devices;

View file

@ -0,0 +1,165 @@
use crate::clients::networkmanager::dbus::{
ActiveConnectionDbusProxy, DeviceDbusProxy, DeviceState, DeviceType,
};
use color_eyre::Result;
use std::collections::HashMap;
use zbus::zvariant::ObjectPath;
type PathMap<'l, ValueType> = HashMap<ObjectPath<'l>, ValueType>;
#[derive(Clone, Debug)]
pub struct State {
pub wired: WiredState,
pub wifi: WifiState,
pub cellular: CellularState,
pub vpn: VpnState,
}
#[derive(Clone, Debug)]
pub enum WiredState {
Connected,
Disconnected,
NotPresent,
Unknown,
}
#[derive(Clone, Debug)]
pub enum WifiState {
Connected(WifiConnectedState),
Disconnected,
Disabled,
NotPresent,
Unknown,
}
#[derive(Clone, Debug)]
pub struct WifiConnectedState {
pub ssid: String,
}
#[derive(Clone, Debug)]
pub enum CellularState {
Connected,
Disconnected,
Disabled,
NotPresent,
Unknown,
}
#[derive(Clone, Debug)]
pub enum VpnState {
Connected(VpnConnectedState),
Disconnected,
Unknown,
}
#[derive(Clone, Debug)]
pub struct VpnConnectedState {
pub name: String,
}
pub(super) async fn determine_wired_state(
devices: &PathMap<'_, DeviceDbusProxy<'_>>,
) -> Result<WiredState> {
let mut present = false;
let mut connected = false;
for device in devices.values() {
if device.device_type().await? == DeviceType::Ethernet {
present = true;
if device.state().await?.is_enabled() {
connected = true;
break;
}
}
}
if connected {
Ok(WiredState::Connected)
} else if present {
Ok(WiredState::Disconnected)
} else {
Ok(WiredState::NotPresent)
}
}
pub(super) async fn determine_wifi_state(
devices: &PathMap<'_, DeviceDbusProxy<'_>>,
) -> Result<WifiState> {
let mut present = false;
let mut enabled = false;
let mut connected = false;
for device in devices.values() {
if device.device_type().await? == DeviceType::Wifi {
present = true;
if device.state().await?.is_enabled() {
enabled = true;
if device.state().await? == DeviceState::Activated {
connected = true;
break;
}
}
}
}
if connected {
Ok(WifiState::Connected(WifiConnectedState {
// TODO: Implement obtaining SSID
ssid: "unknown".into(),
}))
} else if enabled {
Ok(WifiState::Disconnected)
} else if present {
Ok(WifiState::Disabled)
} else {
Ok(WifiState::NotPresent)
}
}
pub(super) async fn determine_cellular_state(
devices: &PathMap<'_, DeviceDbusProxy<'_>>,
) -> Result<CellularState> {
let mut present = false;
let mut enabled = false;
let mut connected = false;
for device in devices.values() {
if device.device_type().await? == DeviceType::Modem {
present = true;
if device.state().await?.is_enabled() {
enabled = true;
if device.state().await? == DeviceState::Activated {
connected = true;
break;
}
}
}
}
if connected {
Ok(CellularState::Connected)
} else if enabled {
Ok(CellularState::Disconnected)
} else if present {
Ok(CellularState::Disabled)
} else {
Ok(CellularState::NotPresent)
}
}
pub(super) async fn determine_vpn_state(
active_connections: &PathMap<'_, ActiveConnectionDbusProxy<'_>>,
) -> Result<VpnState> {
for connection in active_connections.values() {
match connection.type_().await?.as_str() {
"vpn" | "wireguard" => {
return Ok(VpnState::Connected(VpnConnectedState {
name: "unknown".into(),
}));
}
_ => {}
}
}
Ok(VpnState::Disconnected)
}

View file

@ -3,7 +3,7 @@ mod sink_input;
use crate::{APP_ID, arc_mut, lock, register_client, spawn_blocking}; use crate::{APP_ID, arc_mut, lock, register_client, spawn_blocking};
use libpulse_binding::callbacks::ListResult; use libpulse_binding::callbacks::ListResult;
use libpulse_binding::context::introspect::ServerInfo; use libpulse_binding::context::introspect::{Introspector, ServerInfo};
use libpulse_binding::context::subscribe::{Facility, InterestMaskSet, Operation}; use libpulse_binding::context::subscribe::{Facility, InterestMaskSet, Operation};
use libpulse_binding::context::{Context, FlagSet, State}; use libpulse_binding::context::{Context, FlagSet, State};
use libpulse_binding::mainloop::standard::{IterateResult, Mainloop}; use libpulse_binding::mainloop::standard::{IterateResult, Mainloop};
@ -24,11 +24,11 @@ type ArcMutVec<T> = Arc<Mutex<Vec<T>>>;
pub enum Event { pub enum Event {
AddSink(Sink), AddSink(Sink),
UpdateSink(Sink), UpdateSink(Sink),
RemoveSink, RemoveSink(String),
AddInput, AddInput(SinkInput),
UpdateInput, UpdateInput(SinkInput),
RemoveInput, RemoveInput(u32),
} }
#[derive(Debug)] #[derive(Debug)]
@ -51,7 +51,10 @@ struct Data {
pub enum ConnectionState { pub enum ConnectionState {
Disconnected, Disconnected,
Connected, Connected {
context: Arc<Mutex<Context>>,
introspector: Introspector,
},
} }
impl Debug for ConnectionState { impl Debug for ConnectionState {
@ -61,7 +64,7 @@ impl Debug for ConnectionState {
"{}", "{}",
match self { match self {
Self::Disconnected => "Disconnected", Self::Disconnected => "Disconnected",
Self::Connected => "Connected", Self::Connected { .. } => "Connected",
} }
) )
} }
@ -117,9 +120,14 @@ impl Client {
error!("{err:?}"); error!("{err:?}");
} }
let introspector = lock!(context).introspect();
{ {
let mut inner = lock!(self.connection); let mut inner = lock!(self.connection);
*inner = ConnectionState::Connected; *inner = ConnectionState::Connected {
context,
introspector,
};
} }
loop { loop {
@ -283,4 +291,22 @@ fn volume_to_percent(volume: ChannelVolumes) -> f64 {
((avg - Volume::MUTED.0) as f64 / base_delta).round() ((avg - Volume::MUTED.0) as f64 / base_delta).round()
} }
/// Converts a percentage volume into a Pulse volume value,
/// which can be used for setting channel volumes.
pub fn percent_to_volume(target_percent: f64) -> u32 {
let base_delta = (Volume::NORMAL.0 as f32 - Volume::MUTED.0 as f32) / 100.0;
if target_percent < 0.0 {
Volume::MUTED.0
} else if target_percent == 100.0 {
Volume::NORMAL.0
} else if target_percent >= 150.0 {
(Volume::NORMAL.0 as f32 * 1.5) as u32
} else if target_percent < 100.0 {
Volume::MUTED.0 + target_percent as u32 * base_delta as u32
} else {
Volume::NORMAL.0 + (target_percent - 100.0) as u32 * base_delta as u32
}
}
register_client!(Client, volume); register_client!(Client, volume);

View file

@ -1,4 +1,4 @@
use super::{ArcMutVec, Client, Event, volume_to_percent}; use super::{ArcMutVec, Client, ConnectionState, Event, percent_to_volume, volume_to_percent};
use crate::channels::SyncSenderExt; use crate::channels::SyncSenderExt;
use crate::lock; use crate::lock;
use libpulse_binding::callbacks::ListResult; use libpulse_binding::callbacks::ListResult;
@ -6,7 +6,7 @@ use libpulse_binding::context::Context;
use libpulse_binding::context::introspect::SinkInfo; use libpulse_binding::context::introspect::SinkInfo;
use libpulse_binding::context::subscribe::Operation; use libpulse_binding::context::subscribe::Operation;
use libpulse_binding::def::SinkState; use libpulse_binding::def::SinkState;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex, mpsc};
use tokio::sync::broadcast; use tokio::sync::broadcast;
use tracing::{debug, error, instrument, trace}; use tracing::{debug, error, instrument, trace};
@ -14,6 +14,7 @@ use tracing::{debug, error, instrument, trace};
pub struct Sink { pub struct Sink {
index: u32, index: u32,
pub name: String, pub name: String,
pub description: String,
pub volume: f64, pub volume: f64,
pub muted: bool, pub muted: bool,
pub active: bool, pub active: bool,
@ -28,6 +29,11 @@ impl From<&SinkInfo<'_>> for Sink {
.as_ref() .as_ref()
.map(ToString::to_string) .map(ToString::to_string)
.unwrap_or_default(), .unwrap_or_default(),
description: value
.description
.as_ref()
.map(ToString::to_string)
.unwrap_or_default(),
muted: value.mute, muted: value.mute,
volume: volume_to_percent(value.volume), volume: volume_to_percent(value.volume),
active: value.state == SinkState::Running, active: value.state == SinkState::Running,
@ -40,6 +46,43 @@ impl Client {
pub fn sinks(&self) -> Arc<Mutex<Vec<Sink>>> { pub fn sinks(&self) -> Arc<Mutex<Vec<Sink>>> {
self.data.sinks.clone() self.data.sinks.clone()
} }
#[instrument(level = "trace")]
pub fn set_default_sink(&self, name: &str) {
if let ConnectionState::Connected { context, .. } = &*lock!(self.connection) {
lock!(context).set_default_sink(name, |_| {});
}
}
#[instrument(level = "trace")]
pub fn set_sink_volume(&self, name: &str, volume_percent: f64) {
if let ConnectionState::Connected { introspector, .. } = &mut *lock!(self.connection) {
let (tx, rx) = mpsc::channel();
introspector.get_sink_info_by_name(name, move |info| {
let ListResult::Item(info) = info else {
return;
};
tx.send_expect(info.volume);
});
let new_volume = percent_to_volume(volume_percent);
let mut volume = rx.recv().expect("to receive info");
for v in volume.get_mut() {
v.0 = new_volume;
}
introspector.set_sink_volume_by_name(name, &volume, None);
}
}
#[instrument(level = "trace")]
pub fn set_sink_muted(&self, name: &str, muted: bool) {
if let ConnectionState::Connected { introspector, .. } = &mut *lock!(self.connection) {
introspector.set_sink_mute_by_name(name, muted, None);
}
}
} }
pub fn on_event( pub fn on_event(
@ -134,9 +177,10 @@ fn update(
fn remove(index: u32, sinks: &ArcMutVec<Sink>, tx: &broadcast::Sender<Event>) { fn remove(index: u32, sinks: &ArcMutVec<Sink>, tx: &broadcast::Sender<Event>) {
trace!("removing {index}"); trace!("removing {index}");
let sinks = lock!(sinks); let mut sinks = lock!(sinks);
if let Some(_pos) = sinks.iter().position(|s| s.index == index) { if let Some(pos) = sinks.iter().position(|s| s.index == index) {
tx.send_expect(Event::RemoveSink); let info = sinks.remove(pos);
tx.send_expect(Event::RemoveSink(info.name));
} }
} }

View file

@ -1,22 +1,37 @@
use super::{ArcMutVec, Client, Event}; use super::{ArcMutVec, Client, ConnectionState, Event, percent_to_volume, volume_to_percent};
use crate::channels::SyncSenderExt; use crate::channels::SyncSenderExt;
use crate::lock; use crate::lock;
use libpulse_binding::callbacks::ListResult; use libpulse_binding::callbacks::ListResult;
use libpulse_binding::context::Context; use libpulse_binding::context::Context;
use libpulse_binding::context::introspect::SinkInputInfo; use libpulse_binding::context::introspect::SinkInputInfo;
use libpulse_binding::context::subscribe::Operation; use libpulse_binding::context::subscribe::Operation;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex, mpsc};
use tokio::sync::broadcast; use tokio::sync::broadcast;
use tracing::{debug, error, instrument, trace}; use tracing::{debug, error, instrument, trace};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SinkInput { pub struct SinkInput {
pub index: u32, pub index: u32,
pub name: String,
pub volume: f64,
pub muted: bool,
pub can_set_volume: bool,
} }
impl From<&SinkInputInfo<'_>> for SinkInput { impl From<&SinkInputInfo<'_>> for SinkInput {
fn from(value: &SinkInputInfo) -> Self { fn from(value: &SinkInputInfo) -> Self {
Self { index: value.index } Self {
index: value.index,
name: value
.name
.as_ref()
.map(ToString::to_string)
.unwrap_or_default(),
muted: value.mute,
volume: volume_to_percent(value.volume),
can_set_volume: value.has_volume && value.volume_writable,
}
} }
} }
@ -25,6 +40,36 @@ impl Client {
pub fn sink_inputs(&self) -> Arc<Mutex<Vec<SinkInput>>> { pub fn sink_inputs(&self) -> Arc<Mutex<Vec<SinkInput>>> {
self.data.sink_inputs.clone() self.data.sink_inputs.clone()
} }
#[instrument(level = "trace")]
pub fn set_input_volume(&self, index: u32, volume_percent: f64) {
if let ConnectionState::Connected { introspector, .. } = &mut *lock!(self.connection) {
let (tx, rx) = mpsc::channel();
introspector.get_sink_input_info(index, move |info| {
let ListResult::Item(info) = info else {
return;
};
tx.send_expect(info.volume);
});
let new_volume = percent_to_volume(volume_percent);
let mut volume = rx.recv().expect("to receive info");
for v in volume.get_mut() {
v.0 = new_volume;
}
introspector.set_sink_input_volume(index, &volume, None);
}
}
#[instrument(level = "trace")]
pub fn set_input_muted(&self, index: u32, muted: bool) {
if let ConnectionState::Connected { introspector, .. } = &mut *lock!(self.connection) {
introspector.set_sink_input_mute(index, muted, None);
}
}
} }
pub fn on_event( pub fn on_event(
@ -74,7 +119,7 @@ pub fn add(
trace!("adding {info:?}"); trace!("adding {info:?}");
lock!(inputs).push(info.into()); lock!(inputs).push(info.into());
tx.send_expect(Event::AddInput); tx.send_expect(Event::AddInput(info.into()));
} }
fn update( fn update(
@ -98,15 +143,16 @@ fn update(
inputs[pos] = info.into(); inputs[pos] = info.into();
} }
tx.send_expect(Event::UpdateInput); tx.send_expect(Event::UpdateInput(info.into()));
} }
fn remove(index: u32, inputs: &ArcMutVec<SinkInput>, tx: &broadcast::Sender<Event>) { fn remove(index: u32, inputs: &ArcMutVec<SinkInput>, tx: &broadcast::Sender<Event>) {
let inputs = lock!(inputs); let mut inputs = lock!(inputs);
trace!("removing {index}"); trace!("removing {index}");
if let Some(_pos) = inputs.iter().position(|s| s.index == index) { if let Some(pos) = inputs.iter().position(|s| s.index == index) {
tx.send_expect(Event::RemoveInput); let info = inputs.remove(pos);
tx.send_expect(Event::RemoveInput(info.index));
} }
} }

View file

@ -52,11 +52,16 @@ where
} }
#[cfg(feature = "schema")] #[cfg(feature = "schema")]
pub fn schema_layer(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema { pub fn schema_layer(generator: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
schemars::json_schema!({ use schemars::JsonSchema;
"type": "string", let mut schema: schemars::schema::SchemaObject = <String>::json_schema(generator).into();
"enum": ["background", "bottom", "top", "overlay"], schema.enum_values = Some(vec![
}) "background".into(),
"bottom".into(),
"top".into(),
"overlay".into(),
]);
schema.into()
} }
impl BarPosition { impl BarPosition {

View file

@ -3,10 +3,9 @@ use color_eyre::{Help, Report, Result};
use std::collections::HashMap; use std::collections::HashMap;
use std::env; use std::env;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::Stdio; use std::process::{Command, Stdio};
use std::sync::Arc; use std::sync::Arc;
use tokio::io::{AsyncBufReadExt, BufReader}; use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::process::Command;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tracing::{debug, error}; use tracing::{debug, error};
use walkdir::{DirEntry, WalkDir}; use walkdir::{DirEntry, WalkDir};
@ -239,7 +238,6 @@ impl DesktopFiles {
/// Checks file contents for an exact or partial match of the provided input. /// Checks file contents for an exact or partial match of the provided input.
async fn find_by_file_contents(&self, app_id: &str) -> Result<Option<DesktopFile>> { async fn find_by_file_contents(&self, app_id: &str) -> Result<Option<DesktopFile>> {
let mut files = self.files.lock().await; let mut files = self.files.lock().await;
let app_id_lower = app_id.to_lowercase();
// first pass - check name for exact match // first pass - check name for exact match
for (_, file_ref) in files.iter_mut() { for (_, file_ref) in files.iter_mut() {
@ -255,7 +253,7 @@ impl DesktopFiles {
for (_, file_ref) in files.iter_mut() { for (_, file_ref) in files.iter_mut() {
let file = file_ref.get().await?; let file = file_ref.get().await?;
if let Some(name) = &file.name { if let Some(name) = &file.name {
if name.to_lowercase().contains(&app_id_lower) { if name.to_lowercase().contains(app_id) {
return Ok(Some(file)); return Ok(Some(file));
} }
} }
@ -266,19 +264,19 @@ impl DesktopFiles {
let file = file_ref.get().await?; let file = file_ref.get().await?;
if let Some(name) = &file.exec { if let Some(name) = &file.exec {
if name.to_lowercase().contains(&app_id_lower) { if name.to_lowercase().contains(app_id) {
return Ok(Some(file)); return Ok(Some(file));
} }
} }
if let Some(name) = &file.startup_wm_class { if let Some(name) = &file.startup_wm_class {
if name.to_lowercase().contains(&app_id_lower) { if name.to_lowercase().contains(app_id) {
return Ok(Some(file)); return Ok(Some(file));
} }
} }
if let Some(name) = &file.icon { if let Some(name) = &file.icon {
if name.to_lowercase().contains(&app_id_lower) { if name.to_lowercase().contains(app_id) {
return Ok(Some(file)); return Ok(Some(file));
} }
} }
@ -327,37 +325,21 @@ fn files(dir: &Path) -> Vec<PathBuf> {
} }
/// Starts a `.desktop` file with the provided formatted command. /// Starts a `.desktop` file with the provided formatted command.
pub async fn open_program(file_name: &str, launch_command: &str) { pub fn open_program(file_name: &str, str: &str) {
let expanded = launch_command.replace("{app_name}", file_name); let expanded = str.replace("{app_name}", file_name);
let launch_command_parts: Vec<&str> = expanded.split_whitespace().collect(); let launch_command_parts: Vec<&str> = expanded.split_whitespace().collect();
if let Err(err) = Command::new(&launch_command_parts[0])
debug!("running {launch_command_parts:?}");
let exit_status = match Command::new(launch_command_parts[0])
.args(&launch_command_parts[1..]) .args(&launch_command_parts[1..])
.stdin(Stdio::null())
.stdout(Stdio::null()) .stdout(Stdio::null())
.stderr(Stdio::null()) .stderr(Stdio::null())
.kill_on_drop(true)
.spawn() .spawn()
{ {
Ok(mut child) => Some(child.wait().await),
Err(err) => {
error!( error!(
"{:?}", "{:?}",
Report::new(err) Report::new(err)
.wrap_err("Failed to run launch command.") .wrap_err("Failed to run launch command.")
.suggestion("Perhaps the desktop file is invalid or orphaned?") .suggestion("Perhaps the applications file is invalid?")
); );
None
}
};
match exit_status {
Some(Ok(exit_status)) if !exit_status.success() => {
error!("received non-success exit status running {launch_command_parts:?}")
}
Some(Err(err)) => error!("{err:?}"),
_ => {}
} }
} }

View file

@ -2,7 +2,7 @@ use super::Ipc;
use crate::ipc::{Command, Response}; use crate::ipc::{Command, Response};
use color_eyre::Result; use color_eyre::Result;
use color_eyre::{Help, Report}; use color_eyre::{Help, Report};
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::UnixStream; use tokio::net::UnixStream;
impl Ipc { impl Ipc {
@ -16,20 +16,18 @@ impl Ipc {
.suggestion("Is Ironbar running?")), .suggestion("Is Ironbar running?")),
}?; }?;
let mut write_buffer = serde_json::to_vec(&command)?; let write_buffer = serde_json::to_vec(&command)?;
if debug { if debug {
eprintln!("REQUEST JSON: {}", serde_json::to_string(&command)?); eprintln!("REQUEST JSON: {}", serde_json::to_string(&command)?);
} }
write_buffer.push(b'\n');
stream.write_all(&write_buffer).await?; stream.write_all(&write_buffer).await?;
let mut read_buffer = String::new(); let mut read_buffer = vec![0; 1024];
let mut reader = BufReader::new(stream); let bytes = stream.read(&mut read_buffer).await?;
let bytes = reader.read_line(&mut read_buffer).await?;
let response = serde_json::from_str(&read_buffer[..bytes])?; let response = serde_json::from_slice(&read_buffer[..bytes])?;
Ok(response) Ok(response)
} }
} }

View file

@ -8,10 +8,10 @@ use std::rc::Rc;
use color_eyre::{Report, Result}; use color_eyre::{Report, Result};
use gtk::Application; use gtk::Application;
use gtk::prelude::*; use gtk::prelude::*;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{UnixListener, UnixStream}; use tokio::net::{UnixListener, UnixStream};
use tokio::sync::mpsc::{self, Receiver, Sender}; use tokio::sync::mpsc::{self, Receiver, Sender};
use tracing::{debug, error, info, trace, warn}; use tracing::{debug, error, info, warn};
use super::Ipc; use super::Ipc;
use crate::channels::{AsyncSenderExt, MpscReceiverExt}; use crate::channels::{AsyncSenderExt, MpscReceiverExt};
@ -52,13 +52,11 @@ impl Ipc {
loop { loop {
match listener.accept().await { match listener.accept().await {
Ok((stream, _addr)) => { Ok((stream, _addr)) => {
debug!("handling incoming connection");
if let Err(err) = if let Err(err) =
Self::handle_connection(stream, &cmd_tx, &mut res_rx).await Self::handle_connection(stream, &cmd_tx, &mut res_rx).await
{ {
error!("{err:?}"); error!("{err:?}");
} }
debug!("done");
} }
Err(err) => { Err(err) => {
error!("{err:?}"); error!("{err:?}");
@ -82,16 +80,10 @@ impl Ipc {
cmd_tx: &Sender<Command>, cmd_tx: &Sender<Command>,
res_rx: &mut Receiver<Response>, res_rx: &mut Receiver<Response>,
) -> Result<()> { ) -> Result<()> {
trace!("awaiting readable state"); let (mut stream_read, mut stream_write) = stream.split();
stream.readable().await?;
let mut read_buffer = Vec::with_capacity(1024); let mut read_buffer = vec![0; 1024];
let bytes = stream_read.read(&mut read_buffer).await?;
let mut reader = BufReader::new(&mut stream);
trace!("reading bytes");
let bytes = reader.read_until(b'\n', &mut read_buffer).await?;
debug!("read {} bytes", bytes);
// FIXME: Error on invalid command // FIXME: Error on invalid command
let command = serde_json::from_slice::<Command>(&read_buffer[..bytes])?; let command = serde_json::from_slice::<Command>(&read_buffer[..bytes])?;
@ -103,18 +95,10 @@ impl Ipc {
.recv() .recv()
.await .await
.unwrap_or(Response::Err { message: None }); .unwrap_or(Response::Err { message: None });
let res = serde_json::to_vec(&res)?;
let mut res = serde_json::to_vec(&res)?; stream_write.write_all(&res).await?;
res.push(b'\n'); stream_write.shutdown().await?;
trace!("awaiting writable state");
stream.writable().await?;
debug!("writing {} bytes", res.len());
stream.write_all(&res).await?;
trace!("bytes written, shutting down stream");
stream.shutdown().await?;
Ok(()) Ok(())
} }

View file

@ -1,7 +1,8 @@
use std::collections::HashMap;
use color_eyre::Result; use color_eyre::Result;
use color_eyre::eyre::Report; use color_eyre::eyre::Report;
use gtk::prelude::*; use gtk::prelude::*;
use indexmap::IndexMap;
use serde::Deserialize; use serde::Deserialize;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tracing::{debug, trace}; use tracing::{debug, trace};
@ -128,7 +129,7 @@ struct Icons {
/// } /// }
/// ``` /// ```
#[serde(default)] #[serde(default)]
layout_map: IndexMap<String, String>, layout_map: HashMap<String, String>,
} }
impl Default for Icons { impl Default for Icons {
@ -140,7 +141,7 @@ impl Default for Icons {
num_off: String::new(), num_off: String::new(),
scroll_on: default_icon_scroll(), scroll_on: default_icon_scroll(),
scroll_off: String::new(), scroll_off: String::new(),
layout_map: IndexMap::new(), layout_map: HashMap::new(),
} }
} }
} }
@ -337,19 +338,7 @@ impl Module<gtk::Box> for KeyboardModule {
} }
} }
KeyboardUpdate::Layout(KeyboardLayoutUpdate(language)) => { KeyboardUpdate::Layout(KeyboardLayoutUpdate(language)) => {
let text = icons let text = icons.layout_map.get(&language).unwrap_or(&language);
.layout_map
.iter()
.find_map(|(pattern, display_text)| {
let is_match = if pattern.ends_with("*") {
language.starts_with(&pattern[..pattern.len() - 1])
} else {
pattern == &language
};
is_match.then(|| display_text)
})
.unwrap_or(&language);
layout_button.set_label(text); layout_button.set_label(text);
} }
}); });

View file

@ -244,43 +244,21 @@ impl Module<gtk::Box> for LauncherModule {
let tx2 = context.tx.clone(); let tx2 = context.tx.clone();
let wl = context.client::<wayland::Client>(); let wl = context.client::<wayland::Client>();
let desktop_files = context.ironbar.desktop_files();
spawn(async move { spawn(async move {
let items = items2; let items = items2;
let tx = tx2; let tx = tx2;
// Build app_id mapping once at startup
let mut app_id_map = IndexMap::<String, String>::new();
{
let favorites: Vec<_> = lock!(items).keys().cloned().collect();
for fav in favorites {
if let Ok(Some(file)) = desktop_files.find(&fav).await {
if let Some(wm_class) = file.startup_wm_class {
app_id_map.insert(wm_class, fav);
}
}
}
}
let resolve_app_id = |app_id: &str| {
app_id_map
.get(app_id)
.cloned()
.unwrap_or_else(|| app_id.to_string())
};
let mut wlrx = wl.subscribe_toplevels(); let mut wlrx = wl.subscribe_toplevels();
let handles = wl.toplevel_info_all(); let handles = wl.toplevel_info_all();
for info in handles { for info in handles {
let mut items = lock!(items); let mut items = lock!(items);
let app_id = resolve_app_id(&info.app_id); let item = items.get_mut(&info.app_id);
if let Some(item) = items.get_mut(&app_id) { if let Some(item) = item {
item.merge_toplevel(info.clone()); item.merge_toplevel(info.clone());
} else { } else {
let mut item = Item::from(info.clone()); let item = Item::from(info.clone());
item.app_id = app_id.clone(); items.insert(info.app_id.clone(), item);
items.insert(app_id, item);
} }
} }
@ -306,14 +284,14 @@ impl Module<gtk::Box> for LauncherModule {
match event { match event {
ToplevelEvent::New(info) => { ToplevelEvent::New(info) => {
let app_id = resolve_app_id(&info.app_id); let app_id = info.app_id.clone();
let new_item = { let new_item = {
let mut items = lock!(items); let mut items = lock!(items);
match items.get_mut(&app_id) { let item = items.get_mut(&info.app_id);
match item {
None => { None => {
let mut item: Item = info.into(); let item: Item = info.into();
item.app_id = app_id.clone();
items.insert(app_id.clone(), item.clone()); items.insert(app_id.clone(), item.clone());
ItemOrWindow::Item(item) ItemOrWindow::Item(item)
@ -335,10 +313,9 @@ impl Module<gtk::Box> for LauncherModule {
}?; }?;
} }
ToplevelEvent::Update(info) => { ToplevelEvent::Update(info) => {
let app_id = resolve_app_id(&info.app_id);
// check if open, as updates can be sent as program closes // check if open, as updates can be sent as program closes
// if it's a focused favourite closing, it otherwise incorrectly re-focuses. // if it's a focused favourite closing, it otherwise incorrectly re-focuses.
let is_open = if let Some(item) = lock!(items).get_mut(&app_id) { let is_open = if let Some(item) = lock!(items).get_mut(&info.app_id) {
item.set_window_focused(info.id, info.focused); item.set_window_focused(info.id, info.focused);
item.set_window_name(info.id, info.title.clone()); item.set_window_name(info.id, info.title.clone());
@ -348,27 +325,27 @@ impl Module<gtk::Box> for LauncherModule {
}; };
send_update(LauncherUpdate::Focus( send_update(LauncherUpdate::Focus(
app_id.clone(), info.app_id.clone(),
is_open && info.focused, is_open && info.focused,
)) ))
.await?; .await?;
send_update(LauncherUpdate::Title( send_update(LauncherUpdate::Title(
app_id.clone(), info.app_id.clone(),
info.id, info.id,
info.title.clone(), info.title.clone(),
)) ))
.await?; .await?;
} }
ToplevelEvent::Remove(info) => { ToplevelEvent::Remove(info) => {
let app_id = resolve_app_id(&info.app_id);
let remove_item = { let remove_item = {
let mut items = lock!(items); let mut items = lock!(items);
match items.get_mut(&app_id) { let item = items.get_mut(&info.app_id);
match item {
Some(item) => { Some(item) => {
item.unmerge_toplevel(&info); item.unmerge_toplevel(&info);
if item.windows.is_empty() { if item.windows.is_empty() {
items.shift_remove(&app_id); items.shift_remove(&info.app_id);
Some(ItemOrWindowId::Item) Some(ItemOrWindowId::Item)
} else { } else {
Some(ItemOrWindowId::Window) Some(ItemOrWindowId::Window)
@ -380,10 +357,14 @@ impl Module<gtk::Box> for LauncherModule {
match remove_item { match remove_item {
Some(ItemOrWindowId::Item) => { Some(ItemOrWindowId::Item) => {
send_update(LauncherUpdate::RemoveItem(app_id.clone())).await?; send_update(LauncherUpdate::RemoveItem(info.app_id.clone()))
.await?;
} }
Some(ItemOrWindowId::Window) => { Some(ItemOrWindowId::Window) => {
send_update(LauncherUpdate::RemoveWindow(app_id.clone(), info.id)) send_update(LauncherUpdate::RemoveWindow(
info.app_id.clone(),
info.id,
))
.await?; .await?;
} }
None => {} None => {}
@ -407,7 +388,7 @@ impl Module<gtk::Box> for LauncherModule {
if let ItemEvent::OpenItem(app_id) = event { if let ItemEvent::OpenItem(app_id) = event {
match desktop_files.find(&app_id).await { match desktop_files.find(&app_id).await {
Ok(Some(file)) => { Ok(Some(file)) => {
open_program(&file.file_name, &launch_command_str).await; open_program(&file.file_name, &launch_command_str);
} }
Ok(None) => warn!("Could not find applications file for {}", app_id), Ok(None) => warn!("Could not find applications file for {}", app_id),
Err(err) => error!("Failed to find parse file for {}: {}", app_id, err), Err(err) => error!("Failed to find parse file for {}: {}", app_id, err),

View file

@ -100,11 +100,7 @@ where
let tx = tx.clone(); let tx = tx.clone();
button.connect_clicked(move |_button| { button.connect_clicked(move |_button| {
// TODO: this needs refactoring to call open from the controller open_program(&file_name, &command);
let file_name = file_name.clone();
let command = command.clone();
spawn(async move { open_program(&file_name, &command).await });
sub_menu.hide(); sub_menu.hide();
tx.send_spawn(ModuleUpdateEvent::ClosePopup); tx.send_spawn(ModuleUpdateEvent::ClosePopup);

View file

@ -122,34 +122,38 @@ async fn handle_update_events(
fn get_icon_for_device_state(r#type: &DeviceType, state: &DeviceState) -> Option<&'static str> { fn get_icon_for_device_state(r#type: &DeviceType, state: &DeviceState) -> Option<&'static str> {
match r#type { match r#type {
DeviceType::Ethernet => match state { DeviceType::Ethernet => match state {
DeviceState::Unavailable DeviceState::Unavailable => Some("icon:network-wired-disconnected-symbolic"),
| DeviceState::Disconnected DeviceState::Disconnected => Some("icon:network-wired-disconnected-symbolic"),
| DeviceState::Prepare DeviceState::Prepare => Some("icon:network-wired-disconnected-symbolic"),
| DeviceState::Config DeviceState::Config => Some("icon:network-wired-disconnected-symbolic"),
| DeviceState::NeedAuth DeviceState::NeedAuth => Some("icon:network-wired-disconnected-symbolic"),
| DeviceState::IpConfig DeviceState::IpConfig => Some("icon:network-wired-disconnected-symbolic"),
| DeviceState::IpCheck DeviceState::IpCheck => Some("icon:network-wired-disconnected-symbolic"),
| DeviceState::Secondaries DeviceState::Secondaries => Some("icon:network-wired-disconnected-symbolic"),
| DeviceState::Deactivating
| DeviceState::Failed => Some("icon:network-wired-disconnected-symbolic"),
DeviceState::Activated => Some("icon:network-wired-symbolic"), DeviceState::Activated => Some("icon:network-wired-symbolic"),
DeviceState::Deactivating => Some("icon:network-wired-disconnected-symbolic"),
DeviceState::Failed => Some("icon:network-wired-disconnected-symbolic"),
_ => None, _ => None,
}, },
DeviceType::Wifi => match state { DeviceType::Wifi => match state {
DeviceState::Unavailable => Some("icon:network-wireless-hardware-disabled-symbolic"), DeviceState::Unavailable => Some("icon:network-wireless-hardware-disabled-symbolic"),
DeviceState::Disconnected DeviceState::Disconnected => Some("icon:network-wireless-offline-symbolic"),
| DeviceState::Prepare DeviceState::Prepare => Some("icon:network-wireless-offline-symbolic"),
| DeviceState::Config DeviceState::Config => Some("icon:network-wireless-offline-symbolic"),
| DeviceState::NeedAuth DeviceState::NeedAuth => Some("icon:network-wireless-offline-symbolic"),
| DeviceState::IpConfig DeviceState::IpConfig => Some("icon:network-wireless-offline-symbolic"),
| DeviceState::IpCheck DeviceState::IpCheck => Some("icon:network-wireless-offline-symbolic"),
| DeviceState::Secondaries DeviceState::Secondaries => Some("icon:network-wireless-offline-symbolic"),
| DeviceState::Deactivating
| DeviceState::Failed => Some("icon:network-wireless-offline-symbolic"),
DeviceState::Activated => Some("icon:network-wireless-connected-symbolic"), DeviceState::Activated => Some("icon:network-wireless-connected-symbolic"),
DeviceState::Deactivating => Some("icon:network-wireless-offline-symbolic"),
DeviceState::Failed => Some("icon:network-wireless-offline-symbolic"),
_ => None, _ => None,
}, },
DeviceType::Tun | DeviceType::Wireguard => match state { DeviceType::Tun => match state {
DeviceState::Activated => Some("icon:network-vpn-symbolic"),
_ => None,
},
DeviceType::Wireguard => match state {
DeviceState::Activated => Some("icon:network-vpn-symbolic"), DeviceState::Activated => Some("icon:network-vpn-symbolic"),
_ => None, _ => None,
}, },

View file

@ -11,7 +11,6 @@ use std::collections::HashSet;
use std::ffi::CStr; use std::ffi::CStr;
use std::os::raw::{c_char, c_int}; use std::os::raw::{c_char, c_int};
use std::ptr; use std::ptr;
use system_tray::item::IconPixmap;
/// Gets the GTK icon theme search paths by calling the FFI function. /// Gets the GTK icon theme search paths by calling the FFI function.
/// Conveniently returns the result as a `HashSet`. /// Conveniently returns the result as a `HashSet`.
@ -46,10 +45,10 @@ pub fn get_image(
icon_theme: &IconTheme, icon_theme: &IconTheme,
) -> Result<Image> { ) -> Result<Image> {
if !prefer_icons && item.icon_pixmap.is_some() { if !prefer_icons && item.icon_pixmap.is_some() {
get_image_from_pixmap(item.icon_pixmap.as_deref(), size) get_image_from_pixmap(item, size)
} else { } else {
get_image_from_icon_name(item, size, icon_theme) get_image_from_icon_name(item, size, icon_theme)
.or_else(|_| get_image_from_pixmap(item.icon_pixmap.as_deref(), size)) .or_else(|_| get_image_from_pixmap(item, size))
} }
} }
@ -82,10 +81,12 @@ fn get_image_from_icon_name(item: &TrayMenu, size: u32, icon_theme: &IconTheme)
/// which has 8 bits per sample and a bit stride of `4*width`. /// which has 8 bits per sample and a bit stride of `4*width`.
/// The Pixbuf expects RGBA32 format, so some channel shuffling /// The Pixbuf expects RGBA32 format, so some channel shuffling
/// is required. /// is required.
fn get_image_from_pixmap(item: Option<&[IconPixmap]>, size: u32) -> Result<Image> { fn get_image_from_pixmap(item: &TrayMenu, size: u32) -> Result<Image> {
const BITS_PER_SAMPLE: i32 = 8; const BITS_PER_SAMPLE: i32 = 8;
let pixmap = item let pixmap = item
.icon_pixmap
.as_ref()
.and_then(|pixmap| pixmap.first()) .and_then(|pixmap| pixmap.first())
.ok_or_else(|| Report::msg("Failed to get pixmap from tray icon"))?; .ok_or_else(|| Report::msg("Failed to get pixmap from tray icon"))?;

View file

@ -181,14 +181,9 @@ fn on_update(
UpdateEvent::AttentionIcon(_icon) => { UpdateEvent::AttentionIcon(_icon) => {
warn!("received unimplemented NewAttentionIcon event"); warn!("received unimplemented NewAttentionIcon event");
} }
UpdateEvent::Icon { UpdateEvent::Icon(icon) => {
icon_name, if icon.as_ref() != menu_item.icon_name() {
icon_pixmap, menu_item.set_icon_name(icon);
} => {
menu_item.icon_pixmap = icon_pixmap;
if icon_name.as_ref() != menu_item.icon_name() {
menu_item.set_icon_name(icon_name);
match icon::get_image(menu_item, icon_size, prefer_icons, icon_theme) { match icon::get_image(menu_item, icon_size, prefer_icons, icon_theme) {
Ok(image) => menu_item.set_image(&image), Ok(image) => menu_item.set_image(&image),
Err(_) => menu_item.show_label(), Err(_) => menu_item.show_label(),

View file

@ -1,13 +1,13 @@
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt}; use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
use crate::clients::volume::{self, Event}; use crate::clients::volume::{self, Event};
use crate::config::CommonConfig; use crate::config::{CommonConfig, LayoutConfig, TruncateMode};
use crate::gtk_helpers::IronbarGtkExt; use crate::gtk_helpers::IronbarGtkExt;
use crate::modules::{ use crate::modules::{
Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, PopupButton, WidgetContext, Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, PopupButton, WidgetContext,
}; };
use crate::{lock, module_impl, spawn}; use crate::{lock, module_impl, spawn};
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Button, Image}; use gtk::{Button, Image, Label, Scale, ToggleButton};
use serde::Deserialize; use serde::Deserialize;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tracing::trace; use tracing::trace;
@ -15,21 +15,48 @@ use tracing::trace;
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct VolumeModule { pub struct VolumeModule {
/// Maximum value to allow volume sliders to reach.
/// Pulse supports values > 100 but this may result in distortion.
///
/// **Default**: `100`
#[serde(default = "default_max_volume")]
max_volume: f64,
#[serde(default = "default_icon_size")] #[serde(default = "default_icon_size")]
icon_size: i32, icon_size: i32,
// -- Common -- // -- Common --
/// See [truncate options](module-level-options#truncate-mode).
///
/// **Default**: `null`
pub(crate) truncate: Option<TruncateMode>,
/// See [layout options](module-level-options#layout)
#[serde(default, flatten)]
layout: LayoutConfig,
/// See [common options](module-level-options#common-options). /// See [common options](module-level-options#common-options).
#[serde(flatten)] #[serde(flatten)]
pub common: Option<CommonConfig>, pub common: Option<CommonConfig>,
} }
const fn default_max_volume() -> f64 {
100.0
}
const fn default_icon_size() -> i32 { const fn default_icon_size() -> i32 {
24 24
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Update {} pub enum Update {
SinkChange(String),
SinkVolume(String, f64),
SinkMute(String, bool),
InputVolume(u32, f64),
InputMute(u32, bool),
}
impl Module<Button> for VolumeModule { impl Module<Button> for VolumeModule {
type SendMessage = Event; type SendMessage = Event;
@ -41,7 +68,7 @@ impl Module<Button> for VolumeModule {
&self, &self,
_info: &ModuleInfo, _info: &ModuleInfo,
context: &WidgetContext<Self::SendMessage, Self::ReceiveMessage>, context: &WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
_rx: mpsc::Receiver<Self::ReceiveMessage>, mut rx: mpsc::Receiver<Self::ReceiveMessage>,
) -> color_eyre::Result<()> ) -> color_eyre::Result<()>
where where
<Self as Module<Button>>::SendMessage: Clone, <Self as Module<Button>>::SendMessage: Clone,
@ -75,8 +102,8 @@ impl Module<Button> for VolumeModule {
tx.send_update(Event::AddSink(sink)).await; tx.send_update(Event::AddSink(sink)).await;
} }
for _input in inputs { for input in inputs {
tx.send_update(Event::AddInput).await; tx.send_update(Event::AddInput(input)).await;
} }
// recv loop // recv loop
@ -87,6 +114,19 @@ impl Module<Button> for VolumeModule {
}); });
} }
// ui events
spawn(async move {
while let Some(update) = rx.recv().await {
match update {
Update::SinkChange(name) => client.set_default_sink(&name),
Update::SinkVolume(name, volume) => client.set_sink_volume(&name, volume),
Update::SinkMute(name, muted) => client.set_sink_muted(&name, muted),
Update::InputVolume(index, volume) => client.set_input_volume(index, volume),
Update::InputMute(index, muted) => client.set_input_muted(index, muted),
}
}
});
Ok(()) Ok(())
} }
@ -139,6 +179,13 @@ impl Module<Button> for VolumeModule {
} }
} }
struct InputUi {
container: gtk::Box,
label: Label,
slider: Scale,
btn_mute: ToggleButton,
}
fn determine_volume_icon(muted: bool, volume: f64) -> String { fn determine_volume_icon(muted: bool, volume: f64) -> String {
let icon_variant = if muted { let icon_variant = if muted {
"muted" "muted"